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.io.IOException;
20 import java.io.Writer;
21 import java.nio.charset.Charset;
22 import java.util.LinkedHashMap;
23 import java.util.Map;
24
25 import org.apache.logging.log4j.Level;
26 import org.apache.logging.log4j.Marker;
27 import org.apache.logging.log4j.ThreadContext;
28 import org.apache.logging.log4j.core.LogEvent;
29 import org.apache.logging.log4j.core.config.Configuration;
30 import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
31 import org.apache.logging.log4j.core.config.plugins.PluginElement;
32 import org.apache.logging.log4j.core.impl.Log4jLogEvent;
33 import org.apache.logging.log4j.core.impl.ThrowableProxy;
34 import org.apache.logging.log4j.core.jackson.XmlConstants;
35 import org.apache.logging.log4j.core.lookup.StrSubstitutor;
36 import org.apache.logging.log4j.core.time.Instant;
37 import org.apache.logging.log4j.core.util.KeyValuePair;
38 import org.apache.logging.log4j.core.util.StringBuilderWriter;
39 import org.apache.logging.log4j.message.Message;
40 import org.apache.logging.log4j.util.ReadOnlyStringMap;
41 import org.apache.logging.log4j.util.Strings;
42
43 import com.fasterxml.jackson.annotation.JsonAnyGetter;
44 import com.fasterxml.jackson.annotation.JsonIgnore;
45 import com.fasterxml.jackson.annotation.JsonRootName;
46 import com.fasterxml.jackson.annotation.JsonUnwrapped;
47 import com.fasterxml.jackson.core.JsonGenerationException;
48 import com.fasterxml.jackson.databind.JsonMappingException;
49 import com.fasterxml.jackson.databind.ObjectWriter;
50 import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
51
52 abstract class AbstractJacksonLayout extends AbstractStringLayout {
53
54 protected static final String DEFAULT_EOL = "\r\n";
55 protected static final String COMPACT_EOL = Strings.EMPTY;
56
57 public static abstract class Builder<B extends Builder<B>> extends AbstractStringLayout.Builder<B> {
58
59 @PluginBuilderAttribute
60 private boolean eventEol;
61
62 @PluginBuilderAttribute
63 private String endOfLine;
64
65 @PluginBuilderAttribute
66 private boolean compact;
67
68 @PluginBuilderAttribute
69 private boolean complete;
70
71 @PluginBuilderAttribute
72 private boolean locationInfo;
73
74 @PluginBuilderAttribute
75 private boolean properties;
76
77 @PluginBuilderAttribute
78 private boolean includeStacktrace = true;
79
80 @PluginBuilderAttribute
81 private boolean stacktraceAsString = false;
82
83 @PluginBuilderAttribute
84 private boolean includeNullDelimiter = false;
85
86 @PluginBuilderAttribute
87 private boolean includeTimeMillis = false;
88
89 @PluginElement("AdditionalField")
90 private KeyValuePair[] additionalFields;
91
92 protected String toStringOrNull(final byte[] header) {
93 return header == null ? null : new String(header, Charset.defaultCharset());
94 }
95
96 public boolean getEventEol() {
97 return eventEol;
98 }
99
100 public String getEndOfLine() {
101 return endOfLine;
102 }
103
104 public boolean isCompact() {
105 return compact;
106 }
107
108 public boolean isComplete() {
109 return complete;
110 }
111
112 public boolean isLocationInfo() {
113 return locationInfo;
114 }
115
116 public boolean isProperties() {
117 return properties;
118 }
119
120
121
122
123
124 public boolean isIncludeStacktrace() {
125 return includeStacktrace;
126 }
127
128 public boolean isStacktraceAsString() {
129 return stacktraceAsString;
130 }
131
132 public boolean isIncludeNullDelimiter() { return includeNullDelimiter; }
133
134 public boolean isIncludeTimeMillis() {
135 return includeTimeMillis;
136 }
137
138 public KeyValuePair[] getAdditionalFields() {
139 return additionalFields;
140 }
141
142 public B setEventEol(final boolean eventEol) {
143 this.eventEol = eventEol;
144 return asBuilder();
145 }
146
147 public B setEndOfLine(final String endOfLine) {
148 this.endOfLine = endOfLine;
149 return asBuilder();
150 }
151
152 public B setCompact(final boolean compact) {
153 this.compact = compact;
154 return asBuilder();
155 }
156
157 public B setComplete(final boolean complete) {
158 this.complete = complete;
159 return asBuilder();
160 }
161
162 public B setLocationInfo(final boolean locationInfo) {
163 this.locationInfo = locationInfo;
164 return asBuilder();
165 }
166
167 public B setProperties(final boolean properties) {
168 this.properties = properties;
169 return asBuilder();
170 }
171
172
173
174
175
176
177 public B setIncludeStacktrace(final boolean includeStacktrace) {
178 this.includeStacktrace = includeStacktrace;
179 return asBuilder();
180 }
181
182
183
184
185
186
187 public B setStacktraceAsString(final boolean stacktraceAsString) {
188 this.stacktraceAsString = stacktraceAsString;
189 return asBuilder();
190 }
191
192
193
194
195
196
197 public B setIncludeNullDelimiter(final boolean includeNullDelimiter) {
198 this.includeNullDelimiter = includeNullDelimiter;
199 return asBuilder();
200 }
201
202
203
204
205
206
207 public B setIncludeTimeMillis(final boolean includeTimeMillis) {
208 this.includeTimeMillis = includeTimeMillis;
209 return asBuilder();
210 }
211
212
213
214
215
216
217 public B setAdditionalFields(final KeyValuePair[] additionalFields) {
218 this.additionalFields = additionalFields;
219 return asBuilder();
220 }
221 }
222
223 protected final String eol;
224 protected final ObjectWriter objectWriter;
225 protected final boolean compact;
226 protected final boolean complete;
227 protected final boolean includeNullDelimiter;
228 protected final ResolvableKeyValuePair[] additionalFields;
229
230 @Deprecated
231 protected AbstractJacksonLayout(final Configuration config, final ObjectWriter objectWriter, final Charset charset,
232 final boolean compact, final boolean complete, final boolean eventEol, final Serializer headerSerializer,
233 final Serializer footerSerializer) {
234 this(config, objectWriter, charset, compact, complete, eventEol, headerSerializer, footerSerializer, false);
235 }
236
237 @Deprecated
238 protected AbstractJacksonLayout(final Configuration config, final ObjectWriter objectWriter, final Charset charset,
239 final boolean compact, final boolean complete, final boolean eventEol, final Serializer headerSerializer,
240 final Serializer footerSerializer, final boolean includeNullDelimiter) {
241 this(config, objectWriter, charset, compact, complete, eventEol, null, headerSerializer, footerSerializer, includeNullDelimiter, null);
242 }
243
244 protected AbstractJacksonLayout(final Configuration config, final ObjectWriter objectWriter, final Charset charset,
245 final boolean compact, final boolean complete, final boolean eventEol, final String endOfLine, final Serializer headerSerializer,
246 final Serializer footerSerializer, final boolean includeNullDelimiter,
247 final KeyValuePair[] additionalFields) {
248 super(config, charset, headerSerializer, footerSerializer);
249 this.objectWriter = objectWriter;
250 this.compact = compact;
251 this.complete = complete;
252 this.eol = endOfLine != null ? endOfLine : compact && !eventEol ? COMPACT_EOL : DEFAULT_EOL;
253 this.includeNullDelimiter = includeNullDelimiter;
254 this.additionalFields = prepareAdditionalFields(config, additionalFields);
255 }
256
257 protected static boolean valueNeedsLookup(final String value) {
258 return value != null && value.contains("${");
259 }
260
261 private static ResolvableKeyValuePair[] prepareAdditionalFields(final Configuration config, final KeyValuePair[] additionalFields) {
262 if (additionalFields == null || additionalFields.length == 0) {
263
264 return new ResolvableKeyValuePair[0];
265 }
266
267
268 final ResolvableKeyValuePair[] resolvableFields = new ResolvableKeyValuePair[additionalFields.length];
269
270 for (int i = 0; i < additionalFields.length; i++) {
271 final ResolvableKeyValuePair resolvable = resolvableFields[i] = new ResolvableKeyValuePair(additionalFields[i]);
272
273
274 if (config == null && resolvable.valueNeedsLookup) {
275 throw new IllegalArgumentException("configuration needs to be set when there are additional fields with variables");
276 }
277 }
278
279 return resolvableFields;
280 }
281
282
283
284
285
286
287
288 @Override
289 public String toSerializable(final LogEvent event) {
290 final StringBuilderWriter writer = new StringBuilderWriter();
291 try {
292 toSerializable(event, writer);
293 return writer.toString();
294 } catch (final IOException e) {
295
296 LOGGER.error(e);
297 return Strings.EMPTY;
298 }
299 }
300
301 private static LogEvent convertMutableToLog4jEvent(final LogEvent event) {
302
303
304
305 return event instanceof Log4jLogEvent ? event : Log4jLogEvent.createMemento(event);
306 }
307
308 protected Object wrapLogEvent(final LogEvent event) {
309 if (additionalFields.length > 0) {
310
311 final Map<String, String> additionalFieldsMap = resolveAdditionalFields(event);
312
313 return new LogEventWithAdditionalFields(event, additionalFieldsMap);
314 } else if (event instanceof Message) {
315
316 return new ReadOnlyLogEventWrapper(event);
317 } else {
318
319 return event;
320 }
321 }
322
323 private Map<String, String> resolveAdditionalFields(final LogEvent logEvent) {
324
325 final Map<String, String> additionalFieldsMap = new LinkedHashMap<>(additionalFields.length);
326 final StrSubstitutor strSubstitutor = configuration.getStrSubstitutor();
327
328
329 for (final ResolvableKeyValuePair pair : additionalFields) {
330 if (pair.valueNeedsLookup) {
331
332 additionalFieldsMap.put(pair.key, strSubstitutor.replace(logEvent, pair.value));
333 } else {
334
335 additionalFieldsMap.put(pair.key, pair.value);
336 }
337 }
338
339 return additionalFieldsMap;
340 }
341
342 public void toSerializable(final LogEvent event, final Writer writer)
343 throws JsonGenerationException, JsonMappingException, IOException {
344 objectWriter.writeValue(writer, wrapLogEvent(convertMutableToLog4jEvent(event)));
345 writer.write(eol);
346 if (includeNullDelimiter) {
347 writer.write('\0');
348 }
349 markEvent();
350 }
351
352 @JsonRootName(XmlConstants.ELT_EVENT)
353 @JacksonXmlRootElement(namespace = XmlConstants.XML_NAMESPACE, localName = XmlConstants.ELT_EVENT)
354 public static class LogEventWithAdditionalFields {
355
356 private final Object logEvent;
357 private final Map<String, String> additionalFields;
358
359 public LogEventWithAdditionalFields(final Object logEvent, final Map<String, String> additionalFields) {
360 this.logEvent = logEvent;
361 this.additionalFields = additionalFields;
362 }
363
364 @JsonUnwrapped
365 public Object getLogEvent() {
366 return logEvent;
367 }
368
369 @JsonAnyGetter
370 @SuppressWarnings("unused")
371 public Map<String, String> getAdditionalFields() {
372 return additionalFields;
373 }
374 }
375
376 protected static class ResolvableKeyValuePair {
377
378 final String key;
379 final String value;
380 final boolean valueNeedsLookup;
381
382 ResolvableKeyValuePair(final KeyValuePair pair) {
383 this.key = pair.getKey();
384 this.value = pair.getValue();
385 this.valueNeedsLookup = AbstractJacksonLayout.valueNeedsLookup(this.value);
386 }
387 }
388
389 private static class ReadOnlyLogEventWrapper implements LogEvent {
390
391 @JsonIgnore
392 private final LogEvent event;
393
394 public ReadOnlyLogEventWrapper(LogEvent event) {
395 this.event = event;
396 }
397
398 @Override
399 public LogEvent toImmutable() {
400 return event.toImmutable();
401 }
402
403 @Override
404 public Map<String, String> getContextMap() {
405 return event.getContextMap();
406 }
407
408 @Override
409 public ReadOnlyStringMap getContextData() {
410 return event.getContextData();
411 }
412
413 @Override
414 public ThreadContext.ContextStack getContextStack() {
415 return event.getContextStack();
416 }
417
418 @Override
419 public String getLoggerFqcn() {
420 return event.getLoggerFqcn();
421 }
422
423 @Override
424 public Level getLevel() {
425 return event.getLevel();
426 }
427
428 @Override
429 public String getLoggerName() {
430 return event.getLoggerName();
431 }
432
433 @Override
434 public Marker getMarker() {
435 return event.getMarker();
436 }
437
438 @Override
439 public Message getMessage() {
440 return event.getMessage();
441 }
442
443 @Override
444 public long getTimeMillis() {
445 return event.getTimeMillis();
446 }
447
448 @Override
449 public Instant getInstant() {
450 return event.getInstant();
451 }
452
453 @Override
454 public StackTraceElement getSource() {
455 return event.getSource();
456 }
457
458 @Override
459 public String getThreadName() {
460 return event.getThreadName();
461 }
462
463 @Override
464 public long getThreadId() {
465 return event.getThreadId();
466 }
467
468 @Override
469 public int getThreadPriority() {
470 return event.getThreadPriority();
471 }
472
473 @Override
474 public Throwable getThrown() {
475 return event.getThrown();
476 }
477
478 @Override
479 public ThrowableProxy getThrownProxy() {
480 return event.getThrownProxy();
481 }
482
483 @Override
484 public boolean isEndOfBatch() {
485 return event.isEndOfBatch();
486 }
487
488 @Override
489 public boolean isIncludeLocation() {
490 return event.isIncludeLocation();
491 }
492
493 @Override
494 public void setEndOfBatch(boolean endOfBatch) {
495
496 }
497
498 @Override
499 public void setIncludeLocation(boolean locationRequired) {
500
501 }
502
503 @Override
504 public long getNanoTime() {
505 return event.getNanoTime();
506 }
507 }
508 }