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 }