Merge pull request #4592 from yashdotdev13/issue-4135-log-health-contributor-status

Log health contributor statuses when Eureka health status changes
This commit is contained in:
Ryan Baxter
2026-09-02 17:03:07 -04:00
committed by Ryan Baxter
parent ab72b970c0
commit 0df610d62e
2 changed files with 65 additions and 13 deletions
@@ -24,6 +24,8 @@ import java.util.Set;
import com.netflix.appinfo.HealthCheckHandler;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.appinfo.InstanceInfo.InstanceStatus;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
@@ -68,6 +70,8 @@ import org.springframework.util.Assert;
public class EurekaHealthCheckHandler
implements HealthCheckHandler, ApplicationContextAware, InitializingBean, Ordered, Lifecycle {
private static final Log log = LogFactory.getLog(EurekaHealthCheckHandler.class);
private static final Map<Status, InstanceInfo.InstanceStatus> STATUS_MAPPING = new HashMap<>() {
{
put(Status.UNKNOWN, InstanceStatus.UNKNOWN);
@@ -127,7 +131,11 @@ public class EurekaHealthCheckHandler
@Override
public InstanceStatus getStatus(InstanceStatus instanceStatus) {
if (running) {
return getHealthStatus();
InstanceStatus status = getHealthStatus();
if (status != instanceStatus) {
log.info("Eureka health status changed to " + status);
}
return status;
}
else {
// Return nothing if the context is not running, so the status held by the
@@ -143,37 +151,41 @@ public class EurekaHealthCheckHandler
}
protected Status getStatus(StatusAggregator statusAggregator) {
Set<Status> statusSet = new HashSet<>();
for (HealthContributor contributor : healthContributors.values()) {
processContributor(statusSet, contributor);
Set<Status> statuses = new HashSet<>();
for (Map.Entry<String, HealthContributor> entry : healthContributors.entrySet()) {
processContributor(statuses, entry.getKey(), entry.getValue());
}
for (ReactiveHealthContributor contributor : reactiveHealthContributors.values()) {
processContributor(statusSet, contributor);
for (Map.Entry<String, ReactiveHealthContributor> entry : reactiveHealthContributors.entrySet()) {
processContributor(statuses, entry.getKey(), entry.getValue());
}
return statusAggregator.getAggregateStatus(statusSet);
return statusAggregator.getAggregateStatus(statuses);
}
private void processContributor(Set<Status> statusSet, HealthContributor contributor) {
private void processContributor(Set<Status> statuses, String name, HealthContributor contributor) {
if (contributor instanceof CompositeHealthContributor) {
for (HealthContributors.Entry contrib : (CompositeHealthContributor) contributor) {
processContributor(statusSet, contrib.contributor());
processContributor(statuses, contrib.name(), contrib.contributor());
}
}
else if (contributor instanceof HealthIndicator) {
statusSet.add(((HealthIndicator) contributor).health().getStatus());
Status status = ((HealthIndicator) contributor).health().getStatus();
log.debug("Health contributor " + name + " has status " + status);
statuses.add(status);
}
}
private void processContributor(Set<Status> statusSet, ReactiveHealthContributor contributor) {
private void processContributor(Set<Status> statuses, String name, ReactiveHealthContributor contributor) {
if (contributor instanceof CompositeReactiveHealthContributor) {
for (ReactiveHealthContributors.Entry contrib : (CompositeReactiveHealthContributor) contributor) {
processContributor(statusSet, contrib.contributor());
processContributor(statuses, contrib.name(), contrib.contributor());
}
}
else if (contributor instanceof ReactiveHealthIndicator) {
Health health = ((ReactiveHealthIndicator) contributor).health().block();
if (health != null) {
statusSet.add(health.getStatus());
Status status = health.getStatus();
log.debug("Health contributor " + name + " has status " + status);
statuses.add(status);
}
}
}
@@ -30,6 +30,7 @@ import org.springframework.boot.health.actuate.endpoint.SimpleStatusAggregator;
import org.springframework.boot.health.contributor.AbstractHealthIndicator;
import org.springframework.boot.health.contributor.AbstractReactiveHealthIndicator;
import org.springframework.boot.health.contributor.CompositeHealthContributor;
import org.springframework.boot.health.contributor.CompositeReactiveHealthContributor;
import org.springframework.boot.health.contributor.Health;
import org.springframework.boot.health.contributor.HealthContributor;
import org.springframework.boot.health.contributor.HealthContributors;
@@ -41,6 +42,7 @@ import org.springframework.cloud.client.discovery.health.DiscoveryHealthIndicato
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.assertj.core.api.Assertions.assertThat;
@@ -195,6 +197,33 @@ class EurekaHealthCheckHandlerTests {
}
public static class NamedUpHealthConfiguration {
@Bean
public HealthIndicator upHealthIndicator() {
return () -> Health.up().build();
}
}
public static class NamedDownHealthConfiguration {
@Bean
public HealthIndicator downHealthIndicator() {
return () -> Health.down().build();
}
}
public static class NamedReactiveUpHealthConfiguration {
@Bean
public ReactiveHealthIndicator reactiveUpHealthIndicator() {
return () -> Mono.just(Health.up().build());
}
}
public static class FatalHealthConfiguration {
@Bean
@@ -327,4 +356,15 @@ class EurekaHealthCheckHandlerTests {
}
@Configuration
static class NamedReactiveDownHealthConfiguration {
@Bean
CompositeReactiveHealthContributor namedReactiveDownHealthContributor() {
return CompositeReactiveHealthContributor.fromMap(Map.of("reactiveDownHealthIndicator",
(ReactiveHealthIndicator) () -> Mono.just(Health.down().build())));
}
}
}