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 b391410f837 CAMEL-24592: only expose health check stack traces in full 
exposure level
b391410f837 is described below

commit b391410f837c051d51782847f77bb63924891ffd
Author: croway <[email protected]>
AuthorDate: Wed Sep 2 14:49:13 2026 +0200

    CAMEL-24592: only expose health check stack traces in full exposure level
    
    CamelHealthHelper.applyHealthDetail added the full stack trace of a failed
    health check as error.stacktrace to the per-check data at every exposure 
level
    except oneline, so a DOWN check whose result carries an exception 
serialised the
    whole cause chain into /actuator/health at the default exposure level.
    
    error.stacktrace is now emitted only when the exposure level is full.
    error.message is still reported at the default level, which is what Spring
    Boot's own health indicators expose; camel-main's management endpoint is
    stricter still and includes error-stacktrace only when the caller asks with
    ?stackTrace=true. It also matches the documented meaning of the levels, 
where
    full is the level that includes all details from the invoked health checks.
    camel-microprofile-health, through which Camel Quarkus builds its health
    responses, gates the trace the same way in a companion change so the 
runtimes
    stay aligned. The trace is unchanged in the application log.
    
    Adds CamelHealthHelperTest covering a DOWN check carrying an exception at 
the
    default, full and oneline levels, and regenerates spring-boot.json for the
    clarified exposure-level description.
    
    Co-Authored-By: Claude Opus 5 <[email protected]>
---
 .../src/main/docs/spring-boot.json                 |   2 +-
 .../CamelHealthCheckConfigurationProperties.java   |   5 +-
 .../boot/actuate/health/CamelHealthHelper.java     |  11 ++-
 .../boot/actuate/health/CamelHealthHelperTest.java | 103 +++++++++++++++++++++
 4 files changed, 114 insertions(+), 7 deletions(-)

diff --git a/core/camel-spring-boot/src/main/docs/spring-boot.json 
b/core/camel-spring-boot/src/main/docs/spring-boot.json
index 5c360d573e6..29389f13467 100644
--- a/core/camel-spring-boot/src/main/docs/spring-boot.json
+++ b/core/camel-spring-boot/src/main/docs/spring-boot.json
@@ -378,7 +378,7 @@
     {
       "name": "camel.health.exposure-level",
       "type": "java.lang.String",
-      "description": "Sets the level of details to exposure as result of 
invoking health checks. There are the following levels: full, default, oneline 
The full level will include all details and status from all the invoked health 
checks. The default level will report UP if everything is okay, and only 
include detailed information for health checks that was DOWN. The oneline level 
will only report either UP or DOWN.",
+      "description": "Sets the level of details to exposure as result of 
invoking health checks. There are the following levels: full, default, oneline 
The full level will include all details and status from all the invoked health 
checks, including the stack trace of any error carried by a health check 
result. The default level will report UP if everything is okay, and only 
include detailed information for health checks that was DOWN, such as the error 
message, but not the stack trace. T [...]
       "sourceType": 
"org.apache.camel.spring.boot.actuate.health.CamelHealthCheckConfigurationProperties",
       "defaultValue": "default"
     },
diff --git 
a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/actuate/health/CamelHealthCheckConfigurationProperties.java
 
b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/actuate/health/CamelHealthCheckConfigurationProperties.java
index 350af04f1e7..f6c171c5e2d 100644
--- 
a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/actuate/health/CamelHealthCheckConfigurationProperties.java
+++ 
b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/actuate/health/CamelHealthCheckConfigurationProperties.java
@@ -89,10 +89,11 @@ public class CamelHealthCheckConfigurationProperties {
      * Sets the level of details to exposure as result of invoking health 
checks. There are the following levels: full,
      * default, oneline
      *
-     * The full level will include all details and status from all the invoked 
health checks.
+     * The full level will include all details and status from all the invoked 
health checks, including the stack trace
+     * of any error carried by a health check result.
      *
      * The default level will report UP if everything is okay, and only 
include detailed information for health checks
-     * that was DOWN.
+     * that was DOWN, such as the error message, but not the stack trace.
      *
      * The oneline level will only report either UP or DOWN.
      */
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 c6da07f0ade..fe50d3e781f 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
@@ -63,10 +63,13 @@ final class CamelHealthHelper {
                 if (error.getMessage() != null) {
                     builder.withDetail("error.message", error.getMessage());
                 }
-                final StringWriter stackTraceWriter = new StringWriter();
-                try (final PrintWriter pw = new PrintWriter(stackTraceWriter, 
true)) {
-                    error.printStackTrace(pw);
-                    data.put("error.stacktrace", stackTraceWriter.toString());
+                // the stack trace is the most verbose detail there is, so 
only include it in full exposure level
+                if (exposureLevel.equals("full")) {
+                    final StringWriter stackTraceWriter = new StringWriter();
+                    try (final PrintWriter pw = new 
PrintWriter(stackTraceWriter, true)) {
+                        error.printStackTrace(pw);
+                        data.put("error.stacktrace", 
stackTraceWriter.toString());
+                    }
                 }
             });
 
diff --git 
a/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/actuate/health/CamelHealthHelperTest.java
 
b/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/actuate/health/CamelHealthHelperTest.java
new file mode 100644
index 00000000000..3ba3bf17fd1
--- /dev/null
+++ 
b/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/actuate/health/CamelHealthHelperTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Tests that the stack trace of a failing health check is only exposed in the 
full exposure level.
+ */
+public class CamelHealthHelperTest {
+
+    private static final String MY_CHECK_ID = "my-check";
+
+    @Test
+    public void defaultExposureLevelShouldNotIncludeStackTrace() {
+        Health health = applyDownResult("default");
+
+        assertEquals("Cannot connect to broker", 
health.getDetails().get("error.message"));
+        Map<String, String> data = data(health);
+        assertEquals("my-route", data.get("route.id"));
+        assertFalse(data.containsKey("error.stacktrace"), "Stack trace should 
not be exposed at default level");
+    }
+
+    @Test
+    public void fullExposureLevelShouldIncludeStackTrace() {
+        Health health = applyDownResult("full");
+
+        assertEquals("Cannot connect to broker", 
health.getDetails().get("error.message"));
+        Map<String, String> data = data(health);
+        assertEquals("my-route", data.get("route.id"));
+        String stackTrace = data.get("error.stacktrace");
+        assertTrue(stackTrace != null && stackTrace.contains("Cannot connect 
to broker"),
+                "Stack trace should be exposed at full level");
+        assertTrue(stackTrace.contains(CamelHealthHelperTest.class.getName()), 
"Stack trace should contain the frames");
+    }
+
+    @Test
+    public void onelineExposureLevelShouldNotIncludeAnyDetail() {
+        Health health = applyDownResult("oneline");
+
+        assertNull(health.getDetails().get("error.message"));
+        assertNull(health.getDetails().get(MY_CHECK_ID + ".data"));
+    }
+
+    private static Health applyDownResult(String exposureLevel) {
+        HealthCheck check = new MyHealthCheck();
+        HealthCheck.Result result = HealthCheckResultBuilder.on(check)
+                .down()
+                .error(new IllegalStateException("Cannot connect to broker"))
+                .detail("route.id", "my-route")
+                .build();
+
+        Health.Builder builder = new Health.Builder();
+        CamelHealthHelper.applyHealthDetail(builder, result, exposureLevel);
+        return builder.down().build();
+    }
+
+    @SuppressWarnings("unchecked")
+    private static Map<String, String> data(Health health) {
+        Object data = health.getDetails().get(MY_CHECK_ID + ".data");
+        assertInstanceOf(Map.class, data, "Expected health check data to be 
present");
+        return (Map<String, String>) data;
+    }
+
+    private static final class MyHealthCheck extends AbstractHealthCheck {
+
+        private MyHealthCheck() {
+            super(MY_CHECK_ID);
+        }
+
+        @Override
+        protected void doCall(HealthCheckResultBuilder builder, Map<String, 
Object> options) {
+            builder.down();
+        }
+    }
+
+}

Reply via email to