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    }