28
28
import co .elastic .logging .EcsJsonSerializer ;
29
29
import co .elastic .logging .JsonUtils ;
30
30
import org .apache .logging .log4j .Marker ;
31
+ import org .apache .logging .log4j .ThreadContext ;
31
32
import org .apache .logging .log4j .core .Layout ;
32
33
import org .apache .logging .log4j .core .LogEvent ;
33
34
import org .apache .logging .log4j .core .config .Configuration ;
34
35
import org .apache .logging .log4j .core .config .Node ;
35
36
import org .apache .logging .log4j .core .config .plugins .Plugin ;
36
37
import org .apache .logging .log4j .core .config .plugins .PluginBuilderAttribute ;
37
38
import org .apache .logging .log4j .core .config .plugins .PluginBuilderFactory ;
39
+ import org .apache .logging .log4j .core .config .plugins .PluginConfiguration ;
38
40
import org .apache .logging .log4j .core .config .plugins .PluginElement ;
39
41
import org .apache .logging .log4j .core .layout .AbstractStringLayout ;
40
42
import org .apache .logging .log4j .core .layout .ByteBufferDestination ;
46
48
import org .apache .logging .log4j .message .Message ;
47
49
import org .apache .logging .log4j .message .MultiformatMessage ;
48
50
import org .apache .logging .log4j .message .ObjectMessage ;
49
- import org .apache .logging .log4j .util .MultiFormatStringBuilderFormattable ;
50
51
import org .apache .logging .log4j .util .StringBuilderFormattable ;
51
- import org .apache .logging .log4j .util .TriConsumer ;
52
52
53
53
import java .nio .charset .Charset ;
54
+ import java .util .Collections ;
54
55
import java .util .List ;
55
56
import java .util .concurrent .ConcurrentHashMap ;
56
57
import java .util .concurrent .ConcurrentMap ;
59
60
public class EcsLayout extends AbstractStringLayout {
60
61
61
62
public static final Charset UTF_8 = Charset .forName ("UTF-8" );
62
- static final String [] JSON_FORMAT = {"JSON" };
63
-
64
- private final TriConsumer <String , Object , StringBuilder > WRITE_MDC = new TriConsumer <String , Object , StringBuilder >() {
65
- @ Override
66
- public void accept (final String key , final Object value , final StringBuilder stringBuilder ) {
67
- stringBuilder .append ('\"' );
68
- JsonUtils .quoteAsString (key , stringBuilder );
69
- stringBuilder .append ("\" :\" " );
70
- JsonUtils .quoteAsString (EcsJsonSerializer .toNullSafeString (String .valueOf (value )), stringBuilder );
71
- stringBuilder .append ("\" ," );
72
- }
73
- };
63
+ private static final ObjectMessageJacksonSerializer JACKSON_SERIALIZER = ObjectMessageJacksonSerializer .Resolver .resolve ();
64
+ private static final MdcSerializer MDC_SERIALIZER = MdcSerializer .Resolver .resolve ();
65
+ private static final MultiFormatHandler MULTI_FORMAT_HANDLER = MultiFormatHandler .Resolver .resolve ();
74
66
75
67
private final KeyValuePair [] additionalFields ;
76
68
private final PatternFormatter [][] fieldValuePatternFormatter ;
@@ -80,7 +72,6 @@ public void accept(final String key, final Object value, final StringBuilder str
80
72
private final boolean includeMarkers ;
81
73
private final boolean includeOrigin ;
82
74
private final ConcurrentMap <Class <? extends MultiformatMessage >, Boolean > supportsJson = new ConcurrentHashMap <Class <? extends MultiformatMessage >, Boolean >();
83
- private final ObjectMessageJacksonSerializer objectMessageJacksonSerializer = ObjectMessageJacksonSerializer .Resolver .INSTANCE .resolve ();
84
75
85
76
private EcsLayout (Configuration config , String serviceName , String eventDataset , boolean includeMarkers , KeyValuePair [] additionalFields , boolean includeOrigin , boolean stackTraceAsArray ) {
86
77
super (config , UTF_8 , null , null );
@@ -103,7 +94,7 @@ private EcsLayout(Configuration config, String serviceName, String eventDataset,
103
94
104
95
@ PluginBuilderFactory
105
96
public static EcsLayout .Builder newBuilder () {
106
- return new EcsLayout .Builder (). asBuilder () ;
97
+ return new EcsLayout .Builder ();
107
98
}
108
99
109
100
private static boolean valueNeedsLookup (final String value ) {
@@ -148,40 +139,38 @@ private StringBuilder toText(LogEvent event, StringBuilder builder, boolean gcFr
148
139
149
140
private void serializeAdditionalFieldsAndMDC (LogEvent event , StringBuilder builder ) {
150
141
final int length = additionalFields .length ;
151
- if (!event .getContextData ().isEmpty () || length > 0 ) {
152
- if (length > 0 ) {
153
- final StrSubstitutor strSubstitutor = getConfiguration ().getStrSubstitutor ();
154
- for (int i = 0 ; i < length ; i ++) {
155
- KeyValuePair additionalField = additionalFields [i ];
156
- PatternFormatter [] formatters = fieldValuePatternFormatter [i ];
157
- CharSequence value = null ;
158
- if (formatters != null ) {
159
- StringBuilder buffer = EcsJsonSerializer .getMessageStringBuilder ();
160
- formatPattern (event , formatters , buffer );
161
- if (buffer .length () > 0 ) {
162
- value = buffer ;
163
- }
164
- } else if (valueNeedsLookup (additionalField .getValue ())) {
165
- StringBuilder lookupValue = EcsJsonSerializer .getMessageStringBuilder ();
166
- lookupValue .append (additionalField .getValue ());
167
- if (strSubstitutor .replaceIn (event , lookupValue )) {
168
- value = lookupValue ;
169
- }
170
- } else {
171
- value = additionalField .getValue ();
142
+ if (length > 0 ) {
143
+ final StrSubstitutor strSubstitutor = getConfiguration ().getStrSubstitutor ();
144
+ for (int i = 0 ; i < length ; i ++) {
145
+ KeyValuePair additionalField = additionalFields [i ];
146
+ PatternFormatter [] formatters = fieldValuePatternFormatter [i ];
147
+ CharSequence value = null ;
148
+ if (formatters != null ) {
149
+ StringBuilder buffer = EcsJsonSerializer .getMessageStringBuilder ();
150
+ formatPattern (event , formatters , buffer );
151
+ if (buffer .length () > 0 ) {
152
+ value = buffer ;
172
153
}
173
-
174
- if (value != null ) {
175
- builder .append ('\"' );
176
- JsonUtils .quoteAsString (additionalField .getKey (), builder );
177
- builder .append ("\" :\" " );
178
- JsonUtils .quoteAsString (EcsJsonSerializer .toNullSafeString (value ), builder );
179
- builder .append ("\" ," );
154
+ } else if (valueNeedsLookup (additionalField .getValue ())) {
155
+ StringBuilder lookupValue = EcsJsonSerializer .getMessageStringBuilder ();
156
+ lookupValue .append (additionalField .getValue ());
157
+ if (strSubstitutor .replaceIn (event , lookupValue )) {
158
+ value = lookupValue ;
180
159
}
160
+ } else {
161
+ value = additionalField .getValue ();
162
+ }
163
+
164
+ if (value != null ) {
165
+ builder .append ('\"' );
166
+ JsonUtils .quoteAsString (additionalField .getKey (), builder );
167
+ builder .append ("\" :\" " );
168
+ JsonUtils .quoteAsString (EcsJsonSerializer .toNullSafeString (value ), builder );
169
+ builder .append ("\" ," );
181
170
}
182
171
}
183
- event .getContextData ().forEach (WRITE_MDC , builder );
184
172
}
173
+ MDC_SERIALIZER .serializeMdc (event , builder );
185
174
}
186
175
187
176
private static void formatPattern (LogEvent event , PatternFormatter [] formatters , StringBuilder buffer ) {
@@ -192,7 +181,13 @@ private static void formatPattern(LogEvent event, PatternFormatter[] formatters,
192
181
}
193
182
194
183
private void serializeTags (LogEvent event , StringBuilder builder ) {
195
- List <String > contextStack = event .getContextStack ().asList ();
184
+ ThreadContext .ContextStack stack = event .getContextStack ();
185
+ List <String > contextStack ;
186
+ if (stack == null ) {
187
+ contextStack = Collections .emptyList ();
188
+ } else {
189
+ contextStack = stack .asList ();
190
+ }
196
191
Marker marker = event .getMarker ();
197
192
boolean hasTags = !contextStack .isEmpty () || (includeMarkers && marker != null );
198
193
if (hasTags ) {
@@ -235,9 +230,9 @@ private void serializeMessage(StringBuilder builder, boolean gcFree, Message mes
235
230
} else {
236
231
serializeSimpleMessage (builder , gcFree , message , thrown );
237
232
}
238
- } else if (objectMessageJacksonSerializer != null && message instanceof ObjectMessage ) {
233
+ } else if (JACKSON_SERIALIZER != null && message instanceof ObjectMessage ) {
239
234
final StringBuilder jsonBuffer = EcsJsonSerializer .getMessageStringBuilder ();
240
- objectMessageJacksonSerializer .formatTo (jsonBuffer , (ObjectMessage ) message );
235
+ JACKSON_SERIALIZER .formatTo (jsonBuffer , (ObjectMessage ) message );
241
236
addJson (builder , jsonBuffer );
242
237
} else {
243
238
serializeSimpleMessage (builder , gcFree , message , thrown );
@@ -246,11 +241,7 @@ private void serializeMessage(StringBuilder builder, boolean gcFree, Message mes
246
241
247
242
private static void serializeJsonMessage (StringBuilder builder , MultiformatMessage message ) {
248
243
final StringBuilder messageBuffer = EcsJsonSerializer .getMessageStringBuilder ();
249
- if (message instanceof MultiFormatStringBuilderFormattable ) {
250
- ((MultiFormatStringBuilderFormattable ) message ).formatTo (JSON_FORMAT , messageBuffer );
251
- } else {
252
- messageBuffer .append (message .getFormattedMessage (JSON_FORMAT ));
253
- }
244
+ MULTI_FORMAT_HANDLER .formatJsonTo (message , messageBuffer );
254
245
addJson (builder , messageBuffer );
255
246
}
256
247
@@ -283,20 +274,27 @@ private void serializeSimpleMessage(StringBuilder builder, boolean gcFree, Messa
283
274
((StringBuilderFormattable ) message ).formatTo (messageBuffer );
284
275
JsonUtils .quoteAsString (messageBuffer , builder );
285
276
} finally {
286
- trimToMaxSize (messageBuffer );
277
+ trimToMaxSizeCopy (messageBuffer );
287
278
}
288
279
} else {
289
280
JsonUtils .quoteAsString (EcsJsonSerializer .toNullSafeString (message .getFormattedMessage ()), builder );
290
281
}
291
282
builder .append ("\" , " );
292
283
}
293
284
285
+ static void trimToMaxSizeCopy (final StringBuilder stringBuilder ) {
286
+ if (stringBuilder .length () > MAX_STRING_BUILDER_SIZE ) {
287
+ stringBuilder .setLength (MAX_STRING_BUILDER_SIZE );
288
+ stringBuilder .trimToSize ();
289
+ }
290
+ }
291
+
294
292
private static boolean isObject (StringBuilder messageBuffer ) {
295
- return messageBuffer .length () > 1 && messageBuffer .charAt (0 ) == '{' && messageBuffer .charAt (messageBuffer .length () -1 ) == '}' ;
293
+ return messageBuffer .length () > 1 && messageBuffer .charAt (0 ) == '{' && messageBuffer .charAt (messageBuffer .length () - 1 ) == '}' ;
296
294
}
297
295
298
296
private static boolean isString (StringBuilder messageBuffer ) {
299
- return messageBuffer .length () > 1 && messageBuffer .charAt (0 ) == '"' && messageBuffer .charAt (messageBuffer .length () -1 ) == '"' ;
297
+ return messageBuffer .length () > 1 && messageBuffer .charAt (0 ) == '"' && messageBuffer .charAt (messageBuffer .length () - 1 ) == '"' ;
300
298
}
301
299
302
300
private static void moveToRoot (StringBuilder messageBuffer ) {
@@ -319,9 +317,10 @@ private boolean supportsJson(MultiformatMessage message) {
319
317
return supportsJson ;
320
318
}
321
319
322
- public static class Builder extends AbstractStringLayout .Builder <EcsLayout .Builder >
323
- implements org .apache .logging .log4j .core .util .Builder <EcsLayout > {
320
+ public static class Builder implements org .apache .logging .log4j .core .util .Builder <EcsLayout > {
324
321
322
+ @ PluginConfiguration
323
+ private Configuration configuration ;
325
324
@ PluginBuilderAttribute ("serviceName" )
326
325
private String serviceName ;
327
326
@ PluginBuilderAttribute ("eventDataset" )
@@ -336,8 +335,15 @@ public static class Builder extends AbstractStringLayout.Builder<EcsLayout.Build
336
335
private boolean includeOrigin = false ;
337
336
338
337
Builder () {
339
- super ();
340
- setCharset (UTF_8 );
338
+ }
339
+
340
+ public Configuration getConfiguration () {
341
+ return configuration ;
342
+ }
343
+
344
+ public EcsLayout .Builder setConfiguration (final Configuration configuration ) {
345
+ this .configuration = configuration ;
346
+ return this ;
341
347
}
342
348
343
349
public KeyValuePair [] getAdditionalFields () {
@@ -367,32 +373,32 @@ public boolean isIncludeOrigin() {
367
373
*/
368
374
public EcsLayout .Builder setAdditionalFields (final KeyValuePair [] additionalFields ) {
369
375
this .additionalFields = additionalFields .clone ();
370
- return asBuilder () ;
376
+ return this ;
371
377
}
372
378
373
379
public EcsLayout .Builder setServiceName (final String serviceName ) {
374
380
this .serviceName = serviceName ;
375
- return asBuilder () ;
381
+ return this ;
376
382
}
377
383
378
384
public EcsLayout .Builder setEventDataset (String eventDataset ) {
379
385
this .eventDataset = eventDataset ;
380
- return asBuilder () ;
386
+ return this ;
381
387
}
382
388
383
389
public EcsLayout .Builder setIncludeMarkers (final boolean includeMarkers ) {
384
390
this .includeMarkers = includeMarkers ;
385
- return asBuilder () ;
391
+ return this ;
386
392
}
387
393
388
394
public EcsLayout .Builder setIncludeOrigin (final boolean includeOrigin ) {
389
395
this .includeOrigin = includeOrigin ;
390
- return asBuilder () ;
396
+ return this ;
391
397
}
392
398
393
399
public EcsLayout .Builder setStackTraceAsArray (boolean stackTraceAsArray ) {
394
400
this .stackTraceAsArray = stackTraceAsArray ;
395
- return asBuilder () ;
401
+ return this ;
396
402
}
397
403
398
404
@ Override
0 commit comments