View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.core.impl;
18  
19  import org.apache.logging.log4j.core.pattern.TextRenderer;
20  import org.apache.logging.log4j.util.Strings;
21  
22  import java.util.List;
23  
24  /**
25   * {@link ThrowableProxyRenderer} is an internal utility providing the code to render a {@link ThrowableProxy}
26   * to a {@link StringBuilder}.
27   */
28  class ThrowableProxyRenderer {
29  
30      private static final String TAB = "\t";
31      private static final String CAUSED_BY_LABEL = "Caused by: ";
32      private static final String SUPPRESSED_LABEL = "Suppressed: ";
33      private static final String WRAPPED_BY_LABEL = "Wrapped by: ";
34  
35      private ThrowableProxyRenderer() {
36          // Utility Class
37      }
38  
39      @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
40      static void formatWrapper(final StringBuilder sb, final ThrowableProxy cause, final List<String> ignorePackages,
41                                final TextRenderer textRenderer, final String suffix, final String lineSeparator) {
42          final Throwable caused = cause.getCauseProxy() != null ? cause.getCauseProxy().getThrowable() : null;
43          if (caused != null) {
44              formatWrapper(sb, cause.getCauseProxy(), ignorePackages, textRenderer, suffix, lineSeparator);
45              sb.append(WRAPPED_BY_LABEL);
46              renderSuffix(suffix, sb, textRenderer);
47          }
48          renderOn(cause, sb, textRenderer);
49          renderSuffix(suffix, sb, textRenderer);
50          textRenderer.render(lineSeparator, sb, "Text");
51          formatElements(sb, Strings.EMPTY, cause.getCommonElementCount(),
52                  cause.getThrowable().getStackTrace(), cause.getExtendedStackTrace(), ignorePackages, textRenderer, suffix, lineSeparator);
53      }
54  
55      private static void formatCause(final StringBuilder sb, final String prefix, final ThrowableProxy cause,
56                                      final List<String> ignorePackages, final TextRenderer textRenderer, final String suffix, final String lineSeparator) {
57          formatThrowableProxy(sb, prefix, CAUSED_BY_LABEL, cause, ignorePackages, textRenderer, suffix, lineSeparator);
58      }
59  
60      private static void formatThrowableProxy(final StringBuilder sb, final String prefix, final String causeLabel,
61                                               final ThrowableProxy throwableProxy, final List<String> ignorePackages,
62                                               final TextRenderer textRenderer, final String suffix, final String lineSeparator) {
63          if (throwableProxy == null) {
64              return;
65          }
66          textRenderer.render(prefix, sb, "Prefix");
67          textRenderer.render(causeLabel, sb, "CauseLabel");
68          renderOn(throwableProxy, sb, textRenderer);
69          renderSuffix(suffix, sb, textRenderer);
70          textRenderer.render(lineSeparator, sb, "Text");
71          formatElements(sb, prefix, throwableProxy.getCommonElementCount(),
72                  throwableProxy.getStackTrace(), throwableProxy.getExtendedStackTrace(), ignorePackages, textRenderer, suffix, lineSeparator);
73          formatSuppressed(sb, prefix + TAB, throwableProxy.getSuppressedProxies(), ignorePackages, textRenderer, suffix, lineSeparator);
74          formatCause(sb, prefix, throwableProxy.getCauseProxy(), ignorePackages, textRenderer, suffix, lineSeparator);
75      }
76  
77      private static void formatSuppressed(final StringBuilder sb, final String prefix, final ThrowableProxy[] suppressedProxies,
78                                           final List<String> ignorePackages, final TextRenderer textRenderer, final String suffix, final String lineSeparator) {
79          if (suppressedProxies == null) {
80              return;
81          }
82          for (final ThrowableProxy suppressedProxy : suppressedProxies) {
83              formatThrowableProxy(sb, prefix, SUPPRESSED_LABEL, suppressedProxy, ignorePackages, textRenderer, suffix, lineSeparator);
84          }
85      }
86  
87      private static void formatElements(final StringBuilder sb, final String prefix, final int commonCount,
88                                         final StackTraceElement[] causedTrace, final ExtendedStackTraceElement[] extStackTrace,
89                                         final List<String> ignorePackages, final TextRenderer textRenderer, final String suffix, final String lineSeparator) {
90          if (ignorePackages == null || ignorePackages.isEmpty()) {
91              for (final ExtendedStackTraceElement element : extStackTrace) {
92                  formatEntry(element, sb, prefix, textRenderer, suffix, lineSeparator);
93              }
94          } else {
95              int count = 0;
96              for (int i = 0; i < extStackTrace.length; ++i) {
97                  if (!ignoreElement(causedTrace[i], ignorePackages)) {
98                      if (count > 0) {
99                          appendSuppressedCount(sb, prefix, count, textRenderer, suffix, lineSeparator);
100                         count = 0;
101                     }
102                     formatEntry(extStackTrace[i], sb, prefix, textRenderer, suffix, lineSeparator);
103                 } else {
104                     ++count;
105                 }
106             }
107             if (count > 0) {
108                 appendSuppressedCount(sb, prefix, count, textRenderer, suffix, lineSeparator);
109             }
110         }
111         if (commonCount != 0) {
112             textRenderer.render(prefix, sb, "Prefix");
113             textRenderer.render("\t... ", sb, "More");
114             textRenderer.render(Integer.toString(commonCount), sb, "More");
115             textRenderer.render(" more", sb, "More");
116             renderSuffix(suffix, sb, textRenderer);
117             textRenderer.render(lineSeparator, sb, "Text");
118         }
119     }
120 
121     private static void renderSuffix(final String suffix, final StringBuilder sb, final TextRenderer textRenderer) {
122         if (!suffix.isEmpty()) {
123             textRenderer.render(" ", sb, "Suffix");
124             textRenderer.render(suffix, sb, "Suffix");
125         }
126     }
127 
128     private static void appendSuppressedCount(final StringBuilder sb, final String prefix, final int count,
129                                               final TextRenderer textRenderer, final String suffix, final String lineSeparator) {
130         textRenderer.render(prefix, sb, "Prefix");
131         if (count == 1) {
132             textRenderer.render("\t... ", sb, "Suppressed");
133         } else {
134             textRenderer.render("\t... suppressed ", sb, "Suppressed");
135             textRenderer.render(Integer.toString(count), sb, "Suppressed");
136             textRenderer.render(" lines", sb, "Suppressed");
137         }
138         renderSuffix(suffix, sb, textRenderer);
139         textRenderer.render(lineSeparator, sb, "Text");
140     }
141 
142     private static void formatEntry(final ExtendedStackTraceElement extStackTraceElement, final StringBuilder sb,
143                                     final String prefix, final TextRenderer textRenderer, final String suffix, final String lineSeparator) {
144         textRenderer.render(prefix, sb, "Prefix");
145         textRenderer.render("\tat ", sb, "At");
146         extStackTraceElement.renderOn(sb, textRenderer);
147         renderSuffix(suffix, sb, textRenderer);
148         textRenderer.render(lineSeparator, sb, "Text");
149     }
150 
151     private static boolean ignoreElement(final StackTraceElement element, final List<String> ignorePackages) {
152         if (ignorePackages != null) {
153             final String className = element.getClassName();
154             for (final String pkg : ignorePackages) {
155                 if (className.startsWith(pkg)) {
156                     return true;
157                 }
158             }
159         }
160         return false;
161     }
162 
163     /**
164      * Formats the stack trace including packaging information.
165      *
166      * @param src            ThrowableProxy instance to format
167      * @param sb             Destination.
168      * @param ignorePackages List of packages to be ignored in the trace.
169      * @param textRenderer   The message renderer.
170      * @param suffix         Append this to the end of each stack frame.
171      * @param lineSeparator  The end-of-line separator.
172      */
173     static void formatExtendedStackTraceTo(final ThrowableProxy src, final StringBuilder sb, final List<String> ignorePackages, final TextRenderer textRenderer, final String suffix, final String lineSeparator) {
174         textRenderer.render(src.getName(), sb, "Name");
175         textRenderer.render(": ", sb, "NameMessageSeparator");
176         textRenderer.render(src.getMessage(), sb, "Message");
177         renderSuffix(suffix, sb, textRenderer);
178         textRenderer.render(lineSeparator, sb, "Text");
179         final StackTraceElement[] causedTrace = src.getThrowable() != null ? src.getThrowable().getStackTrace() : null;
180         formatElements(sb, Strings.EMPTY, 0, causedTrace, src.getExtendedStackTrace(), ignorePackages, textRenderer, suffix, lineSeparator);
181         formatSuppressed(sb, TAB, src.getSuppressedProxies(), ignorePackages, textRenderer, suffix, lineSeparator);
182         formatCause(sb, Strings.EMPTY, src.getCauseProxy(), ignorePackages, textRenderer, suffix, lineSeparator);
183     }
184 
185     /**
186      * Formats the Throwable that is the cause of the <pre>src</pre> Throwable.
187      *
188      * @param src            Throwable whose cause to render
189      * @param sb             Destination to render the formatted Throwable that caused this Throwable onto.
190      * @param ignorePackages The List of packages to be suppressed from the stack trace.
191      * @param textRenderer   The text renderer.
192      * @param suffix         Append this to the end of each stack frame.
193      * @param lineSeparator  The end-of-line separator.
194      */
195     static void formatCauseStackTrace(final ThrowableProxy src, final StringBuilder sb, final List<String> ignorePackages, final TextRenderer textRenderer, final String suffix, final String lineSeparator) {
196         final ThrowableProxy causeProxy = src.getCauseProxy();
197         if (causeProxy != null) {
198             formatWrapper(sb, causeProxy, ignorePackages, textRenderer, suffix, lineSeparator);
199             sb.append(WRAPPED_BY_LABEL);
200             ThrowableProxyRenderer.renderSuffix(suffix, sb, textRenderer);
201         }
202         renderOn(src, sb, textRenderer);
203         ThrowableProxyRenderer.renderSuffix(suffix, sb, textRenderer);
204         textRenderer.render(lineSeparator, sb, "Text");
205         ThrowableProxyRenderer.formatElements(sb, Strings.EMPTY, 0, src.getStackTrace(), src.getExtendedStackTrace(),
206                 ignorePackages, textRenderer, suffix, lineSeparator);
207     }
208 
209     private static void renderOn(final ThrowableProxy src, final StringBuilder output, final TextRenderer textRenderer) {
210         final String msg = src.getMessage();
211         textRenderer.render(src.getName(), output, "Name");
212         if (msg != null) {
213             textRenderer.render(": ", output, "NameMessageSeparator");
214             textRenderer.render(msg, output, "Message");
215         }
216     }
217 }