001 /* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2006-2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.loggers.debug; 028 029 import static org.opends.server.util.ServerConstants.EOL; 030 031 /** 032 * A DebugStackTraceFormatter converts an exception's stack trace into 033 * a String appropriate for tracing, optionally performing filtering 034 * of stack frames. 035 */ 036 public class DebugStackTraceFormatter 037 { 038 /** 039 * The stack depth value to indicate the entire stack should be printed. 040 */ 041 public static final int COMPLETE_STACK= Integer.MAX_VALUE; 042 /** 043 * A nested frame filter that removes debug and trailing no OpenDS frames. 044 */ 045 public static final FrameFilter SMART_FRAME_FILTER = new SmartFrameFilter(); 046 047 /** 048 * A FrameFilter provides stack frame filtering used during formatting. 049 */ 050 public interface FrameFilter { 051 052 /** 053 * Filters out all undesired stack frames from the given Throwable's 054 * stack trace. 055 * @param frames the frames to filter 056 * @return an array of StackTraceElements to be used in formatting. 057 */ 058 public StackTraceElement[] getFilteredStackTrace( 059 StackTraceElement[] frames); 060 } 061 062 /** 063 * A basic FrameFilter that filters out frames from the debug logging and 064 * non OpenDS classes. 065 */ 066 private static class SmartFrameFilter implements FrameFilter { 067 068 private boolean isFrameForPackage(StackTraceElement frame, 069 String packageName) 070 { 071 boolean isContained= false; 072 073 if (frame != null) { 074 String className= frame.getClassName(); 075 isContained= className != null && className.startsWith(packageName); 076 } 077 return isContained; 078 } 079 080 /** 081 * Return the stack trace of an exception with debug and trailing non 082 * OpenDS frames filtered out. 083 * 084 * @param frames the frames to filter 085 * @return the filtered stack trace. 086 */ 087 public StackTraceElement[] getFilteredStackTrace( 088 StackTraceElement[] frames) 089 { 090 StackTraceElement[] trimmedStack= null; 091 if (frames != null && frames.length > 0) { 092 int firstFrame= 0; 093 094 // Skip leading frames debug logging classes 095 while (firstFrame < frames.length && 096 isFrameForPackage(frames[firstFrame], 097 "org.opends.server.loggers.debug")) { 098 firstFrame++; 099 } 100 101 // Skip trailing frames not in OpenDS classes 102 int lastFrame= frames.length - 1; 103 while (lastFrame > firstFrame && 104 !isFrameForPackage(frames[lastFrame], "org.opends")) { 105 lastFrame--; 106 } 107 108 trimmedStack= new StackTraceElement[lastFrame - firstFrame + 1]; 109 for (int i= firstFrame; i <= lastFrame; i++) { 110 trimmedStack[i - firstFrame]= frames[i]; 111 } 112 } 113 114 return trimmedStack; 115 } 116 } 117 118 /** 119 * Generate a String representation of the entire stack trace of the 120 * given Throwable. 121 * @param t - the Throwable for which to generate the stack trace. 122 * @return the stack trace. 123 */ 124 public static String formatStackTrace(Throwable t) 125 { 126 return formatStackTrace(t, COMPLETE_STACK, true); 127 } 128 129 /** 130 * Generate a String representation of the possibly filtered stack trace 131 * of the given Throwable. 132 * @param t - the Throwable for which to generate the stack trace. 133 * @param maxDepth - the maximum number of stack frames to include in the 134 * trace. 135 * @param includeCause - also include the stack trace for the cause Throwable. 136 * @return the stack trace. 137 */ 138 public static String formatStackTrace(Throwable t, int maxDepth, 139 boolean includeCause) 140 { 141 StringBuilder buffer= new StringBuilder(); 142 143 StackTraceElement[] trace = t.getStackTrace(); 144 int frameLimit = Math.min(maxDepth, trace.length); 145 for (int i=0; i < frameLimit; i++) 146 { 147 buffer.append(" at "); 148 buffer.append(trace[i]); 149 buffer.append(EOL); 150 } 151 if(frameLimit < trace.length) 152 { 153 buffer.append(" ... "); 154 buffer.append(trace.length - frameLimit); 155 buffer.append(" more"); 156 buffer.append(EOL); 157 } 158 159 if(includeCause) 160 { 161 Throwable ourCause = t.getCause(); 162 if (ourCause != null) 163 { 164 formatStackTraceForCause(ourCause, maxDepth, buffer, trace); 165 } 166 } 167 168 return buffer.toString(); 169 } 170 171 private static void formatStackTraceForCause(Throwable t, int maxDepth, 172 StringBuilder buffer, 173 StackTraceElement[] causedTrace) 174 { 175 StackTraceElement[] trace = t.getStackTrace(); 176 int framesToSkip = Math.max(trace.length - maxDepth, 0); 177 178 // Compute number of frames in common between this and caused 179 int m = trace.length - 1 - framesToSkip; 180 int n = causedTrace.length - 1 - framesToSkip; 181 while (m >= 0 && n >=0 && trace[m].equals(causedTrace[n])) { 182 m--; n--; 183 } 184 framesToSkip = trace.length - 1 - m; 185 186 buffer.append("Caused by: "); 187 buffer.append(t); 188 buffer.append(EOL); 189 for (int i=0; i <= m; i++) 190 { 191 buffer.append(" at "); 192 buffer.append(trace[i]); 193 buffer.append(EOL); 194 } 195 if (framesToSkip != 0) 196 { 197 buffer.append(" ... "); 198 buffer.append(framesToSkip); 199 buffer.append(" more"); 200 buffer.append(EOL); 201 } 202 203 // Recurse if we have a cause 204 Throwable ourCause = t.getCause(); 205 if (ourCause != null) 206 formatStackTraceForCause(ourCause, maxDepth, buffer, trace); 207 } 208 209 /** 210 * Generate a String representation of the possibly filtered stack trace 211 * from the current position in executation. 212 * 213 * @param stackTrace - The stack trace elements to format. 214 * @param maxDepth - the maximum number of stack frames to include in the 215 * trace. 216 * @return the stack trace. 217 */ 218 public static String formatStackTrace(StackTraceElement[] stackTrace, 219 int maxDepth) 220 { 221 StringBuilder buffer= new StringBuilder(); 222 223 if (stackTrace != null) { 224 int frameLimit= Math.min(maxDepth, stackTrace.length); 225 if (frameLimit > 0) { 226 227 228 for (int i= 0; i < frameLimit; i++) { 229 buffer.append(" "); 230 buffer.append(stackTrace[i]); 231 buffer.append(EOL); 232 } 233 234 if(frameLimit < stackTrace.length) 235 { 236 buffer.append(" ...("); 237 buffer.append(stackTrace.length - frameLimit); 238 buffer.append(" more)"); 239 buffer.append(EOL); 240 } 241 } 242 } 243 244 return buffer.toString(); 245 } 246 }