23
23
import io .opentelemetry .semconv .ExceptionAttributes ;
24
24
import java .io .PrintWriter ;
25
25
import java .io .StringWriter ;
26
+ import java .lang .reflect .Field ;
26
27
import java .util .ArrayList ;
27
28
import java .util .Arrays ;
29
+ import java .util .Iterator ;
28
30
import java .util .List ;
29
31
import java .util .Map ;
30
32
import java .util .concurrent .TimeUnit ;
31
33
import java .util .stream .Collectors ;
34
+ import javax .annotation .Nullable ;
35
+ import net .logstash .logback .marker .LogstashMarker ;
36
+ import net .logstash .logback .marker .MapEntriesAppendingMarker ;
37
+ import net .logstash .logback .marker .SingleFieldAppendingMarker ;
32
38
import org .slf4j .Marker ;
33
39
import org .slf4j .event .KeyValuePair ;
34
40
@@ -50,6 +56,7 @@ public final class LoggingEventMapper {
50
56
private static final boolean supportsInstant = supportsInstant ();
51
57
private static final boolean supportsKeyValuePairs = supportsKeyValuePairs ();
52
58
private static final boolean supportsMultipleMarkers = supportsMultipleMarkers ();
59
+ private static final boolean supportsLogstashMarkers = supportsLogstashMarkers ();
53
60
private static final Cache <String , AttributeKey <String >> mdcAttributeKeys = Cache .bounded (100 );
54
61
private static final Cache <String , AttributeKey <String >> attributeKeys = Cache .bounded (100 );
55
62
@@ -60,6 +67,8 @@ public final class LoggingEventMapper {
60
67
private static final AttributeKey <List <String >> LOG_BODY_PARAMETERS =
61
68
AttributeKey .stringArrayKey ("log.body.parameters" );
62
69
70
+ private static final Cache <Class <?>, Field > valueField = Cache .bounded (20 );
71
+
63
72
private final boolean captureExperimentalAttributes ;
64
73
private final List <String > captureMdcAttributes ;
65
74
private final boolean captureAllMdcAttributes ;
@@ -68,6 +77,7 @@ public final class LoggingEventMapper {
68
77
private final boolean captureKeyValuePairAttributes ;
69
78
private final boolean captureLoggerContext ;
70
79
private final boolean captureArguments ;
80
+ private final boolean captureLogstashAttributes ;
71
81
72
82
private LoggingEventMapper (Builder builder ) {
73
83
this .captureExperimentalAttributes = builder .captureExperimentalAttributes ;
@@ -77,6 +87,7 @@ private LoggingEventMapper(Builder builder) {
77
87
this .captureKeyValuePairAttributes = builder .captureKeyValuePairAttributes ;
78
88
this .captureLoggerContext = builder .captureLoggerContext ;
79
89
this .captureArguments = builder .captureArguments ;
90
+ this .captureLogstashAttributes = builder .captureLogstashAttributes ;
80
91
this .captureAllMdcAttributes =
81
92
builder .captureMdcAttributes .size () == 1 && builder .captureMdcAttributes .get (0 ).equals ("*" );
82
93
}
@@ -170,7 +181,8 @@ private void mapLoggingEvent(
170
181
}
171
182
172
183
if (captureMarkerAttribute ) {
173
- captureMarkerAttribute (attributes , loggingEvent );
184
+ boolean skipLogstashMarkers = supportsLogstashMarkers && captureLogstashAttributes ;
185
+ captureMarkerAttribute (attributes , loggingEvent , skipLogstashMarkers );
174
186
}
175
187
176
188
if (supportsKeyValuePairs && captureKeyValuePairAttributes ) {
@@ -187,6 +199,10 @@ private void mapLoggingEvent(
187
199
captureArguments (attributes , loggingEvent .getMessage (), loggingEvent .getArgumentArray ());
188
200
}
189
201
202
+ if (supportsLogstashMarkers && captureLogstashAttributes ) {
203
+ captureLogstashAttributes (attributes , loggingEvent );
204
+ }
205
+
190
206
builder .setAllAttributes (attributes .build ());
191
207
192
208
// span context
@@ -326,31 +342,35 @@ private static boolean supportsKeyValuePairs() {
326
342
}
327
343
328
344
private static void captureMarkerAttribute (
329
- AttributesBuilder attributes , ILoggingEvent loggingEvent ) {
345
+ AttributesBuilder attributes , ILoggingEvent loggingEvent , boolean skipLogstashMarkers ) {
330
346
if (supportsMultipleMarkers && hasMultipleMarkers (loggingEvent )) {
331
- captureMultipleMarkerAttributes (attributes , loggingEvent );
347
+ captureMultipleMarkerAttributes (attributes , loggingEvent , skipLogstashMarkers );
332
348
} else {
333
- captureSingleMarkerAttribute (attributes , loggingEvent );
349
+ captureSingleMarkerAttribute (attributes , loggingEvent , skipLogstashMarkers );
334
350
}
335
351
}
336
352
337
353
@ SuppressWarnings ("deprecation" ) // getMarker is deprecate since 1.3.0
338
354
private static void captureSingleMarkerAttribute (
339
- AttributesBuilder attributes , ILoggingEvent loggingEvent ) {
355
+ AttributesBuilder attributes , ILoggingEvent loggingEvent , boolean skipLogstashMarkers ) {
340
356
Marker marker = loggingEvent .getMarker ();
341
- if (marker != null ) {
357
+ if (marker != null && (! skipLogstashMarkers || ! isLogstashMarker ( marker )) ) {
342
358
attributes .put (LOG_MARKER , marker .getName ());
343
359
}
344
360
}
345
361
346
362
@ NoMuzzle
347
363
private static void captureMultipleMarkerAttributes (
348
- AttributesBuilder attributes , ILoggingEvent loggingEvent ) {
364
+ AttributesBuilder attributes , ILoggingEvent loggingEvent , boolean skipLogstashMarkers ) {
349
365
List <String > markerNames = new ArrayList <>(loggingEvent .getMarkerList ().size ());
350
366
for (Marker marker : loggingEvent .getMarkerList ()) {
351
- markerNames .add (marker .getName ());
367
+ if (!skipLogstashMarkers || !isLogstashMarker (marker )) {
368
+ markerNames .add (marker .getName ());
369
+ }
370
+ }
371
+ if (!markerNames .isEmpty ()) {
372
+ attributes .put (LOG_MARKER , markerNames .toArray (new String [0 ]));
352
373
}
353
- attributes .put (LOG_MARKER , markerNames .toArray (new String [0 ]));
354
374
}
355
375
356
376
@ NoMuzzle
@@ -369,6 +389,173 @@ private static boolean supportsMultipleMarkers() {
369
389
return true ;
370
390
}
371
391
392
+ private static void captureLogstashAttributes (
393
+ AttributesBuilder attributes , ILoggingEvent loggingEvent ) {
394
+ try {
395
+ if (supportsMultipleMarkers && hasMultipleMarkers (loggingEvent )) {
396
+ captureMultipleLogstashAttributes (attributes , loggingEvent );
397
+ } else {
398
+ captureSingleLogstashAttribute (attributes , loggingEvent );
399
+ }
400
+ } catch (Throwable e ) {
401
+ // ignore
402
+ }
403
+ }
404
+
405
+ private static boolean isLogstashMarker (Marker marker ) {
406
+ return marker instanceof LogstashMarker ;
407
+ }
408
+
409
+ @ NoMuzzle
410
+ @ SuppressWarnings ("deprecation" ) // getMarker is deprecate since 1.3.0
411
+ private static void captureSingleLogstashAttribute (
412
+ AttributesBuilder attributes , ILoggingEvent loggingEvent ) {
413
+ Marker marker = loggingEvent .getMarker ();
414
+ if (isLogstashMarker (marker )) {
415
+ LogstashMarker logstashMarker = (LogstashMarker ) marker ;
416
+ captureLogstashMarker (attributes , logstashMarker );
417
+ }
418
+ }
419
+
420
+ @ NoMuzzle
421
+ private static void captureMultipleLogstashAttributes (
422
+ AttributesBuilder attributes , ILoggingEvent loggingEvent ) {
423
+ for (Marker marker : loggingEvent .getMarkerList ()) {
424
+ if (isLogstashMarker (marker )) {
425
+ LogstashMarker logstashMarker = (LogstashMarker ) marker ;
426
+ captureLogstashMarker (attributes , logstashMarker );
427
+ }
428
+ }
429
+ }
430
+
431
+ private static void captureLogstashMarker (
432
+ AttributesBuilder attributes , LogstashMarker logstashMarker ) {
433
+ captureLogstashMarkerAttributes (attributes , logstashMarker );
434
+
435
+ if (logstashMarker .hasReferences ()) {
436
+ for (Iterator <Marker > it = logstashMarker .iterator (); it .hasNext (); ) {
437
+ Marker referenceMarker = it .next ();
438
+ if (isLogstashMarker (referenceMarker )) {
439
+ LogstashMarker referenceLogstashMarker = (LogstashMarker ) referenceMarker ;
440
+ captureLogstashMarker (attributes , referenceLogstashMarker );
441
+ }
442
+ }
443
+ }
444
+ }
445
+
446
+ @ NoMuzzle
447
+ private static void captureLogstashMarkerAttributes (
448
+ AttributesBuilder attributes , LogstashMarker logstashMarker ) {
449
+ if (logstashMarker instanceof SingleFieldAppendingMarker ) {
450
+ SingleFieldAppendingMarker singleFieldAppendingMarker =
451
+ (SingleFieldAppendingMarker ) logstashMarker ;
452
+ String fieldName = singleFieldAppendingMarker .getFieldName ();
453
+ String fieldValue = extractFieldValue (singleFieldAppendingMarker );
454
+ if (fieldName != null ) {
455
+ if (fieldValue != null ) {
456
+ attributes .put (fieldName , fieldValue );
457
+ } else {
458
+ attributes .put (fieldName , "" );
459
+ }
460
+ }
461
+ } else if (logstashMarker instanceof MapEntriesAppendingMarker ) {
462
+ MapEntriesAppendingMarker mapEntriesAppendingMarker =
463
+ (MapEntriesAppendingMarker ) logstashMarker ;
464
+ Map <?, ?> map = extractMapValue (mapEntriesAppendingMarker );
465
+ if (map != null ) {
466
+ for (Map .Entry <?, ?> entry : map .entrySet ()) {
467
+ Object key = entry .getKey ();
468
+ Object value = entry .getValue ();
469
+ if (key != null ) {
470
+ if (value != null ) {
471
+ attributes .put (key .toString (), value .toString ());
472
+ } else {
473
+ attributes .put (key .toString (), "" );
474
+ }
475
+ }
476
+ }
477
+ }
478
+ }
479
+ }
480
+
481
+ @ Nullable
482
+ private static String extractFieldValue (SingleFieldAppendingMarker singleFieldAppendingMarker ) {
483
+ // ObjectAppendingMarker.fieldValue since v7.0
484
+ // ObjectAppendingMarker.object since v3.0
485
+ // RawJsonAppendingMarker.rawJson since v3.0
486
+ Field field =
487
+ valueField .computeIfAbsent (
488
+ singleFieldAppendingMarker .getClass (),
489
+ clazz -> findValueField (clazz , new String [] {"fieldValue" , "object" , "rawJson" }));
490
+ if (field != null ) {
491
+ try {
492
+ Object value = field .get (singleFieldAppendingMarker );
493
+ if (value != null ) {
494
+ return value .toString ();
495
+ }
496
+ } catch (IllegalAccessException e ) {
497
+ // ignore
498
+ }
499
+ }
500
+ return null ;
501
+ }
502
+
503
+ @ Nullable
504
+ private static Map <?, ?> extractMapValue (MapEntriesAppendingMarker mapEntriesAppendingMarker ) {
505
+ // MapEntriesAppendingMarker.map since v3.0
506
+ Field field =
507
+ valueField .computeIfAbsent (
508
+ mapEntriesAppendingMarker .getClass (),
509
+ clazz -> findValueField (clazz , new String [] {"map" }));
510
+ if (field != null ) {
511
+ try {
512
+ Object value = field .get (mapEntriesAppendingMarker );
513
+ if (value instanceof Map ) {
514
+ return (Map <?, ?>) value ;
515
+ }
516
+ } catch (IllegalAccessException e ) {
517
+ // ignore
518
+ }
519
+ }
520
+ return null ;
521
+ }
522
+
523
+ @ Nullable
524
+ private static Field findValueField (Class <?> clazz , String [] fieldNames ) {
525
+ for (String fieldName : fieldNames ) {
526
+ try {
527
+ Field field = clazz .getDeclaredField (fieldName );
528
+ field .setAccessible (true );
529
+ return field ;
530
+ } catch (NoSuchFieldException e ) {
531
+ // ignore
532
+ }
533
+ }
534
+ return null ;
535
+ }
536
+
537
+ private static boolean supportsLogstashMarkers () {
538
+ try {
539
+ Class .forName ("net.logstash.logback.marker.LogstashMarker" );
540
+ } catch (ClassNotFoundException e ) {
541
+ return false ;
542
+ }
543
+
544
+ try {
545
+ Class .forName ("net.logstash.logback.marker.SingleFieldAppendingMarker" );
546
+ } catch (ClassNotFoundException e ) {
547
+ return false ;
548
+ }
549
+
550
+ try {
551
+ Class .forName ("net.logstash.logback.marker.MapEntriesAppendingMarker" );
552
+ } catch (ClassNotFoundException e ) {
553
+ return false ;
554
+ }
555
+
556
+ return true ;
557
+ }
558
+
372
559
/**
373
560
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
374
561
* any time.
@@ -381,6 +568,7 @@ public static final class Builder {
381
568
private boolean captureKeyValuePairAttributes ;
382
569
private boolean captureLoggerContext ;
383
570
private boolean captureArguments ;
571
+ private boolean captureLogstashAttributes ;
384
572
385
573
Builder () {}
386
574
@@ -426,6 +614,12 @@ public Builder setCaptureArguments(boolean captureArguments) {
426
614
return this ;
427
615
}
428
616
617
+ @ CanIgnoreReturnValue
618
+ public Builder setCaptureLogstashAttributes (boolean captureLogstashAttributes ) {
619
+ this .captureLogstashAttributes = captureLogstashAttributes ;
620
+ return this ;
621
+ }
622
+
429
623
public LoggingEventMapper build () {
430
624
return new LoggingEventMapper (this );
431
625
}
0 commit comments