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