1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.core.pattern;
18
19 import java.io.PrintWriter;
20 import java.io.StringWriter;
21 import java.util.ArrayList;
22 import java.util.Collections;
23 import java.util.List;
24
25 import org.apache.logging.log4j.core.LogEvent;
26 import org.apache.logging.log4j.core.config.Configuration;
27 import org.apache.logging.log4j.core.config.plugins.Plugin;
28 import org.apache.logging.log4j.core.impl.ThrowableFormatOptions;
29 import org.apache.logging.log4j.core.layout.PatternLayout;
30 import org.apache.logging.log4j.core.util.StringBuilderWriter;
31 import org.apache.logging.log4j.util.Strings;
32
33
34
35
36
37
38
39 @Plugin(name = "ThrowablePatternConverter", category = PatternConverter.CATEGORY)
40 @ConverterKeys({ "ex", "throwable", "exception" })
41 public class ThrowablePatternConverter extends LogEventPatternConverter {
42
43
44
45
46 protected final List<PatternFormatter> formatters;
47 private String rawOption;
48 private final boolean subShortOption;
49 private final boolean nonStandardLineSeparator;
50
51
52
53
54 protected final ThrowableFormatOptions options;
55
56
57
58
59
60
61
62
63 @Deprecated
64 protected ThrowablePatternConverter(final String name, final String style, final String[] options) {
65 this(name, style, options, null);
66 }
67
68
69
70
71
72
73
74
75 protected ThrowablePatternConverter(final String name, final String style, final String[] options, final Configuration config) {
76 super(name, style);
77 this.options = ThrowableFormatOptions.newInstance(options);
78 if (options != null && options.length > 0) {
79 rawOption = options[0];
80 }
81 if (this.options.getSuffix() != null) {
82 final PatternParser parser = PatternLayout.createPatternParser(config);
83 final List<PatternFormatter> parsedSuffixFormatters = parser.parse(this.options.getSuffix());
84
85 boolean hasThrowableSuffixFormatter = false;
86 for (final PatternFormatter suffixFormatter : parsedSuffixFormatters) {
87 if (suffixFormatter.handlesThrowable()) {
88 hasThrowableSuffixFormatter = true;
89 }
90 }
91 if (!hasThrowableSuffixFormatter) {
92 this.formatters = parsedSuffixFormatters;
93 } else {
94 final List<PatternFormatter> suffixFormatters = new ArrayList<>();
95 for (final PatternFormatter suffixFormatter : parsedSuffixFormatters) {
96 if (!suffixFormatter.handlesThrowable()) {
97 suffixFormatters.add(suffixFormatter);
98 }
99 }
100 this.formatters = suffixFormatters;
101 }
102 } else {
103 this.formatters = Collections.emptyList();
104 }
105 subShortOption = ThrowableFormatOptions.MESSAGE.equalsIgnoreCase(rawOption) ||
106 ThrowableFormatOptions.LOCALIZED_MESSAGE.equalsIgnoreCase(rawOption) ||
107 ThrowableFormatOptions.FILE_NAME.equalsIgnoreCase(rawOption) ||
108 ThrowableFormatOptions.LINE_NUMBER.equalsIgnoreCase(rawOption) ||
109 ThrowableFormatOptions.METHOD_NAME.equalsIgnoreCase(rawOption) ||
110 ThrowableFormatOptions.CLASS_NAME.equalsIgnoreCase(rawOption);
111 nonStandardLineSeparator = !Strings.LINE_SEPARATOR.equals(this.options.getSeparator());
112 }
113
114
115
116
117
118
119
120
121
122 public static ThrowablePatternConverter newInstance(final Configuration config, final String[] options) {
123 return new ThrowablePatternConverter("Throwable", "throwable", options, config);
124 }
125
126
127
128
129 @Override
130 public void format(final LogEvent event, final StringBuilder buffer) {
131 final Throwable t = event.getThrown();
132
133 if (subShortOption) {
134 formatSubShortOption(t, getSuffix(event), buffer);
135 }
136 else if (t != null && options.anyLines()) {
137 formatOption(t, getSuffix(event), buffer);
138 }
139 }
140
141 private void formatSubShortOption(final Throwable t, final String suffix, final StringBuilder buffer) {
142 StackTraceElement[] trace;
143 StackTraceElement throwingMethod = null;
144 int len;
145
146 if (t != null) {
147 trace = t.getStackTrace();
148 if (trace !=null && trace.length > 0) {
149 throwingMethod = trace[0];
150 }
151 }
152
153 if (t != null && throwingMethod != null) {
154 String toAppend = Strings.EMPTY;
155
156 if (ThrowableFormatOptions.CLASS_NAME.equalsIgnoreCase(rawOption)) {
157 toAppend = throwingMethod.getClassName();
158 }
159 else if (ThrowableFormatOptions.METHOD_NAME.equalsIgnoreCase(rawOption)) {
160 toAppend = throwingMethod.getMethodName();
161 }
162 else if (ThrowableFormatOptions.LINE_NUMBER.equalsIgnoreCase(rawOption)) {
163 toAppend = String.valueOf(throwingMethod.getLineNumber());
164 }
165 else if (ThrowableFormatOptions.MESSAGE.equalsIgnoreCase(rawOption)) {
166 toAppend = t.getMessage();
167 }
168 else if (ThrowableFormatOptions.LOCALIZED_MESSAGE.equalsIgnoreCase(rawOption)) {
169 toAppend = t.getLocalizedMessage();
170 }
171 else if (ThrowableFormatOptions.FILE_NAME.equalsIgnoreCase(rawOption)) {
172 toAppend = throwingMethod.getFileName();
173 }
174
175 len = buffer.length();
176 if (len > 0 && !Character.isWhitespace(buffer.charAt(len - 1))) {
177 buffer.append(' ');
178 }
179 buffer.append(toAppend);
180
181 if (Strings.isNotBlank(suffix)) {
182 buffer.append(' ');
183 buffer.append(suffix);
184 }
185 }
186 }
187
188 private void formatOption(final Throwable throwable, final String suffix, final StringBuilder buffer) {
189 final int len = buffer.length();
190 if (len > 0 && !Character.isWhitespace(buffer.charAt(len - 1))) {
191 buffer.append(' ');
192 }
193 if (!options.allLines() || nonStandardLineSeparator || Strings.isNotBlank(suffix)) {
194 final StringWriter w = new StringWriter();
195 throwable.printStackTrace(new PrintWriter(w));
196
197 final String[] array = w.toString().split(Strings.LINE_SEPARATOR);
198 final int limit = options.minLines(array.length) - 1;
199 final boolean suffixNotBlank = Strings.isNotBlank(suffix);
200 for (int i = 0; i <= limit; ++i) {
201 buffer.append(array[i]);
202 if (suffixNotBlank) {
203 buffer.append(' ');
204 buffer.append(suffix);
205 }
206 if (i < limit) {
207 buffer.append(options.getSeparator());
208 }
209 }
210 } else {
211 throwable.printStackTrace(new PrintWriter(new StringBuilderWriter(buffer)));
212 }
213 }
214
215
216
217
218
219
220 @Override
221 public boolean handlesThrowable() {
222 return true;
223 }
224
225 protected String getSuffix(final LogEvent event) {
226 if (formatters.isEmpty()) {
227 return Strings.EMPTY;
228 }
229
230 final StringBuilder toAppendTo = new StringBuilder();
231 for (int i = 0, size = formatters.size(); i < size; i++) {
232 formatters.get(i).format(event, toAppendTo);
233 }
234 return toAppendTo.toString();
235 }
236
237 public ThrowableFormatOptions getOptions() {
238 return options;
239 }
240 }