Skip to content

Commit 6c8c850

Browse files
snicollmbhave
andcommitted
Log failing calls to health indicators
Closes gh-22632 Co-authored-by: Madhura Bhave <[email protected]>
1 parent e0465f7 commit 6c8c850

File tree

3 files changed

+135
-4
lines changed

3 files changed

+135
-4
lines changed

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

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,15 +82,23 @@ public final Health health() {
8282
doHealthCheck(builder);
8383
}
8484
catch (Exception ex) {
85-
if (this.logger.isWarnEnabled()) {
86-
String message = this.healthCheckFailedMessage.apply(ex);
87-
this.logger.warn(StringUtils.hasText(message) ? message : DEFAULT_MESSAGE, ex);
88-
}
8985
builder.down(ex);
9086
}
87+
logExceptionIfPresent(builder);
9188
return builder.build();
9289
}
9390

91+
private void logExceptionIfPresent(Builder builder) {
92+
Throwable ex = builder.getException();
93+
if (ex != null && this.logger.isWarnEnabled()) {
94+
String message = null;
95+
if (ex instanceof Exception) {
96+
message = this.healthCheckFailedMessage.apply((Exception) ex);
97+
}
98+
this.logger.warn(StringUtils.hasText(message) ? message : DEFAULT_MESSAGE, ex);
99+
}
100+
}
101+
94102
/**
95103
* Actual health check logic.
96104
* @param builder the {@link Builder} to report health status and details

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,8 @@ public static class Builder {
193193

194194
private Map<String, Object> details;
195195

196+
private Throwable exception;
197+
196198
/**
197199
* Create new Builder instance.
198200
*/
@@ -231,6 +233,7 @@ public Builder(Status status, Map<String, ?> details) {
231233
*/
232234
public Builder withException(Throwable ex) {
233235
Assert.notNull(ex, "Exception must not be null");
236+
this.exception = ex;
234237
return withDetail("error", ex.getClass().getName() + ": " + ex.getMessage());
235238
}
236239

@@ -320,6 +323,15 @@ public Builder status(Status status) {
320323
return this;
321324
}
322325

326+
/**
327+
* Return the {@link Exception}.
328+
* @return the exception or {@code null} if the builder has no exception
329+
* @since 2.6.0
330+
*/
331+
public Throwable getException() {
332+
return this.exception;
333+
}
334+
323335
/**
324336
* Create a new {@link Health} instance with the previously specified code and
325337
* details.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/*
2+
* Copyright 2012-2020 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+
22+
import org.springframework.boot.actuate.health.Health.Builder;
23+
import org.springframework.boot.test.system.CapturedOutput;
24+
import org.springframework.boot.test.system.OutputCaptureExtension;
25+
26+
import static org.assertj.core.api.Assertions.assertThat;
27+
28+
/**
29+
* Tests for {@link AbstractHealthIndicator}.
30+
*
31+
* @author Stephane Nicoll
32+
* @author Madhura Bhave
33+
*/
34+
@ExtendWith(OutputCaptureExtension.class)
35+
class AbstractHealthIndicatorTests {
36+
37+
@Test
38+
void healthCheckWhenUpDoesNotLogHealthCheckFailedMessage(CapturedOutput output) {
39+
Health heath = new AbstractHealthIndicator("Test message") {
40+
@Override
41+
protected void doHealthCheck(Builder builder) {
42+
builder.up();
43+
}
44+
}.health();
45+
assertThat(heath.getStatus()).isEqualTo(Status.UP);
46+
assertThat(output).doesNotContain("Test message");
47+
}
48+
49+
@Test
50+
void healthCheckWhenDownWithExceptionThrownDoesNotLogHealthCheckFailedMessage(CapturedOutput output) {
51+
Health heath = new AbstractHealthIndicator("Test message") {
52+
@Override
53+
protected void doHealthCheck(Builder builder) {
54+
throw new IllegalStateException("Test exception");
55+
}
56+
}.health();
57+
assertThat(heath.getStatus()).isEqualTo(Status.DOWN);
58+
assertThat(output).contains("Test message").contains("Test exception");
59+
}
60+
61+
@Test
62+
void healthCheckWhenDownWithExceptionConfiguredDoesNotLogHealthCheckFailedMessage(CapturedOutput output) {
63+
Health heath = new AbstractHealthIndicator("Test message") {
64+
@Override
65+
protected void doHealthCheck(Builder builder) {
66+
builder.down().withException(new IllegalStateException("Test exception"));
67+
}
68+
}.health();
69+
assertThat(heath.getStatus()).isEqualTo(Status.DOWN);
70+
assertThat(output).contains("Test message").contains("Test exception");
71+
}
72+
73+
@Test
74+
void healthCheckWhenDownWithExceptionConfiguredDoesNotLogHealthCheckFailedMessageTwice(CapturedOutput output) {
75+
Health heath = new AbstractHealthIndicator("Test message") {
76+
@Override
77+
protected void doHealthCheck(Builder builder) {
78+
IllegalStateException ex = new IllegalStateException("Test exception");
79+
builder.down().withException(ex);
80+
throw ex;
81+
}
82+
}.health();
83+
assertThat(heath.getStatus()).isEqualTo(Status.DOWN);
84+
assertThat(output).contains("Test message").containsOnlyOnce("Test exception");
85+
}
86+
87+
@Test
88+
void healthCheckWhenDownWithExceptionAndNoFailureMessageLogsDefaultMessage(CapturedOutput output) {
89+
Health heath = new AbstractHealthIndicator() {
90+
@Override
91+
protected void doHealthCheck(Builder builder) {
92+
builder.down().withException(new IllegalStateException("Test exception"));
93+
}
94+
}.health();
95+
assertThat(heath.getStatus()).isEqualTo(Status.DOWN);
96+
assertThat(output).contains("Health check failed").contains("Test exception");
97+
}
98+
99+
@Test
100+
void healthCheckWhenDownWithErrorLogsDefaultMessage(CapturedOutput output) {
101+
Health heath = new AbstractHealthIndicator("Test Message") {
102+
@Override
103+
protected void doHealthCheck(Builder builder) {
104+
builder.down().withException(new Error("Test error"));
105+
}
106+
}.health();
107+
assertThat(heath.getStatus()).isEqualTo(Status.DOWN);
108+
assertThat(output).contains("Health check failed").contains("Test error");
109+
}
110+
111+
}

0 commit comments

Comments
 (0)