Rework HealthEndpoint and HealthIndicator

With this commit the state of a component or subsystem becomes a first-class citizen in Boot's application health support. HealthIndicators now return a Health instance with status and some contextual details.

An aggregation strategy has been introduced to aggregate several Health instances into one final application Health instance. Out of the box OrderedHealthAggregator can be configured to allow different ordering or a custom HealthAggregator bean can be registered.
pull/931/merge
Christian Dupuis 11 years ago
parent d59cbc830a
commit 4648188782

@ -37,6 +37,8 @@ import org.springframework.boot.actuate.endpoint.RequestMappingEndpoint;
import org.springframework.boot.actuate.endpoint.ShutdownEndpoint;
import org.springframework.boot.actuate.endpoint.TraceEndpoint;
import org.springframework.boot.actuate.endpoint.VanillaPublicMetrics;
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
import org.springframework.boot.actuate.health.HealthAggregator;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.metrics.reader.MetricReader;
import org.springframework.boot.actuate.metrics.repository.InMemoryMetricRepository;
@ -74,7 +76,10 @@ public class EndpointAutoConfiguration {
private InfoPropertiesConfiguration properties;
@Autowired(required = false)
Map<String, HealthIndicator<? extends Object>> healthIndicators = new HashMap<String, HealthIndicator<? extends Object>>();
private HealthAggregator healthAggregator = new OrderedHealthAggregator();
@Autowired(required = false)
Map<String, HealthIndicator> healthIndicators = new HashMap<String, HealthIndicator>();
@Autowired(required = false)
private MetricReader metricRepository = new InMemoryMetricRepository();
@ -97,7 +102,7 @@ public class EndpointAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public HealthEndpoint healthEndpoint() {
return new HealthEndpoint(this.healthIndicators);
return new HealthEndpoint(this.healthAggregator, this.healthIndicators);
}
@Bean

@ -16,15 +16,19 @@
package org.springframework.boot.actuate.autoconfigure;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.health.CompositeHealthIndicator;
import org.springframework.boot.actuate.health.HealthAggregator;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.MongoHealthIndicator;
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
import org.springframework.boot.actuate.health.RabbitHealthIndicator;
import org.springframework.boot.actuate.health.RedisHealthIndicator;
import org.springframework.boot.actuate.health.SimpleDataSourceHealthIndicator;
@ -58,9 +62,22 @@ import org.springframework.data.redis.connection.RedisConnectionFactory;
RabbitAutoConfiguration.class })
public class HealthIndicatorAutoConfiguration {
@Value("${health.status.order:}")
private List<String> statusOrder = null;
@Bean
@ConditionalOnMissingBean
public HealthAggregator healthAggregator() {
OrderedHealthAggregator healthAggregator = new OrderedHealthAggregator();
if (this.statusOrder != null) {
healthAggregator.setStatusOrder(this.statusOrder);
}
return healthAggregator;
}
@Bean
@ConditionalOnMissingBean(HealthIndicator.class)
public HealthIndicator<?> statusHealthIndicator() {
public HealthIndicator statusHealthIndicator() {
return new VanillaHealthIndicator();
}
@ -69,18 +86,22 @@ public class HealthIndicatorAutoConfiguration {
@ConditionalOnExpression("${health.db.enabled:true}")
public static class DataSourcesHealthIndicatorConfiguration {
@Autowired
private HealthAggregator healthAggregator;
@Autowired(required = false)
private Map<String, DataSource> dataSources;
@Bean
@ConditionalOnMissingBean(name = "dbHealthIndicator")
public HealthIndicator<? extends Object> dbHealthIndicator() {
public HealthIndicator dbHealthIndicator() {
if (this.dataSources.size() == 1) {
return new SimpleDataSourceHealthIndicator(this.dataSources.values()
.iterator().next());
}
CompositeHealthIndicator composite = new CompositeHealthIndicator();
CompositeHealthIndicator composite = new CompositeHealthIndicator(
this.healthAggregator);
for (Map.Entry<String, DataSource> entry : this.dataSources.entrySet()) {
composite.addHealthIndicator(entry.getKey(),
new SimpleDataSourceHealthIndicator(entry.getValue()));
@ -94,18 +115,22 @@ public class HealthIndicatorAutoConfiguration {
@ConditionalOnExpression("${health.mongo.enabled:true}")
public static class MongoHealthIndicatorConfiguration {
@Autowired
private HealthAggregator healthAggregator;
@Autowired
private Map<String, MongoTemplate> mongoTemplates;
@Bean
@ConditionalOnMissingBean(name = "mongoHealthIndicator")
public HealthIndicator<?> mongoHealthIndicator() {
public HealthIndicator mongoHealthIndicator() {
if (this.mongoTemplates.size() == 1) {
return new MongoHealthIndicator(this.mongoTemplates.values().iterator()
.next());
}
CompositeHealthIndicator composite = new CompositeHealthIndicator();
CompositeHealthIndicator composite = new CompositeHealthIndicator(
this.healthAggregator);
for (Map.Entry<String, MongoTemplate> entry : this.mongoTemplates.entrySet()) {
composite.addHealthIndicator(entry.getKey(), new MongoHealthIndicator(
entry.getValue()));
@ -119,18 +144,22 @@ public class HealthIndicatorAutoConfiguration {
@ConditionalOnExpression("${health.redis.enabled:true}")
public static class RedisHealthIndicatorConfiguration {
@Autowired
private HealthAggregator healthAggregator;
@Autowired
private Map<String, RedisConnectionFactory> redisConnectionFactories;
@Bean
@ConditionalOnMissingBean(name = "redisHealthIndicator")
public HealthIndicator<?> redisHealthIndicator() {
public HealthIndicator redisHealthIndicator() {
if (this.redisConnectionFactories.size() == 1) {
return new RedisHealthIndicator(this.redisConnectionFactories.values()
.iterator().next());
}
CompositeHealthIndicator composite = new CompositeHealthIndicator();
CompositeHealthIndicator composite = new CompositeHealthIndicator(
this.healthAggregator);
for (Map.Entry<String, RedisConnectionFactory> entry : this.redisConnectionFactories
.entrySet()) {
composite.addHealthIndicator(entry.getKey(), new RedisHealthIndicator(
@ -145,18 +174,22 @@ public class HealthIndicatorAutoConfiguration {
@ConditionalOnExpression("${health.rabbit.enabled:true}")
public static class RabbitHealthIndicatorConfiguration {
@Autowired
private HealthAggregator healthAggregator;
@Autowired
private Map<String, RabbitTemplate> rabbitTemplates;
@Bean
@ConditionalOnMissingBean(name = "rabbitHealthIndicator")
public HealthIndicator<?> rabbitHealthIndicator() {
public HealthIndicator rabbitHealthIndicator() {
if (this.rabbitTemplates.size() == 1) {
return new RabbitHealthIndicator(this.rabbitTemplates.values().iterator()
.next());
}
CompositeHealthIndicator composite = new CompositeHealthIndicator();
CompositeHealthIndicator composite = new CompositeHealthIndicator(
this.healthAggregator);
for (Map.Entry<String, RabbitTemplate> entry : this.rabbitTemplates
.entrySet()) {
composite.addHealthIndicator(entry.getKey(), new RabbitHealthIndicator(

@ -16,10 +16,11 @@
package org.springframework.boot.actuate.endpoint;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.springframework.boot.actuate.health.CompositeHealthIndicator;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthAggregator;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.util.Assert;
@ -31,29 +32,39 @@ import org.springframework.util.Assert;
* @author Christian Dupuis
*/
@ConfigurationProperties(prefix = "endpoints.health", ignoreUnknownFields = false)
public class HealthEndpoint extends AbstractEndpoint<Map<String, Object>> {
public class HealthEndpoint extends AbstractEndpoint<Health> {
private final Map<String, HealthIndicator<? extends Object>> healthIndicators;
private final HealthIndicator healthIndicator;
/**
* Create a new {@link HealthIndicator} instance.
*/
public HealthEndpoint(Map<String, HealthIndicator<? extends Object>> healthIndicators) {
public HealthEndpoint(HealthAggregator healthAggregator,
Map<String, HealthIndicator> healthIndicators) {
super("health", false, true);
Assert.notNull(healthIndicators, "HealthIndicator must not be null");
this.healthIndicators = healthIndicators;
Assert.notNull(healthAggregator, "HealthAggregator must not be null");
Assert.notNull(healthIndicators, "HealthIndicators must not be null");
if (healthIndicators.size() == 1) {
this.healthIndicator = healthIndicators.values().iterator().next();
}
else {
CompositeHealthIndicator healthIndicator = new CompositeHealthIndicator(
healthAggregator);
for (Map.Entry<String, HealthIndicator> h : healthIndicators.entrySet()) {
healthIndicator.addHealthIndicator(getKey(h.getKey()), h.getValue());
}
this.healthIndicator = healthIndicator;
}
}
/**
* Invoke all {@link HealthIndicator} delegates and collect their health information.
*/
@Override
public Map<String, Object> invoke() {
Map<String, Object> health = new LinkedHashMap<String, Object>();
for (Entry<String, HealthIndicator<?>> entry : this.healthIndicators.entrySet()) {
health.put(getKey(entry.getKey()), entry.getValue().health());
}
return health;
public Health invoke() {
return this.healthIndicator.health();
}
/**

@ -19,22 +19,27 @@ package org.springframework.boot.actuate.health;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.util.Assert;
/**
* {@link HealthIndicator} that returns health indications from all registered delegates.
*
* @author Tyler J. Frederick
* @author Phillip Webb
* @author Christian Dupuis
* @since 1.1.0
*/
public class CompositeHealthIndicator implements HealthIndicator<Map<String, Object>> {
public class CompositeHealthIndicator implements HealthIndicator {
private final Map<String, HealthIndicator> indicators;
private final Map<String, HealthIndicator<?>> indicators;
private final HealthAggregator healthAggregator;
/**
* Create a new {@link CompositeHealthIndicator}.
*/
public CompositeHealthIndicator() {
this.indicators = new LinkedHashMap<String, HealthIndicator<?>>();
public CompositeHealthIndicator(HealthAggregator healthAggregator) {
this(healthAggregator, new LinkedHashMap<String, HealthIndicator>());
}
/**
@ -42,21 +47,25 @@ public class CompositeHealthIndicator implements HealthIndicator<Map<String, Obj
* @param indicators a map of {@link HealthIndicator}s with the key being used as an
* indicator name.
*/
public CompositeHealthIndicator(Map<String, HealthIndicator<?>> indicators) {
this.indicators = new LinkedHashMap<String, HealthIndicator<?>>(indicators);
public CompositeHealthIndicator(HealthAggregator healthAggregator,
Map<String, HealthIndicator> indicators) {
Assert.notNull(healthAggregator, "HealthAggregator must not be null");
Assert.notNull(healthAggregator, "Indicators must not be null");
this.indicators = new LinkedHashMap<String, HealthIndicator>(indicators);
this.healthAggregator = healthAggregator;
}
public void addHealthIndicator(String name, HealthIndicator<?> indicator) {
public void addHealthIndicator(String name, HealthIndicator indicator) {
this.indicators.put(name, indicator);
}
@Override
public Map<String, Object> health() {
Map<String, Object> health = new LinkedHashMap<String, Object>();
for (Map.Entry<String, HealthIndicator<?>> entry : this.indicators.entrySet()) {
health.put(entry.getKey(), entry.getValue().health());
public Health health() {
Map<String, Health> healths = new LinkedHashMap<String, Health>();
for (Map.Entry<String, HealthIndicator> entry : this.indicators.entrySet()) {
healths.put(entry.getKey(), entry.getValue().health());
}
return health;
return this.healthAggregator.aggregate(healths);
}
}

@ -0,0 +1,133 @@
/*
* Copyright 2012-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.health;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
/**
* Value object used to carry information about the health information of a component or
* subsystem.
*
* <p>
* {@link Health} contains a {@link Status} to express the state of a component or
* subsystem and some additional details to carry some contextual information.
*
* <p>
* {@link Health} has a fluent API to make it easy to construct instances. Typical usage
* in a {@link HealthIndicator} would be:
*
* <code>
* Health health = new Health();
* try {
* // do some test to determine state of component
*
* health.up().withDetail("version", "1.1.2");
* }
* catch (Exception ex) {
* health.down().withException(ex);
* }
* return health;
* </code>
*
* @author Christian Dupuis
* @since 1.1.0
*/
@JsonInclude(Include.NON_EMPTY)
public class Health {
private Status status;
private Map<String, Object> details;
public Health() {
this(Status.UNKOWN);
}
public Health(Status status) {
this.status = status;
this.details = new LinkedHashMap<String, Object>();
}
public Health status(Status status) {
Assert.notNull(status, "Status must not be null");
this.status = status;
return this;
}
public Health up() {
return status(Status.UP);
}
public Health down() {
return status(Status.DOWN);
}
public Health withException(Exception ex) {
Assert.notNull(ex, "Exception must not be null");
return withDetail("error", ex.getClass().getName() + ": " + ex.getMessage());
}
@JsonAnySetter
public Health withDetail(String key, Object data) {
Assert.notNull(key, "Key must not be null");
Assert.notNull(data, "Data must not be null");
this.details.put(key, data);
return this;
}
@JsonUnwrapped
public Status getStatus() {
return this.status;
}
@JsonAnyGetter
public Map<String, Object> getDetails() {
return this.details;
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj != null && obj instanceof Health) {
return ObjectUtils.nullSafeEquals(this.status, ((Health) obj).status)
&& ObjectUtils.nullSafeEquals(this.details, ((Health) obj).details);
}
return false;
}
@Override
public int hashCode() {
int hashCode = 0;
if (this.status != null) {
hashCode = this.status.hashCode();
}
return 13 * hashCode + this.details.hashCode();
}
}

@ -0,0 +1,45 @@
/*
* Copyright 2012-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.health;
import java.util.Map;
/**
* Strategy interface used by {@link CompositeHealthIndicator} to aggregate {@link Health}
* instances into a final one.
*
* <p>
* This is especially useful to combine subsystem states expressed through
* {@link Health#getStatus()} into one state for the entire system. The default
* implementation {@link OrderedHealthAggregator} sorts {@link Status} instances based on
* a priority list.
*
* <p>
* It is possible to add more complex {@link Status} types to the system. In that case
* either the {@link OrderedHealthAggregator} needs to be properly configured or users
* need to register a custom {@link HealthAggregator} as bean.
*
* @author Christian Dupuis
* @since 1.1.0
*/
public interface HealthAggregator {
/**
* Aggregate several given {@link Health} instances into one.
*/
Health aggregate(Map<String, Health> healths);
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2013 the original author or authors.
* Copyright 2012-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -22,11 +22,11 @@ package org.springframework.boot.actuate.health;
* @author Dave Syer
* @see VanillaHealthIndicator
*/
public interface HealthIndicator<T> {
public interface HealthIndicator {
/**
* @return an indication of health
*/
T health();
Health health();
}

@ -16,9 +16,6 @@
package org.springframework.boot.actuate.health;
import java.util.HashMap;
import java.util.Map;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.util.Assert;
@ -31,7 +28,7 @@ import com.mongodb.CommandResult;
* @author Christian Dupuis
* @since 1.1.0
*/
public class MongoHealthIndicator implements HealthIndicator<Map<String, Object>> {
public class MongoHealthIndicator implements HealthIndicator {
private final MongoTemplate mongoTemplate;
@ -41,17 +38,15 @@ public class MongoHealthIndicator implements HealthIndicator<Map<String, Object>
}
@Override
public Map<String, Object> health() {
Map<String, Object> health = new HashMap<String, Object>();
public Health health() {
Health health = new Health();
try {
CommandResult result = this.mongoTemplate
.executeCommand("{ serverStatus: 1 }");
health.put("status", "ok");
health.put("version", result.getString("version"));
health.up().withDetail("version", result.getString("version"));
}
catch (Exception ex) {
health.put("status", "error");
health.put("error", ex.getClass().getName() + ": " + ex.getMessage());
health.down().withException(ex);
}
return health;
}

@ -0,0 +1,78 @@
/*
* Copyright 2012-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.health;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
/**
* Default {@link HealthAggregator} implementation that aggregates {@link Health}
* instances and determines the final system state based on a simple ordered list.
*
* <p>
* If a different order is required or a new {@link Status} type will be used, the order
* can be set by calling {@link #setStatusOrder(List)}.
*
* @author Christian Dupuis
* @since 1.1.0
*/
public class OrderedHealthAggregator implements HealthAggregator {
private List<String> statusOrder = Arrays.asList("DOWN", "OUT_OF_SERVICE", "UP",
"UNKOWN");
@Override
public Health aggregate(Map<String, Health> healths) {
Health health = new Health();
List<Status> status = new ArrayList<Status>();
for (Map.Entry<String, Health> h : healths.entrySet()) {
health.withDetail(h.getKey(), h.getValue());
status.add(h.getValue().getStatus());
}
health.status(aggregateStatus(status));
return health;
}
public void setStatusOrder(List<String> statusOrder) {
this.statusOrder = statusOrder;
}
protected Status aggregateStatus(List<Status> status) {
if (status.size() == 0) {
return Status.UNKOWN;
}
status.sort(new Comparator<Status>() {
@Override
public int compare(Status s1, Status s2) {
return Integer.valueOf(
OrderedHealthAggregator.this.statusOrder.indexOf(s1.getStatus())).compareTo(
Integer.valueOf(OrderedHealthAggregator.this.statusOrder.indexOf(s2.getStatus())));
}
});
return status.get(0);
}
}

@ -16,7 +16,6 @@
package org.springframework.boot.actuate.health;
import java.util.HashMap;
import java.util.Map;
import org.springframework.amqp.rabbit.core.ChannelCallback;
@ -32,7 +31,7 @@ import com.rabbitmq.client.Channel;
* @author Christian Dupuis
* @since 1.1.0
*/
public class RabbitHealthIndicator implements HealthIndicator<Map<String, Object>> {
public class RabbitHealthIndicator implements HealthIndicator {
private final RabbitTemplate rabbitTemplate;
@ -42,10 +41,10 @@ public class RabbitHealthIndicator implements HealthIndicator<Map<String, Object
}
@Override
public Map<String, Object> health() {
Map<String, Object> health = new HashMap<String, Object>();
public Health health() {
Health health = new Health();
try {
health.put("version",
health.up().withDetail("version",
this.rabbitTemplate.execute(new ChannelCallback<String>() {
@Override
@ -55,12 +54,9 @@ public class RabbitHealthIndicator implements HealthIndicator<Map<String, Object
return serverProperties.get("version").toString();
}
}));
health.put("status", "ok");
}
catch (Exception ex) {
health.put("status", "error");
health.put("error", ex.getClass().getName() + ": " + ex.getMessage());
health.down().withException(ex);
}
return health;
}

@ -16,8 +16,6 @@
package org.springframework.boot.actuate.health;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import org.springframework.data.redis.connection.RedisConnection;
@ -32,7 +30,7 @@ import org.springframework.util.Assert;
* @author Christian Dupuis
* @since 1.1.0
*/
public class RedisHealthIndicator implements HealthIndicator<Map<String, Object>> {
public class RedisHealthIndicator implements HealthIndicator {
private final RedisConnectionFactory redisConnectionFactory;
@ -42,19 +40,17 @@ public class RedisHealthIndicator implements HealthIndicator<Map<String, Object>
}
@Override
public Map<String, Object> health() {
Map<String, Object> health = new HashMap<String, Object>();
public Health health() {
Health health = new Health();
RedisConnection connection = null;
try {
connection = RedisConnectionUtils.getConnection(this.redisConnectionFactory);
Properties info = connection.info();
health.put("status", "ok");
health.put("version", info.getProperty("redis_version"));
health.up().withDetail("version", info.getProperty("redis_version"));
}
catch (Exception ex) {
health.put("status", "error");
health.put("error", ex.getClass().getName() + ": " + ex.getMessage());
health.down().withException(ex);
}
finally {
RedisConnectionUtils.releaseConnection(connection,

@ -19,7 +19,6 @@ package org.springframework.boot.actuate.health;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.sql.DataSource;
@ -34,6 +33,7 @@ import org.springframework.util.StringUtils;
* attempts a simple database test.
*
* @author Dave Syer
* @author Christian Dupuis
*/
public class SimpleDataSourceHealthIndicator implements
HealthIndicator<Map<String, Object>> {
@ -72,9 +72,10 @@ public class SimpleDataSourceHealthIndicator implements
}
@Override
public Map<String, Object> health() {
LinkedHashMap<String, Object> health = new LinkedHashMap<String, Object>();
health.put("status", "ok");
public Health health() {
Health health = new Health();
health.up();
String product = "unknown";
if (this.dataSource != null) {
try {
@ -85,21 +86,19 @@ public class SimpleDataSourceHealthIndicator implements
return connection.getMetaData().getDatabaseProductName();
}
});
health.put("database", product);
health.withDetail("database", product);
}
catch (DataAccessException ex) {
health.put("status", "error");
health.put("error", ex.getClass().getName() + ": " + ex.getMessage());
health.down().withException(ex);
}
String query = detectQuery(product);
if (StringUtils.hasText(query)) {
try {
health.put("hello",
health.withDetail("hello",
this.jdbcTemplate.queryForObject(query, Object.class));
}
catch (Exception ex) {
health.put("status", "error");
health.put("error", ex.getClass().getName() + ": " + ex.getMessage());
health.down().withException(ex);
}
}
}

@ -0,0 +1,100 @@
/*
* Copyright 2012-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.health;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
/**
* Value object to express state of a component or subsystem.
*
* <p>
* Status provides convenient constants for commonly used states like {@link #UP},
* {@link #DOWN} or {@link #OUT_OF_SERVICE}.
*
* <p>
* Custom states can also be created and used throughout the Spring Boot Health subsystem.
*
* @author Christian Dupuis
* @since 1.1.0
*/
@JsonInclude(Include.NON_EMPTY)
public class Status {
/**
* Convenient constant value representing unknown state
*/
public static final Status UNKOWN = new Status("UNKOWN");
/**
* Convenient constant value representing up state
*/
public static final Status UP = new Status("UP");
/**
* Convenient constant value representing down state
*/
public static final Status DOWN = new Status("DOWN");
/**
* Convenient constant value representing out-of-service state
*/
public static final Status OUT_OF_SERVICE = new Status("OUT_OF_SERVICE");
private final String status;
private final String description;
public Status(String code) {
this(code, "");
}
public Status(String code, String description) {
Assert.notNull(code, "Status must not be null");
Assert.notNull(description, "Description must not be null");
this.status = code;
this.description = description;
}
public String getStatus() {
return this.status;
}
@JsonInclude(Include.NON_EMPTY)
public String getDescription() {
return this.description;
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj != null && obj instanceof Status) {
return ObjectUtils.nullSafeEquals(this.status, ((Status) obj).status);
}
return false;
}
@Override
public int hashCode() {
return this.status.hashCode();
}
}

@ -16,16 +16,18 @@
package org.springframework.boot.actuate.health;
/**
* Default implementation of {@link HealthIndicator} that simply returns {@literal "ok"}.
*
* @author Dave Syer
* @author Christian Dupuis
*/
public class VanillaHealthIndicator implements HealthIndicator<String> {
public class VanillaHealthIndicator implements HealthIndicator {
@Override
public String health() {
return "ok";
public Health health() {
return new Health(Status.UP);
}
}

@ -16,8 +16,6 @@
package org.springframework.boot.actuate.autoconfigure;
import java.util.Map;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@ -31,6 +29,7 @@ import org.springframework.boot.actuate.endpoint.MetricsEndpoint;
import org.springframework.boot.actuate.endpoint.RequestMappingEndpoint;
import org.springframework.boot.actuate.endpoint.ShutdownEndpoint;
import org.springframework.boot.actuate.endpoint.TraceEndpoint;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport;
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
import org.springframework.boot.test.EnvironmentTestUtils;
@ -47,6 +46,7 @@ import static org.junit.Assert.assertTrue;
* @author Dave Syer
* @author Phillip Webb
* @author Greg Turnquist
* @author Christian Dupuis
*/
public class EndpointAutoConfigurationTests {
@ -80,7 +80,6 @@ public class EndpointAutoConfigurationTests {
}
@Test
@SuppressWarnings("unchecked")
public void healthEndpoint() {
this.context = new AnnotationConfigApplicationContext();
this.context.register(EmbeddedDataSourceConfiguration.class,
@ -88,12 +87,9 @@ public class EndpointAutoConfigurationTests {
this.context.refresh();
HealthEndpoint bean = this.context.getBean(HealthEndpoint.class);
assertNotNull(bean);
Map<String, Object> result = bean.invoke();
Health result = bean.invoke();
assertNotNull(result);
assertTrue("Wrong result: " + result,
((Map<String, Object>) result.get("db")).containsKey("status"));
assertTrue("Wrong result: " + result,
((Map<String, Object>) result.get("db")).containsKey("database"));
assertTrue("Wrong result: " + result, result.getDetails().containsKey("database"));
}
@Test
@ -104,9 +100,8 @@ public class EndpointAutoConfigurationTests {
this.context.refresh();
HealthEndpoint bean = this.context.getBean(HealthEndpoint.class);
assertNotNull(bean);
Map<String, Object> result = bean.invoke();
Health result = bean.invoke();
assertNotNull(result);
assertTrue("Wrong result: " + result, result.containsKey("status"));
}
@Test

@ -60,7 +60,6 @@ public class HealthIndicatorAutoConfigurationTests {
}
}
@SuppressWarnings("rawtypes")
@Test
public void defaultHealthIndicator() {
this.context = new AnnotationConfigApplicationContext();
@ -73,7 +72,6 @@ public class HealthIndicatorAutoConfigurationTests {
.getClass());
}
@SuppressWarnings("rawtypes")
@Test
public void redisHealthIndicator() {
this.context = new AnnotationConfigApplicationContext();
@ -87,7 +85,6 @@ public class HealthIndicatorAutoConfigurationTests {
.getClass());
}
@SuppressWarnings("rawtypes")
@Test
public void notRedisHealthIndicator() {
this.context = new AnnotationConfigApplicationContext();
@ -102,7 +99,6 @@ public class HealthIndicatorAutoConfigurationTests {
.getClass());
}
@SuppressWarnings("rawtypes")
@Test
public void mongoHealthIndicator() {
this.context = new AnnotationConfigApplicationContext();
@ -116,7 +112,6 @@ public class HealthIndicatorAutoConfigurationTests {
.getClass());
}
@SuppressWarnings("rawtypes")
@Test
public void notMongoHealthIndicator() {
this.context = new AnnotationConfigApplicationContext();
@ -131,7 +126,6 @@ public class HealthIndicatorAutoConfigurationTests {
.getClass());
}
@SuppressWarnings("rawtypes")
@Test
public void combinedHealthIndicator() {
this.context = new AnnotationConfigApplicationContext();
@ -143,7 +137,6 @@ public class HealthIndicatorAutoConfigurationTests {
assertEquals(2, beans.size());
}
@SuppressWarnings("rawtypes")
@Test
public void dataSourceHealthIndicator() {
this.context = new AnnotationConfigApplicationContext();
@ -157,7 +150,6 @@ public class HealthIndicatorAutoConfigurationTests {
.next().getClass());
}
@SuppressWarnings("rawtypes")
@Test
public void notDataSourceHealthIndicator() {
this.context = new AnnotationConfigApplicationContext();
@ -172,7 +164,6 @@ public class HealthIndicatorAutoConfigurationTests {
.getClass());
}
@SuppressWarnings("rawtypes")
@Test
public void rabbitHealthIndicator() {
this.context = new AnnotationConfigApplicationContext();
@ -186,7 +177,6 @@ public class HealthIndicatorAutoConfigurationTests {
.getClass());
}
@SuppressWarnings("rawtypes")
@Test
public void notRabbitHealthIndicator() {
this.context = new AnnotationConfigApplicationContext();

@ -16,11 +16,14 @@
package org.springframework.boot.actuate.endpoint;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthAggregator;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
import org.springframework.boot.actuate.health.Status;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -32,6 +35,7 @@ import static org.junit.Assert.assertThat;
* Tests for {@link HealthEndpoint}.
*
* @author Phillip Webb
* @author Christian Dupuis
*/
public class HealthEndpointTests extends AbstractEndpointTests<HealthEndpoint> {
@ -41,9 +45,8 @@ public class HealthEndpointTests extends AbstractEndpointTests<HealthEndpoint> {
@Test
public void invoke() throws Exception {
Map<String, Object> result = new HashMap<String, Object>();
result.put("status", "fine");
assertThat(getEndpointBean().invoke(), equalTo(result));
Status result = new Status("FINE");
assertThat(getEndpointBean().invoke().getStatus(), equalTo(result));
}
@Configuration
@ -51,18 +54,25 @@ public class HealthEndpointTests extends AbstractEndpointTests<HealthEndpoint> {
public static class Config {
@Bean
public HealthEndpoint endpoint(Map<String, HealthIndicator<?>> healthIndicators) {
return new HealthEndpoint(healthIndicators);
public HealthEndpoint endpoint(HealthAggregator healthAggregator,
Map<String, HealthIndicator> healthIndicators) {
return new HealthEndpoint(healthAggregator, healthIndicators);
}
@Bean
public HealthIndicator<String> statusHealthIndicator() {
return new HealthIndicator<String>() {
public HealthIndicator statusHealthIndicator() {
return new HealthIndicator() {
@Override
public String health() {
return "fine";
public Health health() {
return new Health().status(new Status("FINE"));
}
};
}
@Bean
public HealthAggregator healthAggregator() {
return new OrderedHealthAggregator();
}
}
}

@ -20,10 +20,14 @@ import java.util.HashMap;
import java.util.Map;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasEntry;
import static org.junit.Assert.assertThat;
@ -34,61 +38,96 @@ import static org.mockito.BDDMockito.given;
*
* @author Tyler J. Frederick
* @author Phillip Webb
* @author Christian Dupuis
*/
public class CompositeHealthIndicatorTests {
private HealthAggregator healthAggregator;
@Mock
private HealthIndicator<String> one;
private HealthIndicator one;
@Mock
private HealthIndicator<String> two;
private HealthIndicator two;
@Mock
private HealthIndicator<String> three;
private HealthIndicator three;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
given(this.one.health()).willReturn("1");
given(this.two.health()).willReturn("2");
given(this.three.health()).willReturn("3");
given(this.one.health()).willReturn(new Health().withDetail("1", "1"));
given(this.two.health()).willReturn(new Health().withDetail("2", "2"));
given(this.three.health()).willReturn(new Health().withDetail("3", "3"));
this.healthAggregator = new OrderedHealthAggregator();
}
@Test
public void createWithIndicators() throws Exception {
Map<String, HealthIndicator<?>> indicators = new HashMap<String, HealthIndicator<?>>();
Map<String, HealthIndicator> indicators = new HashMap<String, HealthIndicator>();
indicators.put("one", this.one);
indicators.put("two", this.two);
CompositeHealthIndicator composite = new CompositeHealthIndicator(indicators);
Map<String, Object> result = composite.health();
assertThat(result.size(), equalTo(2));
assertThat(result, hasEntry("one", (Object) "1"));
assertThat(result, hasEntry("two", (Object) "2"));
CompositeHealthIndicator composite = new CompositeHealthIndicator(
this.healthAggregator, indicators);
Health result = composite.health();
assertThat(result.getDetails().size(), equalTo(2));
assertThat(result.getDetails(),
hasEntry("one", (Object) new Health().withDetail("1", "1")));
assertThat(result.getDetails(),
hasEntry("two", (Object) new Health().withDetail("2", "2")));
}
@Test
public void createWithIndicatorsAndAdd() throws Exception {
Map<String, HealthIndicator<?>> indicators = new HashMap<String, HealthIndicator<?>>();
Map<String, HealthIndicator> indicators = new HashMap<String, HealthIndicator>();
indicators.put("one", this.one);
indicators.put("two", this.two);
CompositeHealthIndicator composite = new CompositeHealthIndicator(indicators);
CompositeHealthIndicator composite = new CompositeHealthIndicator(
this.healthAggregator, indicators);
composite.addHealthIndicator("three", this.three);
Map<String, Object> result = composite.health();
assertThat(result.size(), equalTo(3));
assertThat(result, hasEntry("one", (Object) "1"));
assertThat(result, hasEntry("two", (Object) "2"));
assertThat(result, hasEntry("three", (Object) "3"));
Health result = composite.health();
assertThat(result.getDetails().size(), equalTo(3));
assertThat(result.getDetails(),
hasEntry("one", (Object) new Health().withDetail("1", "1")));
assertThat(result.getDetails(),
hasEntry("two", (Object) new Health().withDetail("2", "2")));
assertThat(result.getDetails(),
hasEntry("three", (Object) new Health().withDetail("3", "3")));
}
@Test
public void createWithoutAndAdd() throws Exception {
CompositeHealthIndicator composite = new CompositeHealthIndicator();
CompositeHealthIndicator composite = new CompositeHealthIndicator(
this.healthAggregator);
composite.addHealthIndicator("one", this.one);
composite.addHealthIndicator("two", this.two);
Map<String, Object> result = composite.health();
assertThat(result.size(), equalTo(2));
assertThat(result, hasEntry("one", (Object) "1"));
assertThat(result, hasEntry("two", (Object) "2"));
Health result = composite.health();
assertThat(result.getDetails().size(), equalTo(2));
assertThat(result.getDetails(),
hasEntry("one", (Object) new Health().withDetail("1", "1")));
assertThat(result.getDetails(),
hasEntry("two", (Object) new Health().withDetail("2", "2")));
}
@Test
@Ignore
public void testSerialization() throws Exception {
Map<String, HealthIndicator> indicators = new HashMap<String, HealthIndicator>();
indicators.put("db1", this.one);
indicators.put("db2", this.two);
CompositeHealthIndicator innerComposite = new CompositeHealthIndicator(
this.healthAggregator, indicators);
CompositeHealthIndicator composite = new CompositeHealthIndicator(
this.healthAggregator);
composite.addHealthIndicator("db", innerComposite);
Health result = composite.health();
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
String test = mapper.writeValueAsString(result);
System.out.println(test);
}
}

@ -16,8 +16,6 @@
package org.springframework.boot.actuate.health;
import java.util.Map;
import org.junit.After;
import org.junit.Test;
import org.mockito.Mockito;
@ -73,9 +71,9 @@ public class MongoHealthIndicatorTests {
commandResult);
MongoHealthIndicator healthIndicator = new MongoHealthIndicator(mongoTemplate);
Map<String, Object> health = healthIndicator.health();
assertEquals("ok", health.get("status"));
assertEquals("2.6.4", health.get("version"));
Health health = healthIndicator.health();
assertEquals(Status.UP, health.getStatus());
assertEquals("2.6.4", health.getDetails().get("version"));
Mockito.verify(commandResult).getString("version");
Mockito.verify(mongoTemplate).executeCommand("{ serverStatus: 1 }");
@ -88,9 +86,10 @@ public class MongoHealthIndicatorTests {
new MongoException("Connection failed"));
MongoHealthIndicator healthIndicator = new MongoHealthIndicator(mongoTemplate);
Map<String, Object> health = healthIndicator.health();
assertEquals("error", health.get("status"));
assertTrue(((String) health.get("error")).contains("Connection failed"));
Health health = healthIndicator.health();
assertEquals(Status.DOWN, health.getStatus());
assertTrue(((String) health.getDetails().get("error"))
.contains("Connection failed"));
Mockito.verify(mongoTemplate).executeCommand("{ serverStatus: 1 }");
}

@ -0,0 +1,77 @@
/*
* Copyright 2012-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.health;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
* Tests for {@link OrderedHealthAggregator}.
*
* @author Christian Dupuis
*/
public class OrderedHealthAggregatorTests {
private OrderedHealthAggregator healthAggregator;
@Before
public void setup() {
this.healthAggregator = new OrderedHealthAggregator();
}
@Test
public void testDefaultOrdering() {
Map<String, Health> healths = new HashMap<String, Health>();
healths.put("h1", new Health(Status.DOWN));
healths.put("h2", new Health(Status.UP));
healths.put("h3", new Health(Status.UNKOWN));
healths.put("h4", new Health(Status.OUT_OF_SERVICE));
assertEquals(Status.DOWN, this.healthAggregator.aggregate(healths).getStatus());
}
@Test
public void testDefaultOrderingWithCustomStatus() {
Map<String, Health> healths = new HashMap<String, Health>();
healths.put("h1", new Health(Status.DOWN));
healths.put("h2", new Health(Status.UP));
healths.put("h3", new Health(Status.UNKOWN));
healths.put("h4", new Health(Status.OUT_OF_SERVICE));
healths.put("h5", new Health(new Status("CUSTOM")));
assertEquals(new Status("CUSTOM"),
this.healthAggregator.aggregate(healths).getStatus());
}
@Test
public void testDefaultOrderingWithCustomStatusAndOrder() {
this.healthAggregator.setStatusOrder(Arrays.asList("DOWN", "OUT_OF_SERVICE",
"UP", "UNKOWN", "CUSTOM"));
Map<String, Health> healths = new HashMap<String, Health>();
healths.put("h1", new Health(Status.DOWN));
healths.put("h2", new Health(Status.UP));
healths.put("h3", new Health(Status.UNKOWN));
healths.put("h4", new Health(Status.OUT_OF_SERVICE));
healths.put("h5", new Health(new Status("CUSTOM")));
assertEquals(Status.DOWN, this.healthAggregator.aggregate(healths).getStatus());
}
}

@ -16,7 +16,6 @@
package org.springframework.boot.actuate.health;
import java.util.Map;
import java.util.Properties;
import org.junit.After;
@ -76,9 +75,9 @@ public class RedisHealthIndicatorTests {
RedisHealthIndicator healthIndicator = new RedisHealthIndicator(
redisConnectionFactory);
Map<String, Object> health = healthIndicator.health();
assertEquals("ok", health.get("status"));
assertEquals("2.8.9", health.get("version"));
Health health = healthIndicator.health();
assertEquals(Status.UP, health.getStatus());
assertEquals("2.8.9", health.getDetails().get("version"));
Mockito.verify(redisConnectionFactory).getConnection();
Mockito.verify(redisConnection).info();
@ -95,9 +94,10 @@ public class RedisHealthIndicatorTests {
RedisHealthIndicator healthIndicator = new RedisHealthIndicator(
redisConnectionFactory);
Map<String, Object> health = healthIndicator.health();
assertEquals("error", health.get("status"));
assertTrue(((String) health.get("error")).contains("Connection failed"));
Health health = healthIndicator.health();
assertEquals(Status.DOWN, health.getStatus());
assertTrue(((String) health.getDetails().get("error"))
.contains("Connection failed"));
Mockito.verify(redisConnectionFactory).getConnection();
Mockito.verify(redisConnection).info();

@ -17,7 +17,6 @@
package org.springframework.boot.actuate.health;
import java.sql.Connection;
import java.util.Map;
import javax.sql.DataSource;
@ -55,9 +54,9 @@ public class SimpleDataSourceHealthIndicatorTests {
@Test
public void database() {
this.indicator.setDataSource(this.dataSource);
Map<String, Object> health = this.indicator.health();
assertNotNull(health.get("database"));
assertNotNull(health.get("hello"));
Health health = this.indicator.health();
assertNotNull(health.getDetails().get("database"));
assertNotNull(health.getDetails().get("hello"));
}
@Test
@ -66,20 +65,20 @@ public class SimpleDataSourceHealthIndicatorTests {
new JdbcTemplate(this.dataSource)
.execute("CREATE TABLE FOO (id INTEGER IDENTITY PRIMARY KEY)");
this.indicator.setQuery("SELECT COUNT(*) from FOO");
Map<String, Object> health = this.indicator.health();
Health health = this.indicator.health();
System.err.println(health);
assertNotNull(health.get("database"));
assertEquals("ok", health.get("status"));
assertNotNull(health.get("hello"));
assertNotNull(health.getDetails().get("database"));
assertEquals(Status.UP, health.getStatus());
assertNotNull(health.getDetails().get("hello"));
}
@Test
public void error() {
this.indicator.setDataSource(this.dataSource);
this.indicator.setQuery("SELECT COUNT(*) from BAR");
Map<String, Object> health = this.indicator.health();
assertNotNull(health.get("database"));
assertEquals("error", health.get("status"));
Health health = this.indicator.health();
assertNotNull(health.getDetails().get("database"));
assertEquals(Status.DOWN, health.getStatus());
}
@Test
@ -90,8 +89,8 @@ public class SimpleDataSourceHealthIndicatorTests {
this.dataSource.getConnection().getMetaData());
when(dataSource.getConnection()).thenReturn(connection);
this.indicator.setDataSource(dataSource);
Map<String, Object> health = this.indicator.health();
assertNotNull(health.get("database"));
Health health = this.indicator.health();
assertNotNull(health.getDetails().get("database"));
verify(connection, times(2)).close();
}

@ -18,8 +18,7 @@ package org.springframework.boot.actuate.health;
import org.junit.Test;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertEquals;
/**
* Tests for {@link VanillaHealthIndicator}.
@ -31,7 +30,7 @@ public class VanillaHealthIndicatorTests {
@Test
public void ok() throws Exception {
VanillaHealthIndicator healthIndicator = new VanillaHealthIndicator();
assertThat(healthIndicator.health(), equalTo("ok"));
assertEquals(Status.UP, healthIndicator.health().getStatus());
}
}

@ -75,7 +75,7 @@ public class SampleActuatorUiApplicationPortTests {
ResponseEntity<String> entity = new TestRestTemplate().getForEntity(
"http://localhost:" + this.managementPort + "/health", String.class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
assertEquals("{\"status\":\"ok\"}", entity.getBody());
assertEquals("{\"status\":\"UP\"}", entity.getBody());
}
}

@ -68,6 +68,6 @@ public class EndpointsPropertiesSampleActuatorApplicationTests {
"http://localhost:" + this.port + "/admin/health", String.class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
assertTrue("Wrong body: " + entity.getBody(),
entity.getBody().contains("\"status\":\"ok\""));
entity.getBody().contains("\"status\":\"UP\""));
}
}

@ -73,7 +73,7 @@ public class ManagementAddressActuatorApplicationTests {
String.class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
assertTrue("Wrong body: " + entity.getBody(),
entity.getBody().contains("\"status\":\"ok\""));
entity.getBody().contains("\"status\":\"UP\""));
}
}

@ -82,7 +82,7 @@ public class ManagementPortSampleActuatorApplicationTests {
"http://localhost:" + this.managementPort + "/health", String.class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
assertTrue("Wrong body: " + entity.getBody(),
entity.getBody().contains("\"status\":\"ok\""));
entity.getBody().contains("\"status\":\"UP\""));
}
@Test

@ -131,7 +131,7 @@ public class SampleActuatorApplicationTests {
"http://localhost:" + this.port + "/health", String.class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
assertTrue("Wrong body: " + entity.getBody(),
entity.getBody().contains("\"status\":\"ok\""));
entity.getBody().contains("\"status\":\"UP\""));
}
@Test

Loading…
Cancel
Save