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 protected ThrowablePatternConverter(final String name, final String style, final String[] options, final Configuration config) {
64 super(name, style);
65 this.options = ThrowableFormatOptions.newInstance(options);
66 if (options != null && options.length > 0) {
67 rawOption = options[0];
68 }
69 if (this.options.getSuffix() != null) {
70 final PatternParser parser = PatternLayout.createPatternParser(config);
71 final List<PatternFormatter> parsedSuffixFormatters = parser.parse(this.options.getSuffix());
72
73 boolean hasThrowableSuffixFormatter = false;
74 for (final PatternFormatter suffixFormatter : parsedSuffixFormatters) {
75 if (suffixFormatter.handlesThrowable()) {
76 hasThrowableSuffixFormatter = true;
77 }
78 }
79 if (!hasThrowableSuffixFormatter) {
80 this.formatters = parsedSuffixFormatters;
81 } else {
82 final List<PatternFormatter> suffixFormatters = new ArrayList<>();
83 for (final PatternFormatter suffixFormatter : parsedSuffixFormatters) {
84 if (!suffixFormatter.handlesThrowable()) {
85 suffixFormatters.add(suffixFormatter);
86 }
87 }
88 this.formatters = suffixFormatters;
89 }
90 } else {
91 this.formatters = Collections.emptyList();
92 }
93 subShortOption = ThrowableFormatOptions.MESSAGE.equalsIgnoreCase(rawOption) ||
94 ThrowableFormatOptions.LOCALIZED_MESSAGE.equalsIgnoreCase(rawOption) ||
95 ThrowableFormatOptions.FILE_NAME.equalsIgnoreCase(rawOption) ||
96 ThrowableFormatOptions.LINE_NUMBER.equalsIgnoreCase(rawOption) ||
97 ThrowableFormatOptions.METHOD_NAME.equalsIgnoreCase(rawOption) ||
98 ThrowableFormatOptions.CLASS_NAME.equalsIgnoreCase(rawOption);
99 nonStandardLineSeparator = !Strings.LINE_SEPARATOR.equals(this.options.getSeparator());
100 }
101
102
103
104
105
106
107
108
109
110 public static ThrowablePatternConverter newInstance(final Configuration config, final String[] options) {
111 return new ThrowablePatternConverter("Throwable", "throwable", options, config);
112 }
113
114
115
116
117 @Override
118 public void format(final LogEvent event, final StringBuilder buffer) {
119 final Throwable t = event.getThrown();
120
121 if (subShortOption) {
122 formatSubShortOption(t, getSuffix(event), buffer);
123 }
124 else if (t != null && options.anyLines()) {
125 formatOption(t, getSuffix(event), buffer);
126 }
127 }
128
129 private void formatSubShortOption(final Throwable t, final String suffix, final StringBuilder buffer) {
130 StackTraceElement[] trace;
131 StackTraceElement throwingMethod = null;
132 int len;
133
134 if (t != null) {
135 trace = t.getStackTrace();
136 if (trace !=null && trace.length > 0) {
137 throwingMethod = trace[0];
138 }
139 }
140
141 if (t != null && throwingMethod != null) {
142 String toAppend = Strings.EMPTY;
143
144 if (ThrowableFormatOptions.CLASS_NAME.equalsIgnoreCase(rawOption)) {
145 toAppend = throwingMethod.getClassName();
146 }
147 else if (ThrowableFormatOptions.METHOD_NAME.equalsIgnoreCase(rawOption)) {
148 toAppend = throwingMethod.getMethodName();
149 }
150 else if (ThrowableFormatOptions.LINE_NUMBER.equalsIgnoreCase(rawOption)) {
151 toAppend = String.valueOf(throwingMethod.getLineNumber());
152 }
153 else if (ThrowableFormatOptions.MESSAGE.equalsIgnoreCase(rawOption)) {
154 toAppend = t.getMessage();
155 }
156 else if (ThrowableFormatOptions.LOCALIZED_MESSAGE.equalsIgnoreCase(rawOption)) {
157 toAppend = t.getLocalizedMessage();
158 }
159 else if (ThrowableFormatOptions.FILE_NAME.equalsIgnoreCase(rawOption)) {
160 toAppend = throwingMethod.getFileName();
161 }
162
163 len = buffer.length();
164 if (len > 0 && !Character.isWhitespace(buffer.charAt(len - 1))) {
165 buffer.append(' ');
166 }
167 buffer.append(toAppend);
168
169 if (Strings.isNotBlank(suffix)) {
170 buffer.append(' ');
171 buffer.append(suffix);
172 }
173 }
174 }
175
176 private void formatOption(final Throwable throwable, final String suffix, final StringBuilder buffer) {
177 final int len = buffer.length();
178 if (len > 0 && !Character.isWhitespace(buffer.charAt(len - 1))) {
179 buffer.append(' ');
180 }
181 if (!options.allLines() || nonStandardLineSeparator || Strings.isNotBlank(suffix)) {
182 final StringWriter w = new StringWriter();
183 throwable.printStackTrace(new PrintWriter(w));
184
185 final String[] array = w.toString().split(Strings.LINE_SEPARATOR);
186 final int limit = options.minLines(array.length) - 1;
187 final boolean suffixNotBlank = Strings.isNotBlank(suffix);
188 for (int i = 0; i <= limit; ++i) {
189 buffer.append(array[i]);
190 if (suffixNotBlank) {
191 buffer.append(' ');
192 buffer.append(suffix);
193 }
194 if (i < limit) {
195 buffer.append(options.getSeparator());
196 }
197 }
198 } else {
199 throwable.printStackTrace(new PrintWriter(new StringBuilderWriter(buffer)));
200 }
201 }
202
203
204
205
206
207
208 @Override
209 public boolean handlesThrowable() {
210 return true;
211 }
212
213 protected String getSuffix(final LogEvent event) {
214 if (formatters.isEmpty()) {
215 return Strings.EMPTY;
216 }
217
218 final StringBuilder toAppendTo = new StringBuilder();
219 for (int i = 0, size = formatters.size(); i < size; i++) {
220 formatters.get(i).format(event, toAppendTo);
221 }
222 return toAppendTo.toString();
223 }
224
225 public ThrowableFormatOptions getOptions() {
226 return options;
227 }
228 }