1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.core.layout;
18
19 import java.nio.charset.Charset;
20 import java.util.HashMap;
21 import java.util.List;
22 import java.util.Map;
23
24 import org.apache.logging.log4j.core.Layout;
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.DefaultConfiguration;
28 import org.apache.logging.log4j.core.config.Node;
29 import org.apache.logging.log4j.core.config.plugins.Plugin;
30 import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
31 import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
32 import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
33 import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
34 import org.apache.logging.log4j.core.config.plugins.PluginElement;
35 import org.apache.logging.log4j.core.config.plugins.PluginFactory;
36 import org.apache.logging.log4j.core.pattern.LogEventPatternConverter;
37 import org.apache.logging.log4j.core.pattern.PatternFormatter;
38 import org.apache.logging.log4j.core.pattern.PatternParser;
39 import org.apache.logging.log4j.core.pattern.RegexReplacement;
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55 @Plugin(name = "PatternLayout", category = Node.CATEGORY, elementType = Layout.ELEMENT_TYPE, printObject = true)
56 public final class PatternLayout extends AbstractStringLayout {
57
58
59
60
61
62
63 public static final String DEFAULT_CONVERSION_PATTERN = "%m%n";
64
65
66
67
68
69 public static final String TTCC_CONVERSION_PATTERN =
70 "%r [%t] %p %c %x - %m%n";
71
72
73
74
75
76 public static final String SIMPLE_CONVERSION_PATTERN =
77 "%d [%t] %p %c - %m%n";
78
79
80 public static final String KEY = "Converter";
81
82 private static final long serialVersionUID = 1L;
83
84
85
86
87 private final PatternFormatter[] formatters;
88
89
90
91
92 private final String conversionPattern;
93
94 private final PatternSelector patternSelector;
95
96 private final Serializer serializer;
97
98
99
100
101
102 private final Configuration config;
103
104 private final RegexReplacement replace;
105
106 private final boolean alwaysWriteExceptions;
107
108 private final boolean noConsoleNoAnsi;
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124 private PatternLayout(final Configuration config, final RegexReplacement replace, final String pattern,
125 final PatternSelector patternSelector, final Charset charset,
126 final boolean alwaysWriteExceptions, final boolean noConsoleNoAnsi,
127 final String header, final String footer) {
128 super(charset, toBytes(header, charset), toBytes(footer, charset));
129 this.replace = replace;
130 this.conversionPattern = pattern;
131 this.patternSelector = patternSelector;
132 this.config = config;
133 this.alwaysWriteExceptions = alwaysWriteExceptions;
134 this.noConsoleNoAnsi = noConsoleNoAnsi;
135 if (patternSelector == null) {
136 serializer = new PatternSerializer();
137 final PatternParser parser = createPatternParser(config);
138 try {
139 List<PatternFormatter> list = parser.parse(pattern == null ? DEFAULT_CONVERSION_PATTERN : pattern,
140 this.alwaysWriteExceptions, this.noConsoleNoAnsi);
141 this.formatters = list.toArray(new PatternFormatter[0]);
142 } catch (RuntimeException ex) {
143 throw new IllegalArgumentException("Cannot parse pattern '" + pattern + "'", ex);
144 }
145 } else {
146 this.formatters = null;
147 serializer = new PatternSelectorSerializer();
148 }
149 }
150
151 private byte[] strSubstitutorReplace(final byte... b) {
152 if (b != null && config != null) {
153 return getBytes(config.getStrSubstitutor().replace(new String(b, getCharset())));
154 }
155 return b;
156 }
157
158 @Override
159 public byte[] getHeader() {
160 return strSubstitutorReplace(super.getHeader());
161 }
162
163 @Override
164 public byte[] getFooter() {
165 return strSubstitutorReplace(super.getFooter());
166 }
167
168
169
170
171
172
173 public String getConversionPattern() {
174 return conversionPattern;
175 }
176
177
178
179
180
181
182
183
184
185
186
187 @Override
188 public Map<String, String> getContentFormat()
189 {
190 final Map<String, String> result = new HashMap<>();
191 result.put("structured", "false");
192 result.put("formatType", "conversion");
193 result.put("format", conversionPattern);
194 return result;
195 }
196
197
198
199
200
201
202
203 @Override
204 public String toSerializable(final LogEvent event) {
205 return serializer.toSerializable(event);
206 }
207
208
209
210
211
212
213 public static PatternParser createPatternParser(final Configuration config) {
214 if (config == null) {
215 return new PatternParser(config, KEY, LogEventPatternConverter.class);
216 }
217 PatternParser parser = config.getComponent(KEY);
218 if (parser == null) {
219 parser = new PatternParser(config, KEY, LogEventPatternConverter.class);
220 config.addComponent(KEY, parser);
221 parser = (PatternParser) config.getComponent(KEY);
222 }
223 return parser;
224 }
225
226 @Override
227 public String toString() {
228 return patternSelector == null ? conversionPattern : patternSelector.toString();
229 }
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252 @PluginFactory
253 public static PatternLayout createLayout(
254 @PluginAttribute(value = "pattern", defaultString = DEFAULT_CONVERSION_PATTERN) final String pattern,
255 @PluginElement("PatternSelector") final PatternSelector patternSelector,
256 @PluginConfiguration final Configuration config,
257 @PluginElement("Replace") final RegexReplacement replace,
258
259 @PluginAttribute(value = "charset") final Charset charset,
260 @PluginAttribute(value = "alwaysWriteExceptions", defaultBoolean = true) final boolean alwaysWriteExceptions,
261 @PluginAttribute(value = "noConsoleNoAnsi", defaultBoolean = false) final boolean noConsoleNoAnsi,
262 @PluginAttribute("header") final String header,
263 @PluginAttribute("footer") final String footer) {
264 return newBuilder()
265 .withPattern(pattern)
266 .withPatternSelector(patternSelector)
267 .withConfiguration(config)
268 .withRegexReplacement(replace)
269 .withCharset(charset)
270 .withAlwaysWriteExceptions(alwaysWriteExceptions)
271 .withNoConsoleNoAnsi(noConsoleNoAnsi)
272 .withHeader(header)
273 .withFooter(footer)
274 .build();
275 }
276
277
278 private interface Serializer {
279
280 String toSerializable(final LogEvent event);
281 }
282
283 private class PatternSerializer implements Serializer {
284 @Override
285 public String toSerializable(final LogEvent event) {
286 final StringBuilder buf = getStringBuilder();
287 final int len = formatters.length;
288 for (int i = 0; i < len; i++) {
289 formatters[i].format(event, buf);
290 }
291 String str = buf.toString();
292 if (replace != null) {
293 str = replace.format(str);
294 }
295 return str;
296 }
297 }
298
299 private class PatternSelectorSerializer implements Serializer {
300 @Override
301 public String toSerializable(final LogEvent event) {
302 final StringBuilder buf = getStringBuilder();
303 PatternFormatter[] formatters = patternSelector.getFormatters(event);
304 final int len = formatters.length;
305 for (int i = 0; i < len; i++) {
306 formatters[i].format(event, buf);
307 }
308 String str = buf.toString();
309 if (replace != null) {
310 str = replace.format(str);
311 }
312 return str;
313 }
314 }
315
316
317
318
319
320
321
322
323 public static PatternLayout createDefaultLayout() {
324 return newBuilder().build();
325 }
326
327
328
329
330
331 @PluginBuilderFactory
332 public static Builder newBuilder() {
333 return new Builder();
334 }
335
336
337
338
339 public static class Builder implements org.apache.logging.log4j.core.util.Builder<PatternLayout> {
340
341
342
343
344 @PluginBuilderAttribute
345 private String pattern = PatternLayout.DEFAULT_CONVERSION_PATTERN;
346
347 @PluginElement("PatternSelector")
348 private PatternSelector patternSelector = null;
349
350 @PluginConfiguration
351 private Configuration configuration = null;
352
353 @PluginElement("Replace")
354 private RegexReplacement regexReplacement = null;
355
356
357 @PluginBuilderAttribute
358 private Charset charset = Charset.defaultCharset();
359
360 @PluginBuilderAttribute
361 private boolean alwaysWriteExceptions = true;
362
363 @PluginBuilderAttribute
364 private boolean noConsoleNoAnsi = false;
365
366 @PluginBuilderAttribute
367 private String header = null;
368
369 @PluginBuilderAttribute
370 private String footer = null;
371
372 private Builder() {
373 }
374
375
376
377 public Builder withPattern(final String pattern) {
378 this.pattern = pattern;
379 return this;
380 }
381
382 public Builder withPatternSelector(final PatternSelector patternSelector) {
383 this.patternSelector = patternSelector;
384 return this;
385 }
386
387
388 public Builder withConfiguration(final Configuration configuration) {
389 this.configuration = configuration;
390 return this;
391 }
392
393 public Builder withRegexReplacement(final RegexReplacement regexReplacement) {
394 this.regexReplacement = regexReplacement;
395 return this;
396 }
397
398 public Builder withCharset(final Charset charset) {
399
400 if (charset != null) {
401 this.charset = charset;
402 }
403 return this;
404 }
405
406 public Builder withAlwaysWriteExceptions(final boolean alwaysWriteExceptions) {
407 this.alwaysWriteExceptions = alwaysWriteExceptions;
408 return this;
409 }
410
411 public Builder withNoConsoleNoAnsi(final boolean noConsoleNoAnsi) {
412 this.noConsoleNoAnsi = noConsoleNoAnsi;
413 return this;
414 }
415
416 public Builder withHeader(final String header) {
417 this.header = header;
418 return this;
419 }
420
421 public Builder withFooter(final String footer) {
422 this.footer = footer;
423 return this;
424 }
425
426 @Override
427 public PatternLayout build() {
428
429 if (configuration == null) {
430 configuration = new DefaultConfiguration();
431 }
432 return new PatternLayout(configuration, regexReplacement, pattern, patternSelector, charset,
433 alwaysWriteExceptions, noConsoleNoAnsi, header, footer);
434 }
435 }
436 }