2323import io .opentelemetry .semconv .ExceptionAttributes ;
2424import java .io .PrintWriter ;
2525import java .io .StringWriter ;
26+ import java .lang .reflect .Array ;
2627import java .lang .reflect .Field ;
2728import java .util .ArrayList ;
2829import java .util .Arrays ;
3233import java .util .Map ;
3334import java .util .concurrent .TimeUnit ;
3435import java .util .function .Function ;
35- import java .util .function .IntFunction ;
3636import java .util .stream .Collectors ;
3737import javax .annotation .Nullable ;
3838import net .logstash .logback .marker .LogstashMarker ;
@@ -70,7 +70,13 @@ public final class LoggingEventMapper {
7070 private static final AttributeKey <List <String >> LOG_BODY_PARAMETERS =
7171 AttributeKey .stringArrayKey ("log.body.parameters" );
7272
73- private static final Cache <Class <?>, Field > valueField = Cache .bounded (20 );
73+ private static final ClassValue <FieldReader > valueField =
74+ new ClassValue <FieldReader >() {
75+ @ Override
76+ protected FieldReader computeValue (Class <?> type ) {
77+ return createFieldReader (type );
78+ }
79+ };
7480
7581 private final boolean captureExperimentalAttributes ;
7682 private final List <String > captureMdcAttributes ;
@@ -288,13 +294,13 @@ private static void captureKeyValuePairAttributes(
288294 List <KeyValuePair > keyValuePairs = loggingEvent .getKeyValuePairs ();
289295 if (keyValuePairs != null ) {
290296 for (KeyValuePair keyValuePair : keyValuePairs ) {
291- captureKeyValueAttribute (attributes , keyValuePair .key , keyValuePair .value );
297+ captureAttribute (attributes , keyValuePair .key , keyValuePair .value );
292298 }
293299 }
294300 }
295301
296- private static void captureKeyValueAttribute (
297- AttributesBuilder attributes , Object key , Object value ) {
302+ // visible for testing
303+ static void captureAttribute ( AttributesBuilder attributes , Object key , Object value ) {
298304 // empty values are not serialized
299305 if (key != null && value != null ) {
300306 String keyStr = key .toString ();
@@ -310,12 +316,8 @@ private static void captureKeyValueAttribute(
310316 attributes .put (keyStr , ((Number ) value ).doubleValue ());
311317 } else if (value .getClass ().isArray ()) {
312318 if (value instanceof boolean [] || value instanceof Boolean []) {
313- captureKeyArrayValueAttribute (
314- attributes ,
315- AttributeKey .booleanArrayKey (keyStr ),
316- value ,
317- Boolean []::new ,
318- o -> (Boolean ) o );
319+ captureArrayValueAttribute (
320+ attributes , AttributeKey .booleanArrayKey (keyStr ), value , o -> (Boolean ) o );
319321 } else if (value instanceof byte []
320322 || value instanceof Byte []
321323 || value instanceof int []
@@ -324,66 +326,49 @@ private static void captureKeyValueAttribute(
324326 || value instanceof Long []
325327 || value instanceof short []
326328 || value instanceof Short []) {
327- captureKeyArrayValueAttribute (
328- attributes ,
329- AttributeKey .longArrayKey (keyStr ),
330- value ,
331- Long []::new ,
332- o -> ((Number ) o ).longValue ());
329+ captureArrayValueAttribute (
330+ attributes , AttributeKey .longArrayKey (keyStr ), value , o -> ((Number ) o ).longValue ());
333331 } else if (value instanceof float []
334332 || value instanceof Float []
335333 || value instanceof double []
336334 || value instanceof Double []) {
337- captureKeyArrayValueAttribute (
335+ captureArrayValueAttribute (
338336 attributes ,
339337 AttributeKey .doubleArrayKey (keyStr ),
340338 value ,
341- Double []::new ,
342339 o -> ((Number ) o ).doubleValue ());
343340 } else {
344- captureKeyArrayValueAttribute (
345- attributes ,
346- AttributeKey .stringArrayKey (keyStr ),
347- value ,
348- String []::new ,
349- String ::valueOf );
341+ captureArrayValueAttribute (
342+ attributes , AttributeKey .stringArrayKey (keyStr ), value , String ::valueOf );
350343 }
351344 } else if (value instanceof Collection ) {
352- captureKeyArrayValueAttribute (
345+ captureArrayValueAttribute (
353346 attributes ,
354347 AttributeKey .stringArrayKey (keyStr ),
355348 ((Collection <?>) value ).toArray (),
356- String []::new ,
357349 String ::valueOf );
358350 } else {
359351 attributes .put (getAttributeKey (keyStr ), String .valueOf (value ));
360352 }
361353 }
362354 }
363355
364- private static <T > void captureKeyArrayValueAttribute (
356+ private static <T > void captureArrayValueAttribute (
365357 AttributesBuilder attributes ,
366358 AttributeKey <List <T >> key ,
367359 Object array ,
368- IntFunction <T []> newArray ,
369360 Function <Object , T > extractor ) {
370- int length = java .lang .reflect .Array .getLength (array );
371- T [] typedArray = newArray .apply (length );
372- int offset = 0 ;
361+ List <T > list = new ArrayList <>();
362+ int length = Array .getLength (array );
373363 for (int i = 0 ; i < length ; i ++) {
374- Object value = java .lang .reflect .Array .get (array , i );
375- // empty values are not serialized
364+ Object value = Array .get (array , i );
376365 if (value != null ) {
377- typedArray [i - offset ] = extractor .apply (value );
378- } else {
379- offset ++;
366+ list .add (extractor .apply (value ));
380367 }
381368 }
382369 // empty lists are not serialized
383- if (length != offset ) {
384- attributes .put (
385- key ,
386- Arrays .asList (offset == 0 ? typedArray : Arrays .copyOf (typedArray , length - offset )));
370+ if (!list .isEmpty ()) {
371+ attributes .put (key , list );
387372 }
388373 }
389374
@@ -520,67 +505,84 @@ private static void captureLogstashMarker(
520505 @ NoMuzzle
521506 private static void captureLogstashMarkerAttributes (
522507 AttributesBuilder attributes , LogstashMarker logstashMarker ) {
523- if (logstashMarker instanceof SingleFieldAppendingMarker ) {
524- SingleFieldAppendingMarker singleFieldAppendingMarker =
525- (SingleFieldAppendingMarker ) logstashMarker ;
526- String fieldName = singleFieldAppendingMarker .getFieldName ();
527- Object fieldValue = extractFieldValue (singleFieldAppendingMarker );
528- captureKeyValueAttribute (attributes , fieldName , fieldValue );
529- } else if (logstashMarker instanceof MapEntriesAppendingMarker ) {
530- MapEntriesAppendingMarker mapEntriesAppendingMarker =
531- (MapEntriesAppendingMarker ) logstashMarker ;
532- Map <?, ?> map = extractMapValue (mapEntriesAppendingMarker );
533- if (map != null ) {
534- for (Map .Entry <?, ?> entry : map .entrySet ()) {
535- Object key = entry .getKey ();
536- Object value = entry .getValue ();
537- captureKeyValueAttribute (attributes , key , value );
538- }
539- }
508+ FieldReader fieldReader = valueField .get (logstashMarker .getClass ());
509+ if (fieldReader != null ) {
510+ fieldReader .read (attributes , logstashMarker );
540511 }
541512 }
542513
543- @ Nullable
544- private static Object extractFieldValue (SingleFieldAppendingMarker singleFieldAppendingMarker ) {
545- // ObjectAppendingMarker.fieldValue since v7.0
546- // ObjectAppendingMarker.object since v3.0
547- // RawJsonAppendingMarker.rawJson since v3.0
548- Field field =
549- valueField .computeIfAbsent (
550- singleFieldAppendingMarker .getClass (),
551- clazz -> findValueField (clazz , new String [] {"fieldValue" , "object" , "rawJson" }));
552- if (field != null ) {
553- try {
554- return field .get (singleFieldAppendingMarker );
555- } catch (IllegalAccessException e ) {
556- // ignore
557- }
514+ @ NoMuzzle
515+ private static boolean isSingleFieldAppendingMarker (Class <?> type ) {
516+ return SingleFieldAppendingMarker .class .isAssignableFrom (type );
517+ }
518+
519+ @ NoMuzzle
520+ private static boolean isMapEntriesAppendingMarker (Class <?> type ) {
521+ return MapEntriesAppendingMarker .class .isAssignableFrom (type );
522+ }
523+
524+ private static FieldReader createFieldReader (Class <?> type ) {
525+ if (isSingleFieldAppendingMarker (type )) {
526+ // ObjectAppendingMarker.fieldValue since v7.0
527+ // ObjectAppendingMarker.object since v3.0
528+ // RawJsonAppendingMarker.rawJson since v3.0
529+ return createStringReader (findValueField (type , "fieldValue" , "object" , "rawJson" ));
530+ } else if (isMapEntriesAppendingMarker (type )) {
531+ // MapEntriesAppendingMarker.map since v3.0
532+ return createMapReader (findValueField (type , "map" ));
558533 }
559534 return null ;
560535 }
561536
537+ @ NoMuzzle
538+ private static String getSingleFieldAppendingMarkerName (Object logstashMarker ) {
539+ SingleFieldAppendingMarker singleFieldAppendingMarker =
540+ (SingleFieldAppendingMarker ) logstashMarker ;
541+ return singleFieldAppendingMarker .getFieldName ();
542+ }
543+
562544 @ Nullable
563- private static Map <?, ?> extractMapValue (MapEntriesAppendingMarker mapEntriesAppendingMarker ) {
564- // MapEntriesAppendingMarker.map since v3.0
565- Field field =
566- valueField .computeIfAbsent (
567- mapEntriesAppendingMarker .getClass (),
568- clazz -> findValueField (clazz , new String [] {"map" }));
569- if (field != null ) {
570- try {
571- Object value = field .get (mapEntriesAppendingMarker );
572- if (value instanceof Map ) {
573- return (Map <?, ?>) value ;
545+ private static FieldReader createStringReader (Field field ) {
546+ if (field == null ) {
547+ return null ;
548+ }
549+ return (attributes , logstashMarker ) -> {
550+ String fieldName = getSingleFieldAppendingMarkerName (logstashMarker );
551+ Object fieldValue = extractFieldValue (field , logstashMarker );
552+ captureAttribute (attributes , fieldName , fieldValue );
553+ };
554+ }
555+
556+ @ Nullable
557+ private static FieldReader createMapReader (Field field ) {
558+ if (field == null ) {
559+ return null ;
560+ }
561+ return (attributes , logstashMarker ) -> {
562+ Object fieldValue = extractFieldValue (field , logstashMarker );
563+ if (fieldValue instanceof Map ) {
564+ Map <?, ?> map = (Map <?, ?>) fieldValue ;
565+ for (Map .Entry <?, ?> entry : map .entrySet ()) {
566+ Object key = entry .getKey ();
567+ Object value = entry .getValue ();
568+ captureAttribute (attributes , key , value );
574569 }
575- } catch (IllegalAccessException e ) {
576- // ignore
577570 }
571+ };
572+ }
573+
574+ @ Nullable
575+ private static Object extractFieldValue (Field field , Object logstashMarker ) {
576+ try {
577+ return field .get (logstashMarker );
578+ } catch (IllegalAccessException e ) {
579+ // ignore
578580 }
579581 return null ;
580582 }
581583
582584 @ Nullable
583- private static Field findValueField (Class <?> clazz , String [] fieldNames ) {
585+ private static Field findValueField (Class <?> clazz , String ... fieldNames ) {
584586 for (String fieldName : fieldNames ) {
585587 try {
586588 Field field = clazz .getDeclaredField (fieldName );
@@ -596,17 +598,7 @@ private static Field findValueField(Class<?> clazz, String[] fieldNames) {
596598 private static boolean supportsLogstashMarkers () {
597599 try {
598600 Class .forName ("net.logstash.logback.marker.LogstashMarker" );
599- } catch (ClassNotFoundException e ) {
600- return false ;
601- }
602-
603- try {
604601 Class .forName ("net.logstash.logback.marker.SingleFieldAppendingMarker" );
605- } catch (ClassNotFoundException e ) {
606- return false ;
607- }
608-
609- try {
610602 Class .forName ("net.logstash.logback.marker.MapEntriesAppendingMarker" );
611603 } catch (ClassNotFoundException e ) {
612604 return false ;
@@ -615,6 +607,10 @@ private static boolean supportsLogstashMarkers() {
615607 return true ;
616608 }
617609
610+ private interface FieldReader {
611+ void read (AttributesBuilder attributes , LogstashMarker logstashMarker );
612+ }
613+
618614 /**
619615 * This class is internal and is hence not for public use. Its APIs are unstable and can change at
620616 * any time.
0 commit comments