PrintStackTrace.java
/*
* SPDX-FileCopyrightText: 2025 kaumei.io
* SPDX-License-Identifier: Apache-2.0
*/
package io.kaumei.jdbc.anno.utils;
import java.util.Arrays;
import java.util.IdentityHashMap;
import java.util.Map;
public final class PrintStackTrace {
private static final StackTraceElement[] EMPTY = new StackTraceElement[0];
private PrintStackTrace() {
}
public static void appendStackTrace(StringBuilder sb, Throwable t) {
printThrowable(sb, t, "", EMPTY, new IdentityHashMap<>());
}
public static String appendStackTrace(Throwable t) {
var sb = new StringBuilder();
printThrowable(sb, t, "", EMPTY, new IdentityHashMap<>());
return sb.toString();
}
private static void printThrowable(
StringBuilder sb,
Throwable t,
String prefix,
StackTraceElement[] parentTrace,
Map<Throwable, Boolean> seen) {
// using IdentityHashMap as a Set
if (seen.put(t, Boolean.TRUE) != null) {
sb.append(prefix).append("[CIRCULAR REFERENCE: ").append(t).append("]\n");
return;
}
StackTraceElement[] trace = trimCallerSide(t.getStackTrace());
int commonFrames = countCommonFrames(trace, parentTrace);
sb.append(prefix).append(t).append("\n");
for (int i = 0; i < trace.length - commonFrames; i++) {
sb.append(prefix).append("\tat ").append(trace[i]).append("\n");
}
if (commonFrames > 0) {
sb.append(prefix).append("\t... ").append(commonFrames).append(" more\n");
}
for (Throwable suppressed : t.getSuppressed()) {
sb.append(prefix).append("\tSuppressed: ");
printThrowable(sb, suppressed, prefix + "\t", trace, seen);
}
Throwable cause = t.getCause();
if (cause != null) {
sb.append(prefix).append("Caused by: ");
printThrowable(sb, cause, prefix, trace, seen);
}
}
private static StackTraceElement[] trimCallerSide(StackTraceElement[] trace) {
for (var i = trace.length - 1; i >= 0; i--) {
if (trace[i].getClassName().startsWith("io.kaumei")) {
return Arrays.copyOfRange(trace, 0, i + 1);
}
}
return trace;
}
private static int countCommonFrames(StackTraceElement[] trace, StackTraceElement[] parentTrace) {
int i = trace.length - 1;
int j = parentTrace.length - 1;
int count = 0;
while (i >= 0 && j >= 0 && trace[i].equals(parentTrace[j])) {
count++;
i--;
j--;
}
return count;
}
}