diff --git a/src/main/java/sirius/kernel/health/Exceptions.java b/src/main/java/sirius/kernel/health/Exceptions.java
index 977cbdf3..1a8d8445 100644
--- a/src/main/java/sirius/kernel/health/Exceptions.java
+++ b/src/main/java/sirius/kernel/health/Exceptions.java
@@ -8,6 +8,7 @@
package sirius.kernel.health;
+import com.google.common.base.Throwables;
import sirius.kernel.async.CallContext;
import sirius.kernel.commons.Explain;
import sirius.kernel.commons.Strings;
@@ -17,6 +18,7 @@
import sirius.kernel.nls.NLS;
import javax.annotation.Nullable;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -121,11 +123,11 @@ protected ErrorHandler(boolean processError) {
* one can add %s (%s) to the message provided here to output the actual error and
* exception type if needed.
*
- * @param e the exception which needs to be attached to this error handler
+ * @param throwable the exception which needs to be attached to this error handler
* @return this in order to fluently call more methods on this handler
*/
- public ErrorHandler error(Throwable e) {
- this.ex = e;
+ public ErrorHandler error(Throwable throwable) {
+ this.ex = throwable;
return this;
}
@@ -251,14 +253,14 @@ public HandledException handle() {
IGNORED_EXCEPTIONS_LOG.INFO(result);
}
return result;
- } catch (Exception t) {
+ } catch (Exception exception) {
// We call as few external methods a possible here, since things are really messed up right now
- t.printStackTrace();
+ exception.printStackTrace();
return new HandledException("Kernel Panic: Exception-Handling threw another exception: "
- + t.getMessage()
+ + exception.getMessage()
+ " ("
- + t.getClass().getName()
- + ")", Collections.emptyMap(), t);
+ + exception.getClass().getName()
+ + ")", Collections.emptyMap(), exception);
}
}
@@ -325,9 +327,9 @@ private String computeLocation(HandledException result) {
/*
* Adds the exception message and the exception class to the given params array. Handles null values for
- * e gracefully
+ * the throwable gracefully
*/
- private Object[] extendParams(Throwable e, Object[] params) {
+ private Object[] extendParams(Throwable throwable, Object[] params) {
Object[] newParams;
if (params == null) {
newParams = new Object[2];
@@ -335,9 +337,9 @@ private Object[] extendParams(Throwable e, Object[] params) {
newParams = new Object[params.length + 2];
System.arraycopy(params, 0, newParams, 0, params.length);
}
- if (e != null) {
- newParams[newParams.length - 2] = e.getMessage();
- newParams[newParams.length - 1] = e.getClass().getName();
+ if (throwable != null) {
+ newParams[newParams.length - 2] = throwable.getMessage();
+ newParams[newParams.length - 1] = throwable.getClass().getName();
} else {
newParams[newParams.length - 2] = NLS.get("HandledException.unknownError");
newParams[newParams.length - 1] = "UnknownError";
@@ -391,24 +393,24 @@ public static ErrorHandler handle() {
/**
* Boilerplate method the directly handle the given exception without a special message or logger
*
- * @param e the exception to handle
+ * @param throwable the exception to handle
* @return a HandledException which notifies surrounding calls that an error occurred, which has
* already been taken care of.
*/
- public static HandledException handle(Throwable e) {
- return handle().error(e).handle();
+ public static HandledException handle(Throwable throwable) {
+ return handle().error(throwable).handle();
}
/**
* Boilerplate method the directly handle the given exception without a special message
*
- * @param log the logger used to log the exception
- * @param e the exception to handle
+ * @param log the logger used to log the exception
+ * @param throwable the exception to handle
* @return a HandledException which notifies surrounding calls that an error occurred, which has
* already been taken care of.
*/
- public static HandledException handle(Log log, Throwable e) {
- return handle().error(e).to(log).handle();
+ public static HandledException handle(Log log, Throwable throwable) {
+ return handle().error(throwable).to(log).handle();
}
/**
@@ -431,11 +433,11 @@ public static ErrorHandler createHandled() {
* exception is wanted to be ignored. Additionally, the ignoredExceptions logger can be turned on,
* to still see those exceptions.
*
- * @param t the exception to be ignored. This exception will be discarded unless the ignoredExceptions
- * logger is set to INFO.
+ * @param throwable the exception to be ignored. This exception will be discarded unless the ignoredExceptions
+ * logger is set to INFO.
*/
- public static void ignore(Throwable t) {
- IGNORED_EXCEPTIONS_LOG.INFO(t);
+ public static void ignore(Throwable throwable) {
+ IGNORED_EXCEPTIONS_LOG.INFO(throwable);
}
/**
@@ -461,8 +463,8 @@ public static void logDeprecatedMethodUse() {
List> mdc = CallContext.getCurrent().getMDC();
if (mdc != null) {
msg.append("\n---------------------------------------------------\n");
- for (Tuple t : mdc) {
- msg.append(t.getFirst()).append(": ").append(t.getSecond()).append("\n");
+ for (Tuple tuple : mdc) {
+ msg.append(tuple.getFirst()).append(": ").append(tuple.getSecond()).append("\n");
}
}
@@ -472,15 +474,15 @@ public static void logDeprecatedMethodUse() {
/**
* Retrieves the actual root {@link Throwable} which ended in the given exception.
*
- * @param e the throwable to begin with
+ * @param throwable the throwable to begin with
* @return the root {@link Throwable} of the given one
*/
- public static Throwable getRootCause(@Nullable Throwable e) {
- if (e == null) {
+ public static Throwable getRootCause(@Nullable Throwable throwable) {
+ if (throwable == null) {
return null;
}
- Throwable cause = e;
+ Throwable cause = throwable;
int circuitBreaker = 11;
while (circuitBreaker > 0 && cause.getCause() != null && !cause.equals(cause.getCause())) {
@@ -490,4 +492,46 @@ public static Throwable getRootCause(@Nullable Throwable e) {
return cause;
}
+
+ /**
+ * Generates a stack trace for the given throwable and its causes without the error messages.
+ *
+ * @param throwable the throwable to generate the stack trace for
+ * @return a string representation of the stack trace without the error message
+ */
+ public static String buildStackTraceWithoutErrorMessage(Throwable throwable) {
+ StringBuilder stringBuilder = new StringBuilder();
+
+ stringBuilder.append(throwable.getClass().getName()).append(":").append("\n");
+ appendStackTrace(stringBuilder, throwable);
+
+ try {
+ // The first element of the causal chain is always the throwable followed by its cause hierarchy.
+ // Therefore, the first element is skipped.
+ Throwables.getCausalChain(throwable).stream().skip(1).forEach(cause -> {
+ stringBuilder.append("Caused by: ").append(cause.getClass().getName()).append(":").append("\n");
+ appendStackTrace(stringBuilder, cause);
+ });
+ } catch (IllegalArgumentException exception) {
+ // This happens if the causal chain has a circular reference.
+ stringBuilder.append("Warning: Circular reference detected in causal chain. Skipping causes: ")
+ .append(exception.getMessage());
+ }
+
+ return stringBuilder.toString();
+ }
+
+ /**
+ * Appends the stack trace of the given throwable to the given string builder.
+ *
+ * @param stringBuilder the string builder to append the stack trace to
+ * @param throwable the throwable to append the stack trace of
+ */
+ private static void appendStackTrace(StringBuilder stringBuilder, Throwable throwable) {
+ Arrays.stream(throwable.getStackTrace())
+ .forEach(stackTraceElement -> stringBuilder.append("\t")
+ .append("at ")
+ .append(stackTraceElement)
+ .append("\n"));
+ }
}