From 5c340b099fafa83e6da6ca1a36fcf31832a5ee66 Mon Sep 17 00:00:00 2001 From: "igor.suhorukov" <igor.suhorukov@gmail.com> Date: Fri, 19 Jul 2024 23:47:01 +0300 Subject: [PATCH 1/8] Save ILoggingEvent.getArgumentArray() arguments from Logback to support structured logging --- .../logback-appender-1.0/javaagent/README.md | 1 + .../appender/v1_0/LogbackSingletons.java | 5 ++++ .../logback-appender-1.0/library/README.md | 1 + .../appender/v1_0/OpenTelemetryAppender.java | 11 +++++++++ .../v1_0/internal/LoggingEventMapper.java | 22 +++++++++++++++++ .../logback/appender/v1_0/Slf4j2Test.java | 24 +++++++++++++++++++ .../slf4j2ApiTest/resources/logback-test.xml | 1 + 7 files changed, 65 insertions(+) diff --git a/instrumentation/logback/logback-appender-1.0/javaagent/README.md b/instrumentation/logback/logback-appender-1.0/javaagent/README.md index aaa7be6e0364..2a962ae4e739 100644 --- a/instrumentation/logback/logback-appender-1.0/javaagent/README.md +++ b/instrumentation/logback/logback-appender-1.0/javaagent/README.md @@ -7,6 +7,7 @@ | `otel.instrumentation.logback-appender.experimental.capture-marker-attribute` | Boolean | `false` | Enable the capture of Logback markers as attributes. | | `otel.instrumentation.logback-appender.experimental.capture-key-value-pair-attributes` | Boolean | `false` | Enable the capture of Logback key value pairs as attributes. | | `otel.instrumentation.logback-appender.experimental.capture-logger-context-attributes` | Boolean | `false` | Enable the capture of Logback logger context properties as attributes. | +| `otel.instrumentation.logback-appender.experimental.capture-arguments` | Boolean | `false` | Enable the capture of Logback logger arguments. | | `otel.instrumentation.logback-appender.experimental.capture-mdc-attributes` | String | | Comma separated list of MDC attributes to capture. Use the wildcard character `*` to capture all attributes. | [source code attributes]: https://github.com/open-telemetry/semantic-conventions/blob/main/docs/general/attributes.md#source-code-attributes diff --git a/instrumentation/logback/logback-appender-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/logback/appender/v1_0/LogbackSingletons.java b/instrumentation/logback/logback-appender-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/logback/appender/v1_0/LogbackSingletons.java index b2b9eb8a5a92..4fd9d3ab91cb 100644 --- a/instrumentation/logback/logback-appender-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/logback/appender/v1_0/LogbackSingletons.java +++ b/instrumentation/logback/logback-appender-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/logback/appender/v1_0/LogbackSingletons.java @@ -36,6 +36,10 @@ public final class LogbackSingletons { config.getBoolean( "otel.instrumentation.logback-appender.experimental.capture-logger-context-attributes", false); + boolean captureArguments = + config.getBoolean( + "otel.instrumentation.logback-appender.experimental.capture-arguments", + false); List<String> captureMdcAttributes = config.getList( "otel.instrumentation.logback-appender.experimental.capture-mdc-attributes", @@ -49,6 +53,7 @@ public final class LogbackSingletons { .setCaptureMarkerAttribute(captureMarkerAttribute) .setCaptureKeyValuePairAttributes(captureKeyValuePairAttributes) .setCaptureLoggerContext(captureLoggerContext) + .setCaptureArguments(captureArguments) .build(); } diff --git a/instrumentation/logback/logback-appender-1.0/library/README.md b/instrumentation/logback/logback-appender-1.0/library/README.md index a002e3a704a0..7a1b15c95bbb 100644 --- a/instrumentation/logback/logback-appender-1.0/library/README.md +++ b/instrumentation/logback/logback-appender-1.0/library/README.md @@ -100,6 +100,7 @@ The available settings are: | `captureMarkerAttribute` | Boolean | `false` | Enable the capture of Logback markers as attributes. | | `captureKeyValuePairAttributes` | Boolean | `false` | Enable the capture of Logback key value pairs as attributes. | | `captureLoggerContext` | Boolean | `false` | Enable the capture of Logback logger context properties as attributes. | +| `capture-arguments` | Boolean | `false` | Enable the capture of Logback logger arguments. | | `captureMdcAttributes` | String | | Comma separated list of MDC attributes to capture. Use the wildcard character `*` to capture all attributes. | | `numLogsCapturedBeforeOtelInstall` | Integer | 1000 | Log telemetry is emitted after the initialization of the OpenTelemetry Logback appender with an OpenTelemetry object. This setting allows you to modify the size of the cache used to replay the first logs. thread.id attribute is not captured. | diff --git a/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/OpenTelemetryAppender.java b/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/OpenTelemetryAppender.java index 3d689e7a5eb5..06904c219c81 100644 --- a/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/OpenTelemetryAppender.java +++ b/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/OpenTelemetryAppender.java @@ -33,6 +33,7 @@ public class OpenTelemetryAppender extends UnsynchronizedAppenderBase<ILoggingEv private boolean captureMarkerAttribute = false; private boolean captureKeyValuePairAttributes = false; private boolean captureLoggerContext = false; + private boolean captureArguments = true; private List<String> captureMdcAttributes = emptyList(); private volatile OpenTelemetry openTelemetry; @@ -79,6 +80,7 @@ public void start() { .setCaptureMarkerAttribute(captureMarkerAttribute) .setCaptureKeyValuePairAttributes(captureKeyValuePairAttributes) .setCaptureLoggerContext(captureLoggerContext) + .setCaptureArguments(captureArguments) .build(); eventsToReplay = new ArrayBlockingQueue<>(numLogsCapturedBeforeOtelInstall); super.start(); @@ -164,6 +166,15 @@ public void setCaptureLoggerContext(boolean captureLoggerContext) { this.captureLoggerContext = captureLoggerContext; } + /** + * Sets whether the arguments should be set to logs. + * + * @param captureArguments To enable or disable capturing logger arguments + */ + public void setCaptureArguments(boolean captureArguments) { + this.captureArguments = captureArguments; + } + /** Configures the {@link MDC} attributes that will be copied to logs. */ public void setCaptureMdcAttributes(String attributes) { if (attributes != null) { diff --git a/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapper.java b/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapper.java index 5ff92f6672b7..4d3ed8a00856 100644 --- a/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapper.java +++ b/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapper.java @@ -60,6 +60,7 @@ public final class LoggingEventMapper { private final boolean captureMarkerAttribute; private final boolean captureKeyValuePairAttributes; private final boolean captureLoggerContext; + private final boolean captureArguments; private LoggingEventMapper(Builder builder) { this.captureExperimentalAttributes = builder.captureExperimentalAttributes; @@ -68,6 +69,7 @@ private LoggingEventMapper(Builder builder) { this.captureMarkerAttribute = builder.captureMarkerAttribute; this.captureKeyValuePairAttributes = builder.captureKeyValuePairAttributes; this.captureLoggerContext = builder.captureLoggerContext; + this.captureArguments = builder.captureArguments; this.captureAllMdcAttributes = builder.captureMdcAttributes.size() == 1 && builder.captureMdcAttributes.get(0).equals("*"); } @@ -168,6 +170,11 @@ private void mapLoggingEvent( captureLoggerContext(attributes, loggingEvent.getLoggerContextVO().getPropertyMap()); } + if (captureArguments && + loggingEvent.getArgumentArray()!=null && loggingEvent.getArgumentArray().length > 0) { + captureArguments(attributes, loggingEvent.getMessage(), loggingEvent.getArgumentArray()); + } + builder.setAllAttributes(attributes.build()); // span context @@ -191,6 +198,14 @@ void captureMdcAttributes(AttributesBuilder attributes, Map<String, String> mdcP } } + void captureArguments(AttributesBuilder attributes, String message, Object[] arguments){ + attributes.put("src_msg_", message); + for (int idx = 0; idx < arguments.length; idx++) { + Object argument = arguments[idx]; + attributes.put("log_arg_" + idx, String.valueOf(argument)); + } + } + public static AttributeKey<String> getMdcAttributeKey(String key) { return mdcAttributeKeys.computeIfAbsent(key, AttributeKey::stringKey); } @@ -331,6 +346,7 @@ public static final class Builder { private boolean captureMarkerAttribute; private boolean captureKeyValuePairAttributes; private boolean captureLoggerContext; + private boolean captureArguments; Builder() {} @@ -370,6 +386,12 @@ public Builder setCaptureLoggerContext(boolean captureLoggerContext) { return this; } + @CanIgnoreReturnValue + public Builder setCaptureArguments(boolean captureArguments) { + this.captureArguments = captureArguments; + return this; + } + public LoggingEventMapper build() { return new LoggingEventMapper(this); } diff --git a/instrumentation/logback/logback-appender-1.0/library/src/slf4j2ApiTest/java/io/opentelemetry/instrumentation/logback/appender/v1_0/Slf4j2Test.java b/instrumentation/logback/logback-appender-1.0/library/src/slf4j2ApiTest/java/io/opentelemetry/instrumentation/logback/appender/v1_0/Slf4j2Test.java index 79d3c507295d..df68972d5593 100644 --- a/instrumentation/logback/logback-appender-1.0/library/src/slf4j2ApiTest/java/io/opentelemetry/instrumentation/logback/appender/v1_0/Slf4j2Test.java +++ b/instrumentation/logback/logback-appender-1.0/library/src/slf4j2ApiTest/java/io/opentelemetry/instrumentation/logback/appender/v1_0/Slf4j2Test.java @@ -112,4 +112,28 @@ void multipleMarkers() { AttributeKey.stringArrayKey("logback.marker"), value -> assertThat(value).isEqualTo(Arrays.asList(markerName1, markerName2))); } + + @Test + void arguments() { + logger + .atInfo() + .setMessage("log message {} and {}").addArgument("'world'").addArgument(Math.PI) + .log(); + + List<LogRecordData> logDataList = logRecordExporter.getFinishedLogRecordItems(); + assertThat(logDataList).hasSize(1); + LogRecordData logData = logDataList.get(0); + + assertThat(logData.getResource()).isEqualTo(resource); + assertThat(logData.getInstrumentationScopeInfo()).isEqualTo(instrumentationScopeInfo); + assertThat(logData.getBody().asString()).isEqualTo("log message 'world' and 3.141592653589793"); + assertThat(logData.getAttributes().size()) + .isEqualTo(7); + assertThat(logData) + .hasAttributesSatisfying( + equalTo(AttributeKey.stringKey("log_arg_0"), "'world'"), + equalTo(AttributeKey.stringKey("log_arg_1"), "3.141592653589793"), + equalTo(AttributeKey.stringKey("src_msg_"), "log message {} and {}") + ); + } } diff --git a/instrumentation/logback/logback-appender-1.0/library/src/slf4j2ApiTest/resources/logback-test.xml b/instrumentation/logback/logback-appender-1.0/library/src/slf4j2ApiTest/resources/logback-test.xml index d02bf772803d..366678be3369 100644 --- a/instrumentation/logback/logback-appender-1.0/library/src/slf4j2ApiTest/resources/logback-test.xml +++ b/instrumentation/logback/logback-appender-1.0/library/src/slf4j2ApiTest/resources/logback-test.xml @@ -14,6 +14,7 @@ <captureCodeAttributes>true</captureCodeAttributes> <captureMarkerAttribute>true</captureMarkerAttribute> <captureKeyValuePairAttributes>true</captureKeyValuePairAttributes> + <captureArguments>true</captureArguments> <captureMdcAttributes>*</captureMdcAttributes> </appender> From 567fce4fedea938a3c0b8168ecf92040b55ee94c Mon Sep 17 00:00:00 2001 From: "igor.suhorukov" <igor.suhorukov@gmail.com> Date: Sat, 20 Jul 2024 00:17:41 +0300 Subject: [PATCH 2/8] :instrumentation:logback:logback-appender-1.0:library:spotlessApply --- .../v1_0/internal/LoggingEventMapper.java | 17 +++++++++-------- .../logback/appender/v1_0/Slf4j2Test.java | 10 +++++----- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapper.java b/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapper.java index 4d3ed8a00856..a7f1d9c58012 100644 --- a/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapper.java +++ b/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapper.java @@ -170,9 +170,10 @@ private void mapLoggingEvent( captureLoggerContext(attributes, loggingEvent.getLoggerContextVO().getPropertyMap()); } - if (captureArguments && - loggingEvent.getArgumentArray()!=null && loggingEvent.getArgumentArray().length > 0) { - captureArguments(attributes, loggingEvent.getMessage(), loggingEvent.getArgumentArray()); + if (captureArguments + && loggingEvent.getArgumentArray() != null + && loggingEvent.getArgumentArray().length > 0) { + captureArguments(attributes, loggingEvent.getMessage(), loggingEvent.getArgumentArray()); } builder.setAllAttributes(attributes.build()); @@ -198,12 +199,12 @@ void captureMdcAttributes(AttributesBuilder attributes, Map<String, String> mdcP } } - void captureArguments(AttributesBuilder attributes, String message, Object[] arguments){ + void captureArguments(AttributesBuilder attributes, String message, Object[] arguments) { attributes.put("src_msg_", message); - for (int idx = 0; idx < arguments.length; idx++) { - Object argument = arguments[idx]; - attributes.put("log_arg_" + idx, String.valueOf(argument)); - } + for (int idx = 0; idx < arguments.length; idx++) { + Object argument = arguments[idx]; + attributes.put("log_arg_" + idx, String.valueOf(argument)); + } } public static AttributeKey<String> getMdcAttributeKey(String key) { diff --git a/instrumentation/logback/logback-appender-1.0/library/src/slf4j2ApiTest/java/io/opentelemetry/instrumentation/logback/appender/v1_0/Slf4j2Test.java b/instrumentation/logback/logback-appender-1.0/library/src/slf4j2ApiTest/java/io/opentelemetry/instrumentation/logback/appender/v1_0/Slf4j2Test.java index df68972d5593..2f72826df766 100644 --- a/instrumentation/logback/logback-appender-1.0/library/src/slf4j2ApiTest/java/io/opentelemetry/instrumentation/logback/appender/v1_0/Slf4j2Test.java +++ b/instrumentation/logback/logback-appender-1.0/library/src/slf4j2ApiTest/java/io/opentelemetry/instrumentation/logback/appender/v1_0/Slf4j2Test.java @@ -117,7 +117,9 @@ void multipleMarkers() { void arguments() { logger .atInfo() - .setMessage("log message {} and {}").addArgument("'world'").addArgument(Math.PI) + .setMessage("log message {} and {}") + .addArgument("'world'") + .addArgument(Math.PI) .log(); List<LogRecordData> logDataList = logRecordExporter.getFinishedLogRecordItems(); @@ -127,13 +129,11 @@ void arguments() { assertThat(logData.getResource()).isEqualTo(resource); assertThat(logData.getInstrumentationScopeInfo()).isEqualTo(instrumentationScopeInfo); assertThat(logData.getBody().asString()).isEqualTo("log message 'world' and 3.141592653589793"); - assertThat(logData.getAttributes().size()) - .isEqualTo(7); + assertThat(logData.getAttributes().size()).isEqualTo(7); assertThat(logData) .hasAttributesSatisfying( equalTo(AttributeKey.stringKey("log_arg_0"), "'world'"), equalTo(AttributeKey.stringKey("log_arg_1"), "3.141592653589793"), - equalTo(AttributeKey.stringKey("src_msg_"), "log message {} and {}") - ); + equalTo(AttributeKey.stringKey("src_msg_"), "log message {} and {}")); } } From 7500d3eba9397f43cd103d881577b26873ce6853 Mon Sep 17 00:00:00 2001 From: "igor.suhorukov" <igor.suhorukov@gmail.com> Date: Sat, 20 Jul 2024 00:30:08 +0300 Subject: [PATCH 3/8] :instrumentation:logback:logback-appender-1.0:javaagent:spotlessApply --- .../logback/appender/v1_0/LogbackSingletons.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/instrumentation/logback/logback-appender-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/logback/appender/v1_0/LogbackSingletons.java b/instrumentation/logback/logback-appender-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/logback/appender/v1_0/LogbackSingletons.java index 4fd9d3ab91cb..a3d1c6d90688 100644 --- a/instrumentation/logback/logback-appender-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/logback/appender/v1_0/LogbackSingletons.java +++ b/instrumentation/logback/logback-appender-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/logback/appender/v1_0/LogbackSingletons.java @@ -38,8 +38,7 @@ public final class LogbackSingletons { false); boolean captureArguments = config.getBoolean( - "otel.instrumentation.logback-appender.experimental.capture-arguments", - false); + "otel.instrumentation.logback-appender.experimental.capture-arguments", false); List<String> captureMdcAttributes = config.getList( "otel.instrumentation.logback-appender.experimental.capture-mdc-attributes", From 1b1f307a658abad02b5a24186807b9864bcc9b21 Mon Sep 17 00:00:00 2001 From: "igor.suhorukov" <igor.suhorukov@gmail.com> Date: Mon, 22 Jul 2024 11:06:33 +0300 Subject: [PATCH 4/8] add parameter typing, change attributes names --- .../v1_0/internal/LoggingEventMapper.java | 40 +++++++++++-------- .../logback/appender/v1_0/Slf4j2Test.java | 20 +++++++--- 2 files changed, 37 insertions(+), 23 deletions(-) diff --git a/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapper.java b/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapper.java index a7f1d9c58012..3a1998ba82c9 100644 --- a/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapper.java +++ b/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapper.java @@ -200,10 +200,11 @@ void captureMdcAttributes(AttributesBuilder attributes, Map<String, String> mdcP } void captureArguments(AttributesBuilder attributes, String message, Object[] arguments) { - attributes.put("src_msg_", message); + String bodyKey = "log.body.original"; + attributes.put(bodyKey, message); for (int idx = 0; idx < arguments.length; idx++) { Object argument = arguments[idx]; - attributes.put("log_arg_" + idx, String.valueOf(argument)); + propagateAttribute(attributes, String.format("%s.param.%d", bodyKey, idx), argument); } } @@ -247,21 +248,26 @@ private static void captureKeyValuePairAttributes( if (keyValuePairs != null) { for (KeyValuePair keyValuePair : keyValuePairs) { Object value = keyValuePair.value; - if (keyValuePair.value != null) { - // preserve type for boolean and numeric values, everything else is converted to String - if (value instanceof Boolean) { - attributes.put(keyValuePair.key, (Boolean) keyValuePair.value); - } else if (value instanceof Byte - || value instanceof Integer - || value instanceof Long - || value instanceof Short) { - attributes.put(keyValuePair.key, ((Number) keyValuePair.value).longValue()); - } else if (value instanceof Double || value instanceof Float) { - attributes.put(keyValuePair.key, ((Number) keyValuePair.value).doubleValue()); - } else { - attributes.put(getAttributeKey(keyValuePair.key), keyValuePair.value.toString()); - } - } + propagateAttribute(attributes, keyValuePair.key, value); + } + } + } + + @NoMuzzle + private static void propagateAttribute(AttributesBuilder attributes, String key, Object value) { + if (value != null) { + // preserve type for boolean and numeric values, everything else is converted to String + if (value instanceof Boolean) { + attributes.put(key, (Boolean) value); + } else if (value instanceof Byte + || value instanceof Integer + || value instanceof Long + || value instanceof Short) { + attributes.put(key, ((Number) value).longValue()); + } else if (value instanceof Double || value instanceof Float) { + attributes.put(key, ((Number) value).doubleValue()); + } else { + attributes.put(getAttributeKey(key), value.toString()); } } } diff --git a/instrumentation/logback/logback-appender-1.0/library/src/slf4j2ApiTest/java/io/opentelemetry/instrumentation/logback/appender/v1_0/Slf4j2Test.java b/instrumentation/logback/logback-appender-1.0/library/src/slf4j2ApiTest/java/io/opentelemetry/instrumentation/logback/appender/v1_0/Slf4j2Test.java index 2f72826df766..4cf1c273039a 100644 --- a/instrumentation/logback/logback-appender-1.0/library/src/slf4j2ApiTest/java/io/opentelemetry/instrumentation/logback/appender/v1_0/Slf4j2Test.java +++ b/instrumentation/logback/logback-appender-1.0/library/src/slf4j2ApiTest/java/io/opentelemetry/instrumentation/logback/appender/v1_0/Slf4j2Test.java @@ -117,9 +117,11 @@ void multipleMarkers() { void arguments() { logger .atInfo() - .setMessage("log message {} and {}") + .setMessage("log message {} and {}, bool {}, long {}") .addArgument("'world'") .addArgument(Math.PI) + .addArgument(true) + .addArgument(Long.MAX_VALUE) .log(); List<LogRecordData> logDataList = logRecordExporter.getFinishedLogRecordItems(); @@ -128,12 +130,18 @@ void arguments() { assertThat(logData.getResource()).isEqualTo(resource); assertThat(logData.getInstrumentationScopeInfo()).isEqualTo(instrumentationScopeInfo); - assertThat(logData.getBody().asString()).isEqualTo("log message 'world' and 3.141592653589793"); - assertThat(logData.getAttributes().size()).isEqualTo(7); + assertThat(logData.getBody().asString()) + .isEqualTo( + "log message 'world' and 3.141592653589793, bool true, long 9223372036854775807"); + assertThat(logData.getAttributes().size()).isEqualTo(9); assertThat(logData) .hasAttributesSatisfying( - equalTo(AttributeKey.stringKey("log_arg_0"), "'world'"), - equalTo(AttributeKey.stringKey("log_arg_1"), "3.141592653589793"), - equalTo(AttributeKey.stringKey("src_msg_"), "log message {} and {}")); + equalTo(AttributeKey.stringKey("log.body.original.param.0"), "'world'"), + equalTo(AttributeKey.doubleKey("log.body.original.param.1"), Math.PI), + equalTo(AttributeKey.booleanKey("log.body.original.param.2"), true), + equalTo(AttributeKey.longKey("log.body.original.param.3"), Long.MAX_VALUE), + equalTo( + AttributeKey.stringKey("log.body.original"), + "log message {} and {}, bool {}, long {}")); } } From b5f5f9e2bd271a44ec8d528e2b13f0b90f937c4e Mon Sep 17 00:00:00 2001 From: "igor.suhorukov" <igor.suhorukov@gmail.com> Date: Thu, 25 Jul 2024 00:04:04 +0300 Subject: [PATCH 5/8] fix OpenTelemetryAppender configuration XML element name --- instrumentation/logback/logback-appender-1.0/library/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/logback/logback-appender-1.0/library/README.md b/instrumentation/logback/logback-appender-1.0/library/README.md index 7a1b15c95bbb..14c515071d76 100644 --- a/instrumentation/logback/logback-appender-1.0/library/README.md +++ b/instrumentation/logback/logback-appender-1.0/library/README.md @@ -100,7 +100,7 @@ The available settings are: | `captureMarkerAttribute` | Boolean | `false` | Enable the capture of Logback markers as attributes. | | `captureKeyValuePairAttributes` | Boolean | `false` | Enable the capture of Logback key value pairs as attributes. | | `captureLoggerContext` | Boolean | `false` | Enable the capture of Logback logger context properties as attributes. | -| `capture-arguments` | Boolean | `false` | Enable the capture of Logback logger arguments. | +| `captureArguments` | Boolean | `false` | Enable the capture of Logback logger arguments. | | `captureMdcAttributes` | String | | Comma separated list of MDC attributes to capture. Use the wildcard character `*` to capture all attributes. | | `numLogsCapturedBeforeOtelInstall` | Integer | 1000 | Log telemetry is emitted after the initialization of the OpenTelemetry Logback appender with an OpenTelemetry object. This setting allows you to modify the size of the cache used to replay the first logs. thread.id attribute is not captured. | From ee2d76f61fb7e502f7cf91db62b7e6f0d097a944 Mon Sep 17 00:00:00 2001 From: "igor.suhorukov" <igor.suhorukov@gmail.com> Date: Thu, 25 Jul 2024 21:19:42 +0300 Subject: [PATCH 6/8] New naming based on OpenTelemetry Java + Instrumentation SIG notes --- .../appender/v1_0/internal/LoggingEventMapper.java | 4 ++-- .../logback/appender/v1_0/Slf4j2Test.java | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapper.java b/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapper.java index 3a1998ba82c9..e2e2997b8153 100644 --- a/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapper.java +++ b/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapper.java @@ -200,11 +200,11 @@ void captureMdcAttributes(AttributesBuilder attributes, Map<String, String> mdcP } void captureArguments(AttributesBuilder attributes, String message, Object[] arguments) { - String bodyKey = "log.body.original"; + String bodyKey = "log.body.template"; attributes.put(bodyKey, message); for (int idx = 0; idx < arguments.length; idx++) { Object argument = arguments[idx]; - propagateAttribute(attributes, String.format("%s.param.%d", bodyKey, idx), argument); + propagateAttribute(attributes, String.format("log.body.parameters.%d", idx), argument); } } diff --git a/instrumentation/logback/logback-appender-1.0/library/src/slf4j2ApiTest/java/io/opentelemetry/instrumentation/logback/appender/v1_0/Slf4j2Test.java b/instrumentation/logback/logback-appender-1.0/library/src/slf4j2ApiTest/java/io/opentelemetry/instrumentation/logback/appender/v1_0/Slf4j2Test.java index 4cf1c273039a..6a973451e825 100644 --- a/instrumentation/logback/logback-appender-1.0/library/src/slf4j2ApiTest/java/io/opentelemetry/instrumentation/logback/appender/v1_0/Slf4j2Test.java +++ b/instrumentation/logback/logback-appender-1.0/library/src/slf4j2ApiTest/java/io/opentelemetry/instrumentation/logback/appender/v1_0/Slf4j2Test.java @@ -136,12 +136,12 @@ void arguments() { assertThat(logData.getAttributes().size()).isEqualTo(9); assertThat(logData) .hasAttributesSatisfying( - equalTo(AttributeKey.stringKey("log.body.original.param.0"), "'world'"), - equalTo(AttributeKey.doubleKey("log.body.original.param.1"), Math.PI), - equalTo(AttributeKey.booleanKey("log.body.original.param.2"), true), - equalTo(AttributeKey.longKey("log.body.original.param.3"), Long.MAX_VALUE), + equalTo(AttributeKey.stringKey("log.body.parameters.0"), "'world'"), + equalTo(AttributeKey.doubleKey("log.body.parameters.1"), Math.PI), + equalTo(AttributeKey.booleanKey("log.body.parameters.2"), true), + equalTo(AttributeKey.longKey("log.body.parameters.3"), Long.MAX_VALUE), equalTo( - AttributeKey.stringKey("log.body.original"), + AttributeKey.stringKey("log.body.template"), "log message {} and {}, bool {}, long {}")); } } From e35b9f01c572022370b52bae29a0f9fbec7091af Mon Sep 17 00:00:00 2001 From: "igor.suhorukov" <igor.suhorukov@gmail.com> Date: Thu, 25 Jul 2024 22:00:06 +0300 Subject: [PATCH 7/8] log.body.parameters as stringArray --- .../v1_0/internal/LoggingEventMapper.java | 9 +++++---- .../logback/appender/v1_0/Slf4j2Test.java | 17 ++++++++++++----- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapper.java b/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapper.java index e2e2997b8153..28b42ac32e22 100644 --- a/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapper.java +++ b/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapper.java @@ -24,9 +24,11 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; import org.slf4j.Marker; import org.slf4j.event.KeyValuePair; @@ -202,10 +204,9 @@ void captureMdcAttributes(AttributesBuilder attributes, Map<String, String> mdcP void captureArguments(AttributesBuilder attributes, String message, Object[] arguments) { String bodyKey = "log.body.template"; attributes.put(bodyKey, message); - for (int idx = 0; idx < arguments.length; idx++) { - Object argument = arguments[idx]; - propagateAttribute(attributes, String.format("log.body.parameters.%d", idx), argument); - } + attributes.put( + AttributeKey.stringArrayKey("log.body.parameters"), + Arrays.stream(arguments).map(String::valueOf).collect(Collectors.toList())); } public static AttributeKey<String> getMdcAttributeKey(String key) { diff --git a/instrumentation/logback/logback-appender-1.0/library/src/slf4j2ApiTest/java/io/opentelemetry/instrumentation/logback/appender/v1_0/Slf4j2Test.java b/instrumentation/logback/logback-appender-1.0/library/src/slf4j2ApiTest/java/io/opentelemetry/instrumentation/logback/appender/v1_0/Slf4j2Test.java index 6a973451e825..af17aeaedd84 100644 --- a/instrumentation/logback/logback-appender-1.0/library/src/slf4j2ApiTest/java/io/opentelemetry/instrumentation/logback/appender/v1_0/Slf4j2Test.java +++ b/instrumentation/logback/logback-appender-1.0/library/src/slf4j2ApiTest/java/io/opentelemetry/instrumentation/logback/appender/v1_0/Slf4j2Test.java @@ -133,13 +133,20 @@ void arguments() { assertThat(logData.getBody().asString()) .isEqualTo( "log message 'world' and 3.141592653589793, bool true, long 9223372036854775807"); - assertThat(logData.getAttributes().size()).isEqualTo(9); + assertThat(logData.getAttributes().size()).isEqualTo(6); + assertThat(logData.getAttributes()) + .hasEntrySatisfying( + AttributeKey.stringArrayKey("log.body.parameters"), + value -> + assertThat(value) + .isEqualTo( + Arrays.asList( + "'world'", + String.valueOf(Math.PI), + String.valueOf(true), + String.valueOf(Long.MAX_VALUE)))); assertThat(logData) .hasAttributesSatisfying( - equalTo(AttributeKey.stringKey("log.body.parameters.0"), "'world'"), - equalTo(AttributeKey.doubleKey("log.body.parameters.1"), Math.PI), - equalTo(AttributeKey.booleanKey("log.body.parameters.2"), true), - equalTo(AttributeKey.longKey("log.body.parameters.3"), Long.MAX_VALUE), equalTo( AttributeKey.stringKey("log.body.template"), "log message {} and {}, bool {}, long {}")); From d644db3ce309de0ad1ed5f237156e63aece73ccd Mon Sep 17 00:00:00 2001 From: Lauri Tulmin <ltulmin@splunk.com> Date: Fri, 9 Aug 2024 16:48:41 +0300 Subject: [PATCH 8/8] have constants for attribute keys, revert extracting key value pair handling to separate method --- .../v1_0/internal/LoggingEventMapper.java | 45 +++++++++---------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapper.java b/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapper.java index 28b42ac32e22..fb903c0aeb84 100644 --- a/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapper.java +++ b/instrumentation/logback/logback-appender-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapper.java @@ -54,6 +54,10 @@ public final class LoggingEventMapper { private static final AttributeKey<List<String>> LOG_MARKER = AttributeKey.stringArrayKey("logback.marker"); + private static final AttributeKey<String> LOG_BODY_TEMPLATE = + AttributeKey.stringKey("log.body.template"); + private static final AttributeKey<List<String>> LOG_BODY_PARAMETERS = + AttributeKey.stringArrayKey("log.body.parameters"); private final boolean captureExperimentalAttributes; private final List<String> captureMdcAttributes; @@ -202,10 +206,9 @@ void captureMdcAttributes(AttributesBuilder attributes, Map<String, String> mdcP } void captureArguments(AttributesBuilder attributes, String message, Object[] arguments) { - String bodyKey = "log.body.template"; - attributes.put(bodyKey, message); + attributes.put(LOG_BODY_TEMPLATE, message); attributes.put( - AttributeKey.stringArrayKey("log.body.parameters"), + LOG_BODY_PARAMETERS, Arrays.stream(arguments).map(String::valueOf).collect(Collectors.toList())); } @@ -249,26 +252,22 @@ private static void captureKeyValuePairAttributes( if (keyValuePairs != null) { for (KeyValuePair keyValuePair : keyValuePairs) { Object value = keyValuePair.value; - propagateAttribute(attributes, keyValuePair.key, value); - } - } - } - - @NoMuzzle - private static void propagateAttribute(AttributesBuilder attributes, String key, Object value) { - if (value != null) { - // preserve type for boolean and numeric values, everything else is converted to String - if (value instanceof Boolean) { - attributes.put(key, (Boolean) value); - } else if (value instanceof Byte - || value instanceof Integer - || value instanceof Long - || value instanceof Short) { - attributes.put(key, ((Number) value).longValue()); - } else if (value instanceof Double || value instanceof Float) { - attributes.put(key, ((Number) value).doubleValue()); - } else { - attributes.put(getAttributeKey(key), value.toString()); + if (value != null) { + String key = keyValuePair.key; + // preserve type for boolean and numeric values, everything else is converted to String + if (value instanceof Boolean) { + attributes.put(key, (Boolean) value); + } else if (value instanceof Byte + || value instanceof Integer + || value instanceof Long + || value instanceof Short) { + attributes.put(key, ((Number) value).longValue()); + } else if (value instanceof Double || value instanceof Float) { + attributes.put(key, ((Number) value).doubleValue()); + } else { + attributes.put(getAttributeKey(key), value.toString()); + } + } } } }