Introduce HealthIndicatorRegistry
This commit introduces HealthIndicatorRegistry which handles registration of HealthIndicator instances. Registering new HealthIndicator instances is now possible in runtime. See gh-4965
This commit is contained in:
committed by
Stephane Nicoll
parent
ffdcdc0dc6
commit
d829d522be
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
* Copyright 2012-2018 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.
|
||||
@@ -16,10 +16,13 @@
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.health;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint;
|
||||
import org.springframework.boot.actuate.health.HealthAggregator;
|
||||
import org.springframework.boot.actuate.health.HealthEndpoint;
|
||||
import org.springframework.boot.actuate.health.HealthIndicatorRegistry;
|
||||
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@@ -27,15 +30,27 @@ import org.springframework.context.annotation.Configuration;
|
||||
* Configuration for {@link HealthEndpoint}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Vedran Pavic
|
||||
*/
|
||||
@Configuration
|
||||
class HealthEndpointConfiguration {
|
||||
|
||||
private final HealthAggregator healthAggregator;
|
||||
|
||||
private final HealthIndicatorRegistry healthIndicatorRegistry;
|
||||
|
||||
HealthEndpointConfiguration(ObjectProvider<HealthAggregator> healthAggregator,
|
||||
ObjectProvider<HealthIndicatorRegistry> healthIndicatorRegistry) {
|
||||
this.healthAggregator = healthAggregator
|
||||
.getIfAvailable(OrderedHealthAggregator::new);
|
||||
this.healthIndicatorRegistry = healthIndicatorRegistry.getObject();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnEnabledEndpoint
|
||||
public HealthEndpoint healthEndpoint(ApplicationContext applicationContext) {
|
||||
return new HealthEndpoint(HealthIndicatorBeansComposite.get(applicationContext));
|
||||
public HealthEndpoint healthEndpoint() {
|
||||
return new HealthEndpoint(this.healthAggregator, this.healthIndicatorRegistry);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@@ -107,11 +106,9 @@ class HealthEndpointWebExtensionConfiguration {
|
||||
@ConditionalOnEnabledEndpoint
|
||||
@ConditionalOnBean(HealthEndpoint.class)
|
||||
public HealthEndpointWebExtension healthEndpointWebExtension(
|
||||
ApplicationContext applicationContext,
|
||||
HealthEndpoint healthEndpoint,
|
||||
HealthWebEndpointResponseMapper responseMapper) {
|
||||
return new HealthEndpointWebExtension(
|
||||
HealthIndicatorBeansComposite.get(applicationContext),
|
||||
responseMapper);
|
||||
return new HealthEndpointWebExtension(healthEndpoint, responseMapper);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
* Copyright 2012-2018 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.
|
||||
@@ -16,16 +16,23 @@
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.health;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.actuate.health.ApplicationHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.DefaultHealthIndicatorRegistry;
|
||||
import org.springframework.boot.actuate.health.HealthAggregator;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.boot.actuate.health.HealthIndicatorRegistry;
|
||||
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
|
||||
import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for {@link HealthIndicator}s.
|
||||
@@ -33,6 +40,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
* @author Andy Wilkinson
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
* @author Vedran Pavic
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Configuration
|
||||
@@ -61,4 +69,34 @@ public class HealthIndicatorAutoConfiguration {
|
||||
return healthAggregator;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(HealthIndicatorRegistry.class)
|
||||
public HealthIndicatorRegistry healthIndicatorRegistry(
|
||||
ApplicationContext applicationContext) {
|
||||
HealthIndicatorRegistry registry = new DefaultHealthIndicatorRegistry();
|
||||
Map<String, HealthIndicator> indicators = new LinkedHashMap<>();
|
||||
indicators.putAll(applicationContext.getBeansOfType(HealthIndicator.class));
|
||||
if (ClassUtils.isPresent("reactor.core.publisher.Flux", null)) {
|
||||
new ReactiveHealthIndicators().get(applicationContext)
|
||||
.forEach(indicators::putIfAbsent);
|
||||
}
|
||||
indicators.forEach(registry::register);
|
||||
return registry;
|
||||
}
|
||||
|
||||
private static class ReactiveHealthIndicators {
|
||||
|
||||
public Map<String, HealthIndicator> get(ApplicationContext applicationContext) {
|
||||
Map<String, HealthIndicator> indicators = new LinkedHashMap<>();
|
||||
applicationContext.getBeansOfType(ReactiveHealthIndicator.class)
|
||||
.forEach((name, indicator) -> indicators.put(name, adapt(indicator)));
|
||||
return indicators;
|
||||
}
|
||||
|
||||
private HealthIndicator adapt(ReactiveHealthIndicator indicator) {
|
||||
return () -> indicator.health().block();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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.autoconfigure.health;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.boot.actuate.health.CompositeHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.CompositeHealthIndicatorFactory;
|
||||
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.ReactiveHealthIndicator;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Creates a {@link CompositeHealthIndicator} from beans in the
|
||||
* {@link ApplicationContext}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
final class HealthIndicatorBeansComposite {
|
||||
|
||||
private HealthIndicatorBeansComposite() {
|
||||
}
|
||||
|
||||
public static HealthIndicator get(ApplicationContext applicationContext) {
|
||||
HealthAggregator healthAggregator = getHealthAggregator(applicationContext);
|
||||
Map<String, HealthIndicator> indicators = new LinkedHashMap<>();
|
||||
indicators.putAll(applicationContext.getBeansOfType(HealthIndicator.class));
|
||||
if (ClassUtils.isPresent("reactor.core.publisher.Flux", null)) {
|
||||
new ReactiveHealthIndicators().get(applicationContext)
|
||||
.forEach(indicators::putIfAbsent);
|
||||
}
|
||||
CompositeHealthIndicatorFactory factory = new CompositeHealthIndicatorFactory();
|
||||
return factory.createHealthIndicator(healthAggregator, indicators);
|
||||
}
|
||||
|
||||
private static HealthAggregator getHealthAggregator(
|
||||
ApplicationContext applicationContext) {
|
||||
try {
|
||||
return applicationContext.getBean(HealthAggregator.class);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
return new OrderedHealthAggregator();
|
||||
}
|
||||
}
|
||||
|
||||
private static class ReactiveHealthIndicators {
|
||||
|
||||
public Map<String, HealthIndicator> get(ApplicationContext applicationContext) {
|
||||
Map<String, HealthIndicator> indicators = new LinkedHashMap<>();
|
||||
applicationContext.getBeansOfType(ReactiveHealthIndicator.class)
|
||||
.forEach((name, indicator) -> indicators.put(name, adapt(indicator)));
|
||||
return indicators;
|
||||
}
|
||||
|
||||
private HealthIndicator adapt(ReactiveHealthIndicator indicator) {
|
||||
return () -> indicator.health().block();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -34,8 +34,9 @@ import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.web.PathMapper;
|
||||
import org.springframework.boot.actuate.endpoint.web.WebOperation;
|
||||
import org.springframework.boot.actuate.endpoint.web.annotation.EndpointWebExtension;
|
||||
import org.springframework.boot.actuate.health.HealthAggregator;
|
||||
import org.springframework.boot.actuate.health.HealthEndpoint;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.boot.actuate.health.HealthIndicatorRegistry;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -109,7 +110,8 @@ public class CloudFoundryWebEndpointDiscovererTests {
|
||||
|
||||
@Bean
|
||||
public HealthEndpoint healthEndpoint() {
|
||||
return new HealthEndpoint(mock(HealthIndicator.class));
|
||||
return new HealthEndpoint(mock(HealthAggregator.class),
|
||||
mock(HealthIndicatorRegistry.class));
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -32,6 +32,7 @@ import reactor.ipc.netty.http.HttpResources;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration;
|
||||
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
@@ -87,6 +88,7 @@ public class ReactiveCloudFoundryActuatorAutoConfigurationTests {
|
||||
WebClientCustomizerConfig.class, WebClientAutoConfiguration.class,
|
||||
ManagementContextAutoConfiguration.class,
|
||||
EndpointAutoConfiguration.class, WebEndpointAutoConfiguration.class,
|
||||
HealthIndicatorAutoConfiguration.class,
|
||||
HealthEndpointAutoConfiguration.class,
|
||||
ReactiveCloudFoundryActuatorAutoConfiguration.class));
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.junit.Test;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.servlet.ServletManagementContextAutoConfiguration;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
@@ -270,7 +271,8 @@ public class CloudFoundryActuatorAutoConfigurationTests {
|
||||
"vcap.application.application_id:my-app-id",
|
||||
"vcap.application.cf_api:http://my-cloud-controller.com")
|
||||
.withConfiguration(
|
||||
AutoConfigurations.of(HealthEndpointAutoConfiguration.class))
|
||||
AutoConfigurations.of(HealthIndicatorAutoConfiguration.class,
|
||||
HealthEndpointAutoConfiguration.class))
|
||||
.run((context) -> {
|
||||
Collection<ExposableWebEndpoint> endpoints = context
|
||||
.getBean("cloudFoundryWebEndpointServletHandlerMapping",
|
||||
|
||||
@@ -23,9 +23,10 @@ import javax.sql.DataSource;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.actuate.health.CompositeHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.DefaultHealthIndicatorRegistry;
|
||||
import org.springframework.boot.actuate.health.HealthEndpoint;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.boot.actuate.health.HealthIndicatorRegistry;
|
||||
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
|
||||
import org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator;
|
||||
import org.springframework.boot.actuate.system.DiskSpaceHealthIndicator;
|
||||
@@ -72,8 +73,9 @@ public class HealthEndpointDocumentationTests extends MockMvcEndpointDocumentati
|
||||
|
||||
@Bean
|
||||
public HealthEndpoint endpoint(Map<String, HealthIndicator> healthIndicators) {
|
||||
return new HealthEndpoint(new CompositeHealthIndicator(
|
||||
new OrderedHealthAggregator(), healthIndicators));
|
||||
HealthIndicatorRegistry registry = new DefaultHealthIndicatorRegistry();
|
||||
healthIndicators.forEach(registry::register);
|
||||
return new HealthEndpoint(new OrderedHealthAggregator(), registry);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -46,7 +46,8 @@ public class HealthEndpointAutoConfigurationTests {
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(
|
||||
AutoConfigurations.of(HealthEndpointAutoConfiguration.class));
|
||||
AutoConfigurations.of(HealthIndicatorAutoConfiguration.class,
|
||||
HealthEndpointAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
public void healthEndpointShowDetailsDefault() {
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.jmx.JmxEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.trace.http.HttpTraceAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration;
|
||||
@@ -47,7 +48,7 @@ public class JmxEndpointIntegrationTests {
|
||||
private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(JmxAutoConfiguration.class,
|
||||
EndpointAutoConfiguration.class, JmxEndpointAutoConfiguration.class,
|
||||
HttpTraceAutoConfiguration.class))
|
||||
HttpTraceAutoConfiguration.class, HealthIndicatorAutoConfiguration.class))
|
||||
.withConfiguration(
|
||||
AutoConfigurations.of(EndpointAutoConfigurationClasses.ALL));
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.trace.http.HttpTraceAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.servlet.ServletManagementContextAutoConfiguration;
|
||||
@@ -73,7 +74,8 @@ public class WebMvcEndpointExposureIntegrationTests {
|
||||
ServletManagementContextAutoConfiguration.class,
|
||||
ManagementContextAutoConfiguration.class,
|
||||
ServletManagementContextAutoConfiguration.class,
|
||||
HttpTraceAutoConfiguration.class))
|
||||
HttpTraceAutoConfiguration.class,
|
||||
HealthIndicatorAutoConfiguration.class))
|
||||
.withConfiguration(
|
||||
AutoConfigurations.of(EndpointAutoConfigurationClasses.ALL))
|
||||
.withUserConfiguration(CustomMvcEndpoint.class,
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2012-2018 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.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link HealthIndicatorRegistry}.
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
* @since 2.1.0
|
||||
*/
|
||||
public class DefaultHealthIndicatorRegistry implements HealthIndicatorRegistry {
|
||||
|
||||
private final Map<String, HealthIndicator> healthIndicators = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void register(String name, HealthIndicator healthIndicator) {
|
||||
Assert.notNull(healthIndicator, "HealthIndicator must not be null");
|
||||
synchronized (this.healthIndicators) {
|
||||
if (this.healthIndicators.get(name) != null) {
|
||||
throw new IllegalStateException(
|
||||
"HealthIndicator with name '" + name + "' already registered");
|
||||
}
|
||||
this.healthIndicators.put(name, healthIndicator);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public HealthIndicator unregister(String name) {
|
||||
synchronized (this.healthIndicators) {
|
||||
return this.healthIndicators.remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public HealthIndicator get(String name) {
|
||||
synchronized (this.healthIndicators) {
|
||||
return this.healthIndicators.get(name);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, HealthIndicator> getAll() {
|
||||
synchronized (this.healthIndicators) {
|
||||
return Collections.unmodifiableMap(new HashMap<>(this.healthIndicators));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,25 +26,35 @@ import org.springframework.util.Assert;
|
||||
* @author Dave Syer
|
||||
* @author Christian Dupuis
|
||||
* @author Andy Wilkinson
|
||||
* @author Vedran Pavic
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Endpoint(id = "health")
|
||||
public class HealthEndpoint {
|
||||
|
||||
private final HealthIndicator healthIndicator;
|
||||
private final HealthAggregator healthAggregator;
|
||||
|
||||
private final HealthIndicatorRegistry healthIndicatorRegistry;
|
||||
|
||||
/**
|
||||
* Create a new {@link HealthEndpoint} instance.
|
||||
* @param healthIndicator the health indicator
|
||||
* @param healthAggregator the health aggregator
|
||||
* @param healthIndicatorRegistry the health indicator registry
|
||||
*/
|
||||
public HealthEndpoint(HealthIndicator healthIndicator) {
|
||||
Assert.notNull(healthIndicator, "HealthIndicator must not be null");
|
||||
this.healthIndicator = healthIndicator;
|
||||
public HealthEndpoint(HealthAggregator healthAggregator,
|
||||
HealthIndicatorRegistry healthIndicatorRegistry) {
|
||||
Assert.notNull(healthAggregator, "healthAggregator must not be null");
|
||||
Assert.notNull(healthIndicatorRegistry, "healthIndicatorRegistry must not be null");
|
||||
this.healthAggregator = healthAggregator;
|
||||
this.healthIndicatorRegistry = healthIndicatorRegistry;
|
||||
}
|
||||
|
||||
@ReadOperation
|
||||
public Health health() {
|
||||
return this.healthIndicator.health();
|
||||
CompositeHealthIndicatorFactory factory = new CompositeHealthIndicatorFactory();
|
||||
CompositeHealthIndicator healthIndicator = factory.createHealthIndicator(
|
||||
this.healthAggregator, this.healthIndicatorRegistry.getAll());
|
||||
return healthIndicator.health();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -35,11 +35,11 @@ import org.springframework.boot.actuate.endpoint.web.annotation.EndpointWebExten
|
||||
@EndpointWebExtension(endpoint = HealthEndpoint.class)
|
||||
public class HealthEndpointWebExtension {
|
||||
|
||||
private final HealthIndicator delegate;
|
||||
private final HealthEndpoint delegate;
|
||||
|
||||
private final HealthWebEndpointResponseMapper responseMapper;
|
||||
|
||||
public HealthEndpointWebExtension(HealthIndicator delegate,
|
||||
public HealthEndpointWebExtension(HealthEndpoint delegate,
|
||||
HealthWebEndpointResponseMapper responseMapper) {
|
||||
this.delegate = delegate;
|
||||
this.responseMapper = responseMapper;
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2012-2018 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;
|
||||
|
||||
/**
|
||||
* A registry of {@link HealthIndicator}s.
|
||||
* <p>
|
||||
* Implementations <strong>must</strong> be thread-safe.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Vedran Pavic
|
||||
* @since 2.1.0
|
||||
*/
|
||||
public interface HealthIndicatorRegistry {
|
||||
|
||||
/**
|
||||
* Registers the given {@code healthIndicator}, associating it with the given
|
||||
* {@code name}.
|
||||
* @param name the name of the indicator
|
||||
* @param healthIndicator the indicator
|
||||
* @throws IllegalStateException if an indicator with the given {@code name} is
|
||||
* already registered.
|
||||
*/
|
||||
void register(String name, HealthIndicator healthIndicator);
|
||||
|
||||
/**
|
||||
* Unregisters the {@code HealthIndicator} previously registered with the given
|
||||
* {@code name}.
|
||||
* @param name the name of the indicator
|
||||
* @return the unregistered indicator, or {@code null} if no indicator was found in
|
||||
* the registry for the given {@code name}.
|
||||
*/
|
||||
HealthIndicator unregister(String name);
|
||||
|
||||
/**
|
||||
* Returns the health indicator registered with the given {@code name}.
|
||||
* @param name the name of the indicator
|
||||
* @return the health indicator, or {@code null} if no indicator was registered with
|
||||
* the given {@code name}.
|
||||
*/
|
||||
HealthIndicator get(String name);
|
||||
|
||||
/**
|
||||
* Returns a snapshot of the registered health indicators and their names. The
|
||||
* contents of the map do not reflect subsequent changes to the registry.
|
||||
* @return the snapshot of registered health indicators
|
||||
*/
|
||||
Map<String, HealthIndicator> getAll();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2012-2018 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.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link DefaultHealthIndicatorRegistry}.
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
*/
|
||||
public class DefaultHealthIndicatorRegistryTest {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
private HealthIndicator one = mock(HealthIndicator.class);
|
||||
|
||||
private HealthIndicator two = mock(HealthIndicator.class);
|
||||
|
||||
private DefaultHealthIndicatorRegistry registry;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
given(this.one.health()).willReturn(new Health.Builder().up().build());
|
||||
given(this.two.health()).willReturn(new Health.Builder().unknown().build());
|
||||
|
||||
this.registry = new DefaultHealthIndicatorRegistry();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void register() {
|
||||
this.registry.register("one", this.one);
|
||||
this.registry.register("two", this.two);
|
||||
assertThat(this.registry.getAll()).hasSize(2);
|
||||
assertThat(this.registry.get("one")).isSameAs(this.one);
|
||||
assertThat(this.registry.get("two")).isSameAs(this.two);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void registerAlreadyUsedName() {
|
||||
this.thrown.expect(IllegalStateException.class);
|
||||
this.thrown.expectMessage("HealthIndicator with name 'one' already registered");
|
||||
this.registry.register("one", this.one);
|
||||
this.registry.register("one", this.two);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unregister() {
|
||||
this.registry.register("one", this.one);
|
||||
this.registry.register("two", this.two);
|
||||
assertThat(this.registry.getAll()).hasSize(2);
|
||||
HealthIndicator two = this.registry.unregister("two");
|
||||
assertThat(two).isSameAs(this.two);
|
||||
assertThat(this.registry.getAll()).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unregisterNotKnown() {
|
||||
this.registry.register("one", this.one);
|
||||
assertThat(this.registry.getAll()).hasSize(1);
|
||||
HealthIndicator two = this.registry.unregister("two");
|
||||
assertThat(two).isNull();
|
||||
assertThat(this.registry.getAll()).hasSize(1);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
* Copyright 2012-2018 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.
|
||||
@@ -30,6 +30,7 @@ import static org.assertj.core.api.Assertions.entry;
|
||||
* @author Phillip Webb
|
||||
* @author Christian Dupuis
|
||||
* @author Andy Wilkinson
|
||||
* @author Vedran Pavic
|
||||
*/
|
||||
public class HealthEndpointTests {
|
||||
|
||||
@@ -40,8 +41,8 @@ public class HealthEndpointTests {
|
||||
.withDetail("first", "1").build());
|
||||
healthIndicators.put("upAgain", () -> new Health.Builder().status(Status.UP)
|
||||
.withDetail("second", "2").build());
|
||||
HealthEndpoint endpoint = new HealthEndpoint(
|
||||
createHealthIndicator(healthIndicators));
|
||||
HealthEndpoint endpoint = new HealthEndpoint(new OrderedHealthAggregator(),
|
||||
createHealthIndicatorRegistry(healthIndicators));
|
||||
Health health = endpoint.health();
|
||||
assertThat(health.getStatus()).isEqualTo(Status.UP);
|
||||
assertThat(health.getDetails()).containsOnlyKeys("up", "upAgain");
|
||||
@@ -51,10 +52,11 @@ public class HealthEndpointTests {
|
||||
assertThat(upAgainHealth.getDetails()).containsOnly(entry("second", "2"));
|
||||
}
|
||||
|
||||
private HealthIndicator createHealthIndicator(
|
||||
private HealthIndicatorRegistry createHealthIndicatorRegistry(
|
||||
Map<String, HealthIndicator> healthIndicators) {
|
||||
return new CompositeHealthIndicatorFactory()
|
||||
.createHealthIndicator(new OrderedHealthAggregator(), healthIndicators);
|
||||
HealthIndicatorRegistry registry = new DefaultHealthIndicatorRegistry();
|
||||
healthIndicators.forEach(registry::register);
|
||||
return registry;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
* exposed by Jersey, Spring MVC, and WebFlux.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Vedran Pavic
|
||||
*/
|
||||
@RunWith(WebEndpointRunners.class)
|
||||
public class HealthEndpointWebIntegrationTests {
|
||||
@@ -66,17 +67,15 @@ public class HealthEndpointWebIntegrationTests {
|
||||
@Bean
|
||||
public HealthEndpoint healthEndpoint(
|
||||
Map<String, HealthIndicator> healthIndicators) {
|
||||
return new HealthEndpoint(
|
||||
new CompositeHealthIndicatorFactory().createHealthIndicator(
|
||||
new OrderedHealthAggregator(), healthIndicators));
|
||||
HealthIndicatorRegistry registry = new DefaultHealthIndicatorRegistry();
|
||||
healthIndicators.forEach(registry::register);
|
||||
return new HealthEndpoint(new OrderedHealthAggregator(), registry);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public HealthEndpointWebExtension healthWebEndpointExtension(
|
||||
Map<String, HealthIndicator> healthIndicators) {
|
||||
return new HealthEndpointWebExtension(
|
||||
new CompositeHealthIndicatorFactory().createHealthIndicator(
|
||||
new OrderedHealthAggregator(), healthIndicators),
|
||||
HealthEndpoint healthEndpoint) {
|
||||
return new HealthEndpointWebExtension(healthEndpoint,
|
||||
new HealthWebEndpointResponseMapper(new HealthStatusHttpMapper(),
|
||||
ShowDetails.ALWAYS,
|
||||
new HashSet<>(Arrays.asList("ACTUATOR"))));
|
||||
|
||||
@@ -726,13 +726,14 @@ configuration must permit access to the health endpoint for both authenticated a
|
||||
unauthenticated users.
|
||||
|
||||
Health information is collected from all
|
||||
{sc-spring-boot-actuator}/health/HealthIndicator.{sc-ext}[`HealthIndicator`] beans
|
||||
defined in your `ApplicationContext`. Spring Boot includes a number of auto-configured
|
||||
`HealthIndicators`, and you can also write your own. By default, the final system state
|
||||
is derived by the `HealthAggregator`, which sorts the statuses from each
|
||||
`HealthIndicator` based on an ordered list of statuses. The first status in the sorted
|
||||
list is used as the overall health status. If no `HealthIndicator` returns a status that
|
||||
is known to the `HealthAggregator`, an `UNKNOWN` status is used.
|
||||
{sc-spring-boot-actuator}/health/HealthIndicator.{sc-ext}[`HealthIndicator`] instances
|
||||
registered with {sc-spring-boot-actuator}/health/HealthIndicatorRegistry.{sc-ext}[`HealthIndicatorRegistry`].
|
||||
Spring Boot includes a number of auto-configured `HealthIndicators` and you can also write
|
||||
your own. By default, the final system state is
|
||||
derived by the `HealthAggregator` which sorts the statuses from each `HealthIndicator`
|
||||
based on an ordered list of statuses. The first status in the sorted list is used as the
|
||||
overall health status. If no `HealthIndicator` returns a status that is known to the
|
||||
`HealthAggregator`, an `UNKNOWN` status is used.
|
||||
|
||||
|
||||
|
||||
@@ -818,6 +819,9 @@ NOTE: The identifier for a given `HealthIndicator` is the name of the bean witho
|
||||
`HealthIndicator` suffix, if it exists. In the preceding example, the health information
|
||||
is available in an entry named `my`.
|
||||
|
||||
Additionally, you can register (and unregister) `HealthIndicator` instances in runtime
|
||||
using {sc-spring-boot-actuator}/health/HealthIndicatorRegistry.{sc-ext}[`HealthIndicatorRegistry`].
|
||||
|
||||
In addition to Spring Boot's predefined
|
||||
{sc-spring-boot-actuator}/health/Status.{sc-ext}[`Status`] types, it is also possible for
|
||||
`Health` to return a custom `Status` that represents a new system state. In such cases, a
|
||||
|
||||
Reference in New Issue
Block a user