Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion core/camel-spring-boot/src/main/docs/spring-boot.json
Original file line number Diff line number Diff line change
Expand Up @@ -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. The oneline level will only report either UP or DOWN.",
"sourceType": "org.apache.camel.spring.boot.actuate.health.CamelHealthCheckConfigurationProperties",
"defaultValue": "default"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,13 @@ public static void applyHealthDetail(Health.Builder builder, HealthCheck.Result
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());
}
}
});

Expand Down
Original file line number Diff line number Diff line change
@@ -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();
}
}

}
Loading