diff --git a/common/src/main/java/org/keycloak/common/util/StackUtil.java b/common/src/main/java/org/keycloak/common/util/StackUtil.java index 22a74f6aae..062335f7fc 100644 --- a/common/src/main/java/org/keycloak/common/util/StackUtil.java +++ b/common/src/main/java/org/keycloak/common/util/StackUtil.java @@ -1,5 +1,6 @@ package org.keycloak.common.util; +import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Pattern; import org.jboss.logging.Logger; @@ -11,6 +12,8 @@ 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 @@ -24,11 +27,11 @@ public class StackUtil { * @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 StringBuilder getShortStackTrace() { + public static Object getShortStackTrace() { return getShortStackTrace("\n "); } - private static final Pattern IGNORED = Pattern.compile("^sun\\.|java\\.lang\\.reflect\\."); + private static final Pattern IGNORED = Pattern.compile("sun\\.|java\\.(lang|util|stream)\\.|org\\.jboss\\.logging."); private static final StringBuilder EMPTY = new StringBuilder(0); /** @@ -43,22 +46,43 @@ public class StackUtil { * @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 StringBuilder getShortStackTrace(String prefix) { + public static Object getShortStackTrace(final String prefix) { if (! isShortStackTraceEnabled()) return EMPTY; - StringBuilder sb = new StringBuilder(); - StackTraceElement[] stackTrace = (new Throwable()).getStackTrace(); - for (int endIndex = 2; endIndex < stackTrace.length; endIndex++) { - StackTraceElement st = stackTrace[endIndex]; - if (IGNORED.matcher(st.getClassName()).find()) { - continue; - } - if (st.getClassName().startsWith("org.jboss.resteasy")) { - break; - } - sb.append(prefix).append(st); + 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 sb; + 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() {