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 private static final long serialVersionUID = 1L;
59
60
61
62
63
64
65 public static final String DEFAULT_CONVERSION_PATTERN = "%m%n";
66
67
68
69
70
71 public static final String TTCC_CONVERSION_PATTERN =
72 "%r [%t] %p %c %x - %m%n";
73
74
75
76
77
78 public static final String SIMPLE_CONVERSION_PATTERN =
79 "%d [%t] %p %c - %m%n";
80
81
82 public static final String KEY = "Converter";
83
84
85
86
87 private final List<PatternFormatter> formatters;
88
89
90
91
92 private final String conversionPattern;
93
94
95
96
97
98 private final Configuration config;
99
100 private final RegexReplacement replace;
101
102 private final boolean alwaysWriteExceptions;
103
104 private final boolean noConsoleNoAnsi;
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119 private PatternLayout(final Configuration config, final RegexReplacement replace, final String pattern,
120 final Charset charset, final boolean alwaysWriteExceptions, final boolean noConsoleNoAnsi,
121 final String header, final String footer) {
122 super(charset, toBytes(header, charset), toBytes(footer, charset));
123 this.replace = replace;
124 this.conversionPattern = pattern;
125 this.config = config;
126 this.alwaysWriteExceptions = alwaysWriteExceptions;
127 this.noConsoleNoAnsi = noConsoleNoAnsi;
128 final PatternParser parser = createPatternParser(config);
129 this.formatters = parser.parse(pattern == null ? DEFAULT_CONVERSION_PATTERN : pattern, this.alwaysWriteExceptions, this.noConsoleNoAnsi);
130 }
131
132 private static byte[] toBytes(final String str, final Charset charset) {
133 if (str != null) {
134 return str.getBytes(charset != null ? charset : Charset.defaultCharset());
135 }
136 return null;
137 }
138
139 private byte[] strSubstitutorReplace(final byte... b) {
140 if (b != null && config != null) {
141 final Charset cs = getCharset();
142 return config.getStrSubstitutor().replace(new String(b, cs)).getBytes(cs);
143 }
144 return b;
145 }
146
147 @Override
148 public byte[] getHeader() {
149 return strSubstitutorReplace(super.getHeader());
150 }
151
152 @Override
153 public byte[] getFooter() {
154 return strSubstitutorReplace(super.getFooter());
155 }
156
157
158
159
160
161
162 public String getConversionPattern() {
163 return conversionPattern;
164 }
165
166
167
168
169
170
171
172
173
174
175
176 @Override
177 public Map<String, String> getContentFormat()
178 {
179 final Map<String, String> result = new HashMap<String, String>();
180 result.put("structured", "false");
181 result.put("formatType", "conversion");
182 result.put("format", conversionPattern);
183 return result;
184 }
185
186
187
188
189
190
191
192
193 @Override
194 public String toSerializable(final LogEvent event) {
195 final StringBuilder buf = new StringBuilder();
196 for (final PatternFormatter formatter : formatters) {
197 formatter.format(event, buf);
198 }
199 String str = buf.toString();
200 if (replace != null) {
201 str = replace.format(str);
202 }
203 return str;
204 }
205
206
207
208
209
210
211 public static PatternParser createPatternParser(final Configuration config) {
212 if (config == null) {
213 return new PatternParser(config, KEY, LogEventPatternConverter.class);
214 }
215 PatternParser parser = config.getComponent(KEY);
216 if (parser == null) {
217 parser = new PatternParser(config, KEY, LogEventPatternConverter.class);
218 config.addComponent(KEY, parser);
219 parser = (PatternParser) config.getComponent(KEY);
220 }
221 return parser;
222 }
223
224 @Override
225 public String toString() {
226 return conversionPattern;
227 }
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250 @PluginFactory
251 public static PatternLayout createLayout(
252 @PluginAttribute(value = "pattern", defaultString = DEFAULT_CONVERSION_PATTERN) final String pattern,
253 @PluginConfiguration final Configuration config,
254 @PluginElement("Replace") final RegexReplacement replace,
255 @PluginAttribute(value = "charset", defaultString = "UTF-8") final Charset charset,
256 @PluginAttribute(value = "alwaysWriteExceptions", defaultBoolean = true) final boolean alwaysWriteExceptions,
257 @PluginAttribute(value = "noConsoleNoAnsi", defaultBoolean = false) final boolean noConsoleNoAnsi,
258 @PluginAttribute("header") final String header,
259 @PluginAttribute("footer") final String footer) {
260 return newBuilder()
261 .withPattern(pattern)
262 .withConfiguration(config)
263 .withRegexReplacement(replace)
264 .withCharset(charset)
265 .withAlwaysWriteExceptions(alwaysWriteExceptions)
266 .withNoConsoleNoAnsi(noConsoleNoAnsi)
267 .withHeader(header)
268 .withFooter(footer)
269 .build();
270 }
271
272
273
274
275
276
277
278
279 public static PatternLayout createDefaultLayout() {
280 return newBuilder().build();
281 }
282
283
284
285
286
287 @PluginBuilderFactory
288 public static Builder newBuilder() {
289 return new Builder();
290 }
291
292
293
294
295 public static class Builder implements org.apache.logging.log4j.core.util.Builder<PatternLayout> {
296
297
298
299
300 @PluginBuilderAttribute
301 private String pattern = PatternLayout.DEFAULT_CONVERSION_PATTERN;
302
303 @PluginConfiguration
304 private Configuration configuration = null;
305
306 @PluginElement("Replace")
307 private RegexReplacement regexReplacement = null;
308
309
310 @PluginBuilderAttribute
311 private Charset charset = Charset.defaultCharset();
312
313 @PluginBuilderAttribute
314 private boolean alwaysWriteExceptions = true;
315
316 @PluginBuilderAttribute
317 private boolean noConsoleNoAnsi = false;
318
319 @PluginBuilderAttribute
320 private String header = null;
321
322 @PluginBuilderAttribute
323 private String footer = null;
324
325 private Builder() {
326 }
327
328
329
330 public Builder withPattern(final String pattern) {
331 this.pattern = pattern;
332 return this;
333 }
334
335
336 public Builder withConfiguration(final Configuration configuration) {
337 this.configuration = configuration;
338 return this;
339 }
340
341 public Builder withRegexReplacement(final RegexReplacement regexReplacement) {
342 this.regexReplacement = regexReplacement;
343 return this;
344 }
345
346 public Builder withCharset(final Charset charset) {
347 this.charset = charset;
348 return this;
349 }
350
351 public Builder withAlwaysWriteExceptions(final boolean alwaysWriteExceptions) {
352 this.alwaysWriteExceptions = alwaysWriteExceptions;
353 return this;
354 }
355
356 public Builder withNoConsoleNoAnsi(final boolean noConsoleNoAnsi) {
357 this.noConsoleNoAnsi = noConsoleNoAnsi;
358 return this;
359 }
360
361 public Builder withHeader(final String header) {
362 this.header = header;
363 return this;
364 }
365
366 public Builder withFooter(final String footer) {
367 this.footer = footer;
368 return this;
369 }
370
371 @Override
372 public PatternLayout build() {
373
374 if (configuration == null) {
375 configuration = new DefaultConfiguration();
376 }
377 return new PatternLayout(configuration, regexReplacement, pattern, charset, alwaysWriteExceptions,
378 noConsoleNoAnsi, header, footer);
379 }
380 }
381 }