42
42
import org .apache .logging .log4j .core .lookup .StrSubstitutor ;
43
43
import org .apache .logging .log4j .core .util .KeyValuePair ;
44
44
import org .apache .logging .log4j .core .util .StringBuilderWriter ;
45
- import org .apache .logging .log4j .message .MapMessage ;
46
45
import org .apache .logging .log4j .message .Message ;
46
+ import org .apache .logging .log4j .message .MultiformatMessage ;
47
+ import org .apache .logging .log4j .util .MultiFormatStringBuilderFormattable ;
47
48
import org .apache .logging .log4j .util .StringBuilderFormattable ;
48
49
import org .apache .logging .log4j .util .TriConsumer ;
49
50
55
56
import java .util .HashSet ;
56
57
import java .util .List ;
57
58
import java .util .Set ;
59
+ import java .util .concurrent .ConcurrentHashMap ;
60
+ import java .util .concurrent .ConcurrentMap ;
58
61
59
62
@ Plugin (name = "EcsLayout" , category = Node .CATEGORY , elementType = Layout .ELEMENT_TYPE )
60
63
public class EcsLayout extends AbstractStringLayout {
61
64
62
65
private static final ThreadLocal <StringBuilder > messageStringBuilder = new ThreadLocal <StringBuilder >();
63
66
public static final Charset UTF_8 = Charset .forName ("UTF-8" );
67
+ public static final String [] JSON_FORMAT = {"JSON" };
64
68
65
69
private final TriConsumer <String , Object , StringBuilder > WRITE_KEY_VALUES_INTO = new TriConsumer <String , Object , StringBuilder >() {
66
70
@ Override
@@ -80,6 +84,7 @@ public void accept(final String key, final Object value, final StringBuilder str
80
84
private final Set <String > topLevelLabels ;
81
85
private String serviceName ;
82
86
private boolean includeMarkers ;
87
+ private final ConcurrentMap <Class <? extends MultiformatMessage >, Boolean > supportsJson = new ConcurrentHashMap <Class <? extends MultiformatMessage >, Boolean >();
83
88
84
89
private EcsLayout (Configuration config , String serviceName , boolean includeMarkers , KeyValuePair [] additionalFields , Collection <String > topLevelLabels ) {
85
90
super (config , UTF_8 , null , null );
@@ -201,6 +206,37 @@ private void serializeMarker(StringBuilder builder, Marker marker) {
201
206
}
202
207
203
208
private void serializeMessage (StringBuilder builder , boolean gcFree , Message message , Throwable thrown ) {
209
+ if (message instanceof MultiformatMessage ) {
210
+ MultiformatMessage multiformatMessage = (MultiformatMessage ) message ;
211
+ if (supportsJson (multiformatMessage )) {
212
+ serializeJsonMessage (builder , multiformatMessage );
213
+ } else {
214
+ serializeSimpleMessage (builder , gcFree , message , thrown );
215
+ }
216
+ } else {
217
+ serializeSimpleMessage (builder , gcFree , message , thrown );
218
+ }
219
+ }
220
+
221
+ private void serializeJsonMessage (StringBuilder builder , MultiformatMessage message ) {
222
+ final StringBuilder messageBuffer = getMessageStringBuilder ();
223
+ if (message instanceof MultiFormatStringBuilderFormattable ) {
224
+ ((MultiFormatStringBuilderFormattable ) message ).formatTo (JSON_FORMAT , messageBuffer );
225
+ } else {
226
+ messageBuffer .append (message .getFormattedMessage (JSON_FORMAT ));
227
+ }
228
+ if (isObject (messageBuffer )) {
229
+ moveToRoot (messageBuffer );
230
+ builder .append (messageBuffer );
231
+ builder .append (", " );
232
+ } else {
233
+ builder .append ("\" message\" :" );
234
+ builder .append (messageBuffer );
235
+ builder .append (", " );
236
+ }
237
+ }
238
+
239
+ private void serializeSimpleMessage (StringBuilder builder , boolean gcFree , Message message , Throwable thrown ) {
204
240
builder .append ("\" message\" :\" " );
205
241
if (message instanceof CharSequence ) {
206
242
JsonUtils .quoteAsString (((CharSequence ) message ), builder );
@@ -220,10 +256,30 @@ private void serializeMessage(StringBuilder builder, boolean gcFree, Message mes
220
256
JsonUtils .quoteAsString (formatThrowable (thrown ), builder );
221
257
}
222
258
builder .append ("\" , " );
223
- if (message instanceof MapMessage ) {
224
- MapMessage mapMessage = (MapMessage ) message ;
225
- mapMessage .forEach (WRITE_KEY_VALUES_INTO , builder );
259
+ }
260
+
261
+ private boolean isObject (StringBuilder messageBuffer ) {
262
+ return messageBuffer .length () > 1 && messageBuffer .charAt (0 ) == '{' && messageBuffer .charAt (messageBuffer .length () -1 ) == '}' ;
263
+ }
264
+
265
+ private void moveToRoot (StringBuilder messageBuffer ) {
266
+ messageBuffer .setCharAt (0 , ' ' );
267
+ messageBuffer .setCharAt (messageBuffer .length () -1 , ' ' );
268
+ }
269
+
270
+ private boolean supportsJson (MultiformatMessage message ) {
271
+ Boolean supportsJson = this .supportsJson .get (message .getClass ());
272
+ if (supportsJson == null ) {
273
+ supportsJson = false ;
274
+ for (String format : message .getFormats ()) {
275
+ if (format .equalsIgnoreCase ("JSON" )) {
276
+ supportsJson = true ;
277
+ break ;
278
+ }
279
+ }
280
+ this .supportsJson .put (message .getClass (), supportsJson );
226
281
}
282
+ return supportsJson ;
227
283
}
228
284
229
285
private static CharSequence formatThrowable (final Throwable throwable ) {
0 commit comments