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