/*
* 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();
}
}