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