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