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