This is an automated email from the ASF dual-hosted git repository.
Croway pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-spring-boot.git
The following commit(s) were added to refs/heads/main by this push:
new a4632a181bb CAMEL-24512: camel-spring-boot - keep each failing health
check's error message
a4632a181bb is described below
commit a4632a181bbc6c315b8bfa57e6bb2ec914ff394b
Author: Andrea Cosentino <[email protected]>
AuthorDate: Wed Sep 2 15:28:03 2026 +0200
CAMEL-24512: camel-spring-boot - keep each failing health check's error
message
CamelHealthCheckIndicator applies every health check result to the same
Health.Builder, and error.message was written only as a flat top-level
detail.
Health.Builder.withDetail is a Map.put, so with more than one DOWN check
the last
result overwrote the messages of all the earlier ones.
The message is now also written into the check-scoped <id>.data map,
alongside
the other per-check entries, so each failing check keeps its own. The
top-level
key is left in place for compatibility.
The diff is deliberately confined to error.message: PR #1929 (CAMEL-24592)
changes the stack-trace gating in the same method.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_0165HC1XCB3h6mMii6wbZneG
Signed-off-by: Andrea Cosentino <[email protected]>
---
.../boot/actuate/health/CamelHealthHelper.java | 4 +
.../CamelHealthHelperMultipleFailuresTest.java | 86 ++++++++++++++++++++++
2 files changed, 90 insertions(+)
diff --git
a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/actuate/health/CamelHealthHelper.java
b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/actuate/health/CamelHealthHelper.java
index fe50d3e781f..7db55b403f4 100644
---
a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/actuate/health/CamelHealthHelper.java
+++
b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/actuate/health/CamelHealthHelper.java
@@ -61,7 +61,11 @@ final class CamelHealthHelper {
result.getError().ifPresent(error -> {
if (error.getMessage() != null) {
+ // the top-level key is kept for compatibility, but it is
a single slot on a builder shared
+ // by every check, so with more than one DOWN check the
last one wins; the check-scoped copy
+ // is the one that stays addressable per check
builder.withDetail("error.message", error.getMessage());
+ data.put("error.message", error.getMessage());
}
// the stack trace is the most verbose detail there is, so
only include it in full exposure level
if (exposureLevel.equals("full")) {
diff --git
a/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/actuate/health/CamelHealthHelperMultipleFailuresTest.java
b/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/actuate/health/CamelHealthHelperMultipleFailuresTest.java
new file mode 100644
index 00000000000..bb78f62a4e2
--- /dev/null
+++
b/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/actuate/health/CamelHealthHelperMultipleFailuresTest.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.camel.spring.boot.actuate.health;
+
+import org.apache.camel.health.HealthCheck;
+import org.apache.camel.health.HealthCheckResultBuilder;
+import org.apache.camel.impl.health.AbstractHealthCheck;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.health.contributor.Health;
+
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+/**
+ * The indicator applies every health check result to one {@link
Health.Builder}, so a detail written at the top level
+ * is a single slot shared by all of them. Each failing check has to keep its
own message addressable.
+ */
+public class CamelHealthHelperMultipleFailuresTest {
+
+ private static final class TestHealthCheck extends AbstractHealthCheck {
+ private TestHealthCheck(String id) {
+ super("test", id);
+ }
+
+ @Override
+ protected void doCall(HealthCheckResultBuilder builder, Map<String,
Object> options) {
+ builder.down();
+ }
+ }
+
+ private static HealthCheck.Result downResult(String id, Throwable error) {
+ return HealthCheckResultBuilder.on(new
TestHealthCheck(id)).down().error(error).build();
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Map<String, String> checkData(Health health, String id) {
+ Object data = health.getDetails().get(id + ".data");
+ assertNotNull(data, "expected per-check data for " + id + ", details
were: " + health.getDetails());
+ assertInstanceOf(Map.class, data);
+ return (Map<String, String>) data;
+ }
+
+ @Test
+ public void eachFailingCheckKeepsItsOwnMessage() {
+ Health.Builder builder = new Health.Builder();
+
+ CamelHealthHelper.applyHealthDetail(builder,
+ downResult("first", new
IllegalArgumentException("first-message")), "default");
+ CamelHealthHelper.applyHealthDetail(builder,
+ downResult("second", new
IllegalStateException("second-message")), "default");
+
+ Health health = builder.build();
+
+ assertEquals("first-message", checkData(health,
"first").get("error.message"),
+ "the first check's message must survive a later failing
check");
+ assertEquals("second-message", checkData(health,
"second").get("error.message"));
+ }
+
+ @Test
+ public void topLevelErrorMessageIsStillReported() {
+ Health.Builder builder = new Health.Builder();
+
+ CamelHealthHelper.applyHealthDetail(builder,
+ downResult("only", new IllegalStateException("the-message")),
"default");
+
+ assertEquals("the-message",
builder.build().getDetails().get("error.message"),
+ "the top-level key is kept for compatibility");
+ }
+}