/*
* 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
* trunk/opends/resource/legal-notices/OpenDS.LICENSE
* or https://OpenDS.dev.java.net/OpenDS.LICENSE.
* 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
* trunk/opends/resource/legal-notices/OpenDS.LICENSE. 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.
*/
package org.opends.server.loggers.debug;
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.
*/
public 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.
*/
public 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.
*/
public 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 &&
isFrameForPackage(frames[firstFrame],
"org.opends.server.loggers.debug")) {
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.
*/
public 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 executation.
*
* @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.
*/
public 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();
}
}