Skip to content

Commit cbb7467

Browse files
committed
Merge pull request #33774 from mhalbritter
* pr/33774: Log failing calls to health indicators Closes gh-33774
2 parents 6ef73cd + d7852cb commit cbb7467

File tree

3 files changed

+140
-5
lines changed

3 files changed

+140
-5
lines changed

spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/AbstractReactiveHealthIndicator.java

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
*
3232
* @author Stephane Nicoll
3333
* @author Nikolay Rybak
34+
* @author Moritz Halbritter
3435
* @since 2.0.0
3536
*/
3637
public abstract class AbstractReactiveHealthIndicator implements ReactiveHealthIndicator {
@@ -75,19 +76,24 @@ protected AbstractReactiveHealthIndicator(Function<Throwable, String> healthChec
7576

7677
@Override
7778
public final Mono<Health> health() {
79+
Mono<Health> result;
7880
try {
79-
return doHealthCheck(new Health.Builder()).onErrorResume(this::handleFailure);
81+
result = doHealthCheck(new Health.Builder()).onErrorResume(this::handleFailure);
8082
}
8183
catch (Exception ex) {
82-
return handleFailure(ex);
84+
result = handleFailure(ex);
8385
}
86+
return result.doOnNext((health) -> logExceptionIfPresent(health.getException()));
8487
}
8588

86-
private Mono<Health> handleFailure(Throwable ex) {
87-
if (this.logger.isWarnEnabled()) {
88-
String message = this.healthCheckFailedMessage.apply(ex);
89+
private void logExceptionIfPresent(Throwable ex) {
90+
if (ex != null && this.logger.isWarnEnabled()) {
91+
String message = (ex instanceof Exception) ? this.healthCheckFailedMessage.apply(ex) : null;
8992
this.logger.warn(StringUtils.hasText(message) ? message : DEFAULT_MESSAGE, ex);
9093
}
94+
}
95+
96+
private Mono<Health> handleFailure(Throwable ex) {
9197
return Mono.just(new Health.Builder().down(ex).build());
9298
}
9399

spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/Health.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.util.LinkedHashMap;
2121
import java.util.Map;
2222

23+
import com.fasterxml.jackson.annotation.JsonIgnore;
2324
import com.fasterxml.jackson.annotation.JsonInclude;
2425
import com.fasterxml.jackson.annotation.JsonInclude.Include;
2526

@@ -46,6 +47,7 @@
4647
* @author Christian Dupuis
4748
* @author Phillip Webb
4849
* @author Michael Pratt
50+
* @author Moritz Halbritter
4951
* @since 1.1.0
5052
*/
5153
@JsonInclude(Include.NON_EMPTY)
@@ -55,6 +57,8 @@ public final class Health extends HealthComponent {
5557

5658
private final Map<String, Object> details;
5759

60+
private final Throwable exception;
61+
5862
/**
5963
* Create a new {@link Health} instance with the specified status and details.
6064
* @param builder the Builder to use
@@ -63,11 +67,13 @@ private Health(Builder builder) {
6367
Assert.notNull(builder, "Builder must not be null");
6468
this.status = builder.status;
6569
this.details = Collections.unmodifiableMap(builder.details);
70+
this.exception = builder.exception;
6671
}
6772

6873
Health(Status status, Map<String, Object> details) {
6974
this.status = status;
7075
this.details = details;
76+
this.exception = null;
7177
}
7278

7379
/**
@@ -101,6 +107,11 @@ Health withoutDetails() {
101107
return status(getStatus()).build();
102108
}
103109

110+
@JsonIgnore
111+
Throwable getException() {
112+
return this.exception;
113+
}
114+
104115
@Override
105116
public boolean equals(Object obj) {
106117
if (obj == this) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/*
2+
* Copyright 2012-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.actuate.health;
18+
19+
import org.junit.jupiter.api.Test;
20+
import org.junit.jupiter.api.extension.ExtendWith;
21+
import reactor.core.publisher.Mono;
22+
23+
import org.springframework.boot.actuate.health.Health.Builder;
24+
import org.springframework.boot.test.system.CapturedOutput;
25+
import org.springframework.boot.test.system.OutputCaptureExtension;
26+
27+
import static org.assertj.core.api.Assertions.assertThat;
28+
29+
/**
30+
* Tests for {@link AbstractReactiveHealthIndicator}.
31+
*
32+
* @author Moritz Halbritter
33+
*/
34+
@ExtendWith(OutputCaptureExtension.class)
35+
class AbstractReactiveHealthIndicatorTests {
36+
37+
@Test
38+
void healthCheckWhenUpDoesNotLogHealthCheckFailedMessage(CapturedOutput output) {
39+
Health health = new AbstractReactiveHealthIndicator("Test message") {
40+
@Override
41+
protected Mono<Health> doHealthCheck(Builder builder) {
42+
return Mono.just(builder.up().build());
43+
}
44+
45+
}.health().block();
46+
assertThat(health).isNotNull();
47+
assertThat(health.getStatus()).isEqualTo(Status.UP);
48+
assertThat(output).doesNotContain("Test message");
49+
}
50+
51+
@Test
52+
void healthCheckWhenDownWithExceptionThrownDoesNotLogHealthCheckFailedMessage(CapturedOutput output) {
53+
Health health = new AbstractReactiveHealthIndicator("Test message") {
54+
@Override
55+
protected Mono<Health> doHealthCheck(Builder builder) {
56+
throw new IllegalStateException("Test exception");
57+
}
58+
}.health().block();
59+
assertThat(health).isNotNull();
60+
assertThat(health.getStatus()).isEqualTo(Status.DOWN);
61+
assertThat(output).contains("Test message").contains("Test exception");
62+
}
63+
64+
@Test
65+
void healthCheckWhenDownWithExceptionConfiguredDoesNotLogHealthCheckFailedMessage(CapturedOutput output) {
66+
Health health = new AbstractReactiveHealthIndicator("Test message") {
67+
@Override
68+
protected Mono<Health> doHealthCheck(Builder builder) {
69+
return Mono.just(builder.down().withException(new IllegalStateException("Test exception")).build());
70+
}
71+
}.health().block();
72+
assertThat(health).isNotNull();
73+
assertThat(health.getStatus()).isEqualTo(Status.DOWN);
74+
assertThat(output).contains("Test message").contains("Test exception");
75+
}
76+
77+
@Test
78+
void healthCheckWhenDownWithExceptionConfiguredDoesNotLogHealthCheckFailedMessageTwice(CapturedOutput output) {
79+
Health health = new AbstractReactiveHealthIndicator("Test message") {
80+
@Override
81+
protected Mono<Health> doHealthCheck(Builder builder) {
82+
IllegalStateException ex = new IllegalStateException("Test exception");
83+
builder.down().withException(ex);
84+
throw ex;
85+
}
86+
}.health().block();
87+
assertThat(health).isNotNull();
88+
assertThat(health.getStatus()).isEqualTo(Status.DOWN);
89+
assertThat(output).contains("Test message").containsOnlyOnce("Test exception");
90+
}
91+
92+
@Test
93+
void healthCheckWhenDownWithExceptionAndNoFailureMessageLogsDefaultMessage(CapturedOutput output) {
94+
Health health = new AbstractReactiveHealthIndicator() {
95+
@Override
96+
protected Mono<Health> doHealthCheck(Builder builder) {
97+
return Mono.just(builder.down().withException(new IllegalStateException("Test exception")).build());
98+
}
99+
}.health().block();
100+
assertThat(health).isNotNull();
101+
assertThat(health.getStatus()).isEqualTo(Status.DOWN);
102+
assertThat(output).contains("Health check failed").contains("Test exception");
103+
}
104+
105+
@Test
106+
void healthCheckWhenDownWithErrorLogsDefaultMessage(CapturedOutput output) {
107+
Health health = new AbstractReactiveHealthIndicator("Test Message") {
108+
@Override
109+
protected Mono<Health> doHealthCheck(Builder builder) {
110+
return Mono.just(builder.down().withException(new Error("Test error")).build());
111+
}
112+
}.health().block();
113+
assertThat(health).isNotNull();
114+
assertThat(health.getStatus()).isEqualTo(Status.DOWN);
115+
assertThat(output).contains("Health check failed").contains("Test error");
116+
}
117+
118+
}

0 commit comments

Comments
 (0)