/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt * or http://forgerock.org/license/CDDLv1.0.html. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at legal-notices/CDDLv1_0.txt. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2006-2008 Sun Microsystems, Inc. * Portions Copyright 2014-2015 ForgeRock AS. */ package org.opends.server.loggers; import static org.opends.server.util.ServerConstants.EOL; /** * A DebugStackTraceFormatter converts an exception's stack trace into a String * appropriate for tracing, optionally performing filtering of stack frames. */ class DebugStackTraceFormatter { /** * The stack depth value to indicate the entire stack should be printed. */ public static final int COMPLETE_STACK = Integer.MAX_VALUE; /** * A nested frame filter that removes debug and trailing no OpenDS frames. */ public static final FrameFilter SMART_FRAME_FILTER = new SmartFrameFilter(); /** * A FrameFilter provides stack frame filtering used during formatting. */ interface FrameFilter { /** * Filters out all undesired stack frames from the given Throwable's stack * trace. * * @param frames * the frames to filter * @return an array of StackTraceElements to be used in formatting. */ StackTraceElement[] getFilteredStackTrace(StackTraceElement[] frames); } /** * A basic FrameFilter that filters out frames from the debug logging and non * OpenDS classes. */ private static class SmartFrameFilter implements FrameFilter { private boolean isFrameForPackage(StackTraceElement frame, String packageName) { boolean isContained = false; if (frame != null) { String className = frame.getClassName(); isContained = className != null && className.startsWith(packageName); } return isContained; } /** * Return the stack trace of an exception with debug and trailing non OpenDS * frames filtered out. * * @param frames * the frames to filter * @return the filtered stack trace. */ public StackTraceElement[] getFilteredStackTrace(StackTraceElement[] frames) { StackTraceElement[] trimmedStack = null; if (frames != null && frames.length > 0) { int firstFrame = 0; // Skip leading frames debug logging classes while (firstFrame < frames.length && DebugTracer.isLoggingStackTraceElement(frames[firstFrame])) { firstFrame++; } // Skip trailing frames not in OpenDS classes int lastFrame = frames.length - 1; while (lastFrame > firstFrame && !isFrameForPackage(frames[lastFrame], "org.opends")) { lastFrame--; } trimmedStack = new StackTraceElement[lastFrame - firstFrame + 1]; for (int i = firstFrame; i <= lastFrame; i++) { trimmedStack[i - firstFrame] = frames[i]; } } return trimmedStack; } } /** * Generate a String representation of the entire stack trace of the given * Throwable. * * @param t * - the Throwable for which to generate the stack trace. * @return the stack trace. */ public static String formatStackTrace(Throwable t) { return formatStackTrace(t, COMPLETE_STACK, true); } /** * Generate a String representation of the possibly filtered stack trace of * the given Throwable. * * @param t * - the Throwable for which to generate the stack trace. * @param maxDepth * - the maximum number of stack frames to include in the trace. * @param includeCause * - also include the stack trace for the cause Throwable. * @return the stack trace. */ static String formatStackTrace(Throwable t, int maxDepth, boolean includeCause) { StringBuilder buffer = new StringBuilder(); StackTraceElement[] trace = t.getStackTrace(); int frameLimit = Math.min(maxDepth, trace.length); for (int i = 0; i < frameLimit; i++) { buffer.append(" at "); buffer.append(trace[i]); buffer.append(EOL); } if (frameLimit < trace.length) { buffer.append(" ... "); buffer.append(trace.length - frameLimit); buffer.append(" more"); buffer.append(EOL); } if (includeCause) { Throwable ourCause = t.getCause(); if (ourCause != null) { formatStackTraceForCause(ourCause, maxDepth, buffer, trace); } } return buffer.toString(); } private static void formatStackTraceForCause(Throwable t, int maxDepth, StringBuilder buffer, StackTraceElement[] causedTrace) { StackTraceElement[] trace = t.getStackTrace(); int framesToSkip = Math.max(trace.length - maxDepth, 0); // Compute number of frames in common between this and caused int m = trace.length - 1 - framesToSkip; int n = causedTrace.length - 1 - framesToSkip; while (m >= 0 && n >= 0 && trace[m].equals(causedTrace[n])) { m--; n--; } framesToSkip = trace.length - 1 - m; buffer.append("Caused by: "); buffer.append(t); buffer.append(EOL); for (int i = 0; i <= m; i++) { buffer.append(" at "); buffer.append(trace[i]); buffer.append(EOL); } if (framesToSkip != 0) { buffer.append(" ... "); buffer.append(framesToSkip); buffer.append(" more"); buffer.append(EOL); } // Recurse if we have a cause Throwable ourCause = t.getCause(); if (ourCause != null) { formatStackTraceForCause(ourCause, maxDepth, buffer, trace); } } /** * Generate a String representation of the possibly filtered stack trace from * the current position in execution. * * @param stackTrace * - The stack trace elements to format. * @param maxDepth * - the maximum number of stack frames to include in the trace. * @return the stack trace. */ static String formatStackTrace(StackTraceElement[] stackTrace, int maxDepth) { StringBuilder buffer = new StringBuilder(); if (stackTrace != null) { int frameLimit = Math.min(maxDepth, stackTrace.length); if (frameLimit > 0) { for (int i = 0; i < frameLimit; i++) { buffer.append(" "); buffer.append(stackTrace[i]); buffer.append(EOL); } if (frameLimit < stackTrace.length) { buffer.append(" ...("); buffer.append(stackTrace.length - frameLimit); buffer.append(" more)"); buffer.append(EOL); } } } return buffer.toString(); } }