org.keycloak.common.util.StackUtil Maven / Gradle / Ivy
The newest version!
package org.keycloak.common.util;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
import org.jboss.logging.Logger;
/**
*
* @author hmlnarik
*/
public class StackUtil {
private static final Logger LOG = Logger.getLogger("org.keycloak.STACK_TRACE");
private static final ConcurrentHashMap STACK_TRACE_OBJECTS = new ConcurrentHashMap<>();
/**
* Returns string representation of the stack trace of the current call
* without the call to the {@code getShortStackTrace} itself, and ignoring
* usually irrelevant calls to methods in {@code sun.} and {@code java.lang.reflect}
* packages. The stack trace ignores calls before and including the first
* {@code org.jboss.resteasy} method, hence it usually finishes with the
* method handling respective REST endpoint.
*
* Each line of the stack trace is prepended with {@code "\n "}.
*
* @return If the logger {@code org.keycloak.STACK_TRACE} is set to trace
* level, then returns stack trace, else returns empty {@link StringBuilder}
*/
public static Object getShortStackTrace() {
return getShortStackTrace("\n ");
}
private static final Pattern IGNORED = Pattern.compile("sun\\.|"
+ "java\\.(lang|util|stream)\\.|"
+ "jdk\\.internal\\.|"
+ "org\\.jboss\\.(arquillian|logging|logmanager|threads).|"
+ "org.apache.maven.surefire|"
+ "org\\.xnio\\.|"
+ "org\\.junit\\.|"
+ "org\\.infinispan\\.(interceptors|cache|notifications\\.cachelistener)\\.|"
+ "io\\.quarkus\\.|"
+ "io\\.undertow\\.|"
+ "picocli\\.|"
+ "org.keycloak.testsuite.model.KeycloakModelTest\\."
);
private static final StringBuilder EMPTY = new StringBuilder(0);
/**
* Returns string representation of the stack trace of the current call
* without the call to the {@code getShortStackTrace} itself, and ignoring
* usually irrelevant calls to methods in {@code sun.} and {@code java.lang.reflect}
* packages. The stack trace ignores calls before and including the first
* {@code org.jboss.resteasy} method, hence it usually finishes with the
* method handling respective REST endpoint.
*
* @param prefix Prefix to prepend to every stack trace line
* @return If the logger {@code org.keycloak.STACK_TRACE} is set to trace
* level, then returns stack trace, else returns empty {@link StringBuilder}
*/
public static Object getShortStackTrace(final String prefix) {
if (! isShortStackTraceEnabled()) return EMPTY;
Object res = STACK_TRACE_OBJECTS.get(prefix);
if (res == null) {
res = stackTraceObject(prefix);
// Do not synchronize. We don't care if the objects in the map get overridden, they are in the end the same.
STACK_TRACE_OBJECTS.put(prefix, res);
}
return res;
}
private static Object stackTraceObject(final String prefix) {
return new Object() {
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
StackTraceElement[] stackTrace = (new Throwable()).getStackTrace();
boolean stackTraceStarted = false;
for (int endIndex = 0; endIndex < stackTrace.length; endIndex++) {
StackTraceElement st = stackTrace[endIndex];
if (! stackTraceStarted) {
stackTraceStarted = (getClass().getName().equals(st.getClassName()));
endIndex++;
continue;
}
if (IGNORED.matcher(st.getClassName()).find()) {
continue;
}
if (st.getClassName().startsWith("org.jboss.resteasy")) {
break;
}
sb.append(prefix).append(st);
}
return sb.toString();
}
};
}
public static boolean isShortStackTraceEnabled() {
return LOG.isTraceEnabled();
}
}