From 4231e390aba80a802ef73f29315b690fbacea695 Mon Sep 17 00:00:00 2001 From: Phil Clay Date: Fri, 12 Sep 2025 14:08:15 -0600 Subject: [PATCH 1/9] Capture event name in logback/log4j/jboss-logmanager instrumentation Added a new capture-event-name configuration option to logback/log4j/jboss-logmanager library/javaagent instrumentation that, when true, 1. captures the log event name from the recently undeprecated semantic attribute named `event.name`, and 2. removes the `event.name` attribute from the log event. This allows the log event name to be specified when logging using standard log APIs, rather than having to use the OpenTelemetry SDK log APIs directly. The `event.name` attribute can be captured using any attribute capturing mechanism supported by the log instrumentation. For example, if logback-appender's `captureKeyValuePairAttributes` is true, then an application can specify the `event.name` attribute via an slf4j key/value when logging. Capturing the event name is enabled with the following configuration values, depending on what you're using: * logback-appender-1.0/library - `io.opentelemetry.instrumentation.logback.appender.v1_0.OpenTelemetryAppender` - `captureEventName` * logback-appender-1.0/javaagent - `otel.instrumentation.logback-appender.experimental.capture-event-name` * log4j-appender-2.17/library - `io.opentelemetry.instrumentation.log4j.appender.v2_17.OpenTelemetryAppender` - `captureEventName` * log4j-appender-2.17/javaagent - `otel.instrumentation.log4j-appender.experimental.capture-event-name` * jboss-logmanager-appender-1.1/javaagent - `otel.instrumentation.jboss-logmanager.experimental.capture-event-name` Fixes: gh-14632 --- instrumentation/jboss-logmanager/README.md | 9 +++--- .../javaagent/build.gradle.kts | 1 + .../appender/v1_1/LoggingEventMapper.java | 23 ++++++++++++- .../appender/v1_1/JbossLogmanagerTest.java | 3 ++ .../log4j-appender-2.17/javaagent/README.md | 15 +++++---- .../log4j/appender/v2_17/Log4jHelper.java | 6 +++- .../log4j-appender-2.17/library/README.md | 17 +++++----- .../appender/v2_17/OpenTelemetryAppender.java | 24 +++++++++++++- .../v2_17/internal/LogEventMapper.java | 24 ++++++++++++-- .../AbstractOpenTelemetryAppenderTest.java | 2 ++ .../v2_17/internal/LogEventMapperTest.java | 32 +++++++++++++++---- .../library/src/test/resources/log4j2.xml | 2 +- .../logback-appender-1.0/javaagent/README.md | 21 ++++++------ .../appender/v1_0/LogbackSingletons.java | 4 +++ .../logback-appender-1.0/library/README.md | 1 + .../appender/v1_0/OpenTelemetryAppender.java | 18 +++++++++++ .../v1_0/internal/LoggingEventMapper.java | 28 ++++++++++++++-- .../logback/appender/v1_0/Slf4j2Test.java | 4 +++ .../slf4j2ApiTest/resources/logback-test.xml | 1 + .../AbstractOpenTelemetryAppenderTest.java | 2 ++ .../src/test/resources/logback-test.xml | 1 + 21 files changed, 194 insertions(+), 44 deletions(-) diff --git a/instrumentation/jboss-logmanager/README.md b/instrumentation/jboss-logmanager/README.md index 1cedd4d8aac7..67de476c590b 100644 --- a/instrumentation/jboss-logmanager/README.md +++ b/instrumentation/jboss-logmanager/README.md @@ -1,6 +1,7 @@ # Settings for the JBoss Log Manager instrumentation -| System property | Type | Default | Description | -|-----------------------------------------------------------------------------|---------|---------|--------------------------------------------------------------------------------------------------------------| -| `otel.instrumentation.jboss-logmanager.experimental-log-attributes` | Boolean | `false` | Enable the capture of experimental log attributes `thread.name` and `thread.id`. | -| `otel.instrumentation.jboss-logmanager.experimental.capture-mdc-attributes` | String | | Comma separated list of MDC attributes to capture. Use the wildcard character `*` to capture all attributes. | +| System property | Type | Default | Description | +|-----------------------------------------------------------------------------|---------|---------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `otel.instrumentation.jboss-logmanager.experimental-log-attributes` | Boolean | `false` | Enable the capture of experimental log attributes `thread.name` and `thread.id`. | +| `otel.instrumentation.jboss-logmanager.experimental.capture-mdc-attributes` | String | | Comma separated list of MDC attributes to capture. Use the wildcard character `*` to capture all attributes. | +| `otel.instrumentation.jboss-logmanager.experimental.capture-event-name` | Boolean | `true` | Enable the capture of the log event name from the `event.name` attribute (captured via one of the above means). When true, the `event.name` attribute will be used as the log event name, and the `event.name` attribute will be removed. | diff --git a/instrumentation/jboss-logmanager/jboss-logmanager-appender-1.1/javaagent/build.gradle.kts b/instrumentation/jboss-logmanager/jboss-logmanager-appender-1.1/javaagent/build.gradle.kts index 99b421c73921..be600b3b52b4 100644 --- a/instrumentation/jboss-logmanager/jboss-logmanager-appender-1.1/javaagent/build.gradle.kts +++ b/instrumentation/jboss-logmanager/jboss-logmanager-appender-1.1/javaagent/build.gradle.kts @@ -33,6 +33,7 @@ if (latestDepTest) { tasks.withType().configureEach { // TODO run tests both with and without experimental log attributes jvmArgs("-Dotel.instrumentation.jboss-logmanager.experimental.capture-mdc-attributes=*") + jvmArgs("-Dotel.instrumentation.jboss-logmanager.experimental.capture-event-name=true") jvmArgs("-Dotel.instrumentation.jboss-logmanager.experimental-log-attributes=true") jvmArgs("-Dotel.instrumentation.java-util-logging.experimental-log-attributes=true") } diff --git a/instrumentation/jboss-logmanager/jboss-logmanager-appender-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jbosslogmanager/appender/v1_1/LoggingEventMapper.java b/instrumentation/jboss-logmanager/jboss-logmanager-appender-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jbosslogmanager/appender/v1_1/LoggingEventMapper.java index 344c831605ac..bdc7ab8ae89b 100644 --- a/instrumentation/jboss-logmanager/jboss-logmanager-appender-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jbosslogmanager/appender/v1_1/LoggingEventMapper.java +++ b/instrumentation/jboss-logmanager/jboss-logmanager-appender-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jbosslogmanager/appender/v1_1/LoggingEventMapper.java @@ -30,6 +30,8 @@ public final class LoggingEventMapper { public static final LoggingEventMapper INSTANCE = new LoggingEventMapper(); + // copied from EventIncubatingAttributes + private static final AttributeKey EVENT_NAME = AttributeKey.stringKey("event.name"); private static final Cache> mdcAttributeKeys = Cache.bounded(100); private final List captureMdcAttributes; @@ -41,6 +43,11 @@ public final class LoggingEventMapper { // cached as an optimization private final boolean captureAllMdcAttributes; + private final boolean captureEventName = + AgentInstrumentationConfig.get() + .getBoolean( + "otel.instrumentation.jboss-logmanager.experimental.capture-event-name", false); + private LoggingEventMapper() { this.captureMdcAttributes = AgentInstrumentationConfig.get() @@ -90,7 +97,21 @@ public void capture(Logger logger, ExtLogRecord record) { attributes.put(ThreadIncubatingAttributes.THREAD_ID, currentThread.getId()); } - builder.setAllAttributes(attributes.build()); + Attributes realizedAttributes = attributes.build(); + if (captureEventName) { + realizedAttributes.forEach( + (attributeKey, value) -> { + if (attributeKey.equals(EVENT_NAME)) { + builder.setEventName(String.valueOf(value)); + } else { + @SuppressWarnings("unchecked") + AttributeKey attributeKeyAsObject = (AttributeKey) attributeKey; + builder.setAttribute(attributeKeyAsObject, value); + } + }); + } else { + builder.setAllAttributes(realizedAttributes); + } builder.setContext(Context.current()); diff --git a/instrumentation/jboss-logmanager/jboss-logmanager-appender-1.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jbosslogmanager/appender/v1_1/JbossLogmanagerTest.java b/instrumentation/jboss-logmanager/jboss-logmanager-appender-1.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jbosslogmanager/appender/v1_1/JbossLogmanagerTest.java index 97d886c020a5..bbf8ff2ac4bb 100644 --- a/instrumentation/jboss-logmanager/jboss-logmanager-appender-1.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jbosslogmanager/appender/v1_1/JbossLogmanagerTest.java +++ b/instrumentation/jboss-logmanager/jboss-logmanager-appender-1.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jbosslogmanager/appender/v1_1/JbossLogmanagerTest.java @@ -202,11 +202,13 @@ private static void performLogging( void testMdc() { MDC.put("key1", "val1"); MDC.put("key2", "val2"); + MDC.put("event.name", "MyEventName"); try { logger.info("xyz"); } finally { MDC.remove("key1"); MDC.remove("key2"); + MDC.remove("event.name"); } testing.waitAndAssertLogRecords( @@ -216,6 +218,7 @@ void testMdc() { .hasInstrumentationScope(InstrumentationScopeInfo.builder("abc").build()) .hasSeverity(Severity.INFO) .hasSeverityText("INFO") + .hasEventName("MyEventName") .hasAttributesSatisfyingExactly( equalTo(AttributeKey.stringKey("key1"), "val1"), equalTo(AttributeKey.stringKey("key2"), "val2"), diff --git a/instrumentation/log4j/log4j-appender-2.17/javaagent/README.md b/instrumentation/log4j/log4j-appender-2.17/javaagent/README.md index e3bf213d9498..c794ec7ba612 100644 --- a/instrumentation/log4j/log4j-appender-2.17/javaagent/README.md +++ b/instrumentation/log4j/log4j-appender-2.17/javaagent/README.md @@ -1,11 +1,12 @@ # Settings for the Log4j Appender instrumentation -| System property | Type | Default | Description | -|-----------------------------------------------------------------------------------|---------|---------|-----------------------------------------------------------------------------------------------------------------------------------------------| -| `otel.instrumentation.log4j-appender.experimental-log-attributes` | Boolean | `false` | Enable the capture of experimental log attributes `thread.name` and `thread.id`. | -| `otel.instrumentation.log4j-appender.experimental.capture-code-attributes` | Boolean | `false` | Enable the capture of [source code attributes]. Note that capturing source code attributes at logging sites might add a performance overhead. | -| `otel.instrumentation.log4j-appender.experimental.capture-map-message-attributes` | Boolean | `false` | Enable the capture of `MapMessage` attributes. | -| `otel.instrumentation.log4j-appender.experimental.capture-marker-attribute` | Boolean | `false` | Enable the capture of Log4j markers as attributes. | -| `otel.instrumentation.log4j-appender.experimental.capture-mdc-attributes` | String | | Comma separated list of context data attributes to capture. Use the wildcard character `*` to capture all attributes. | +| System property | Type | Default | Description | +|-----------------------------------------------------------------------------------|---------|---------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `otel.instrumentation.log4j-appender.experimental-log-attributes` | Boolean | `false` | Enable the capture of experimental log attributes `thread.name` and `thread.id`. | +| `otel.instrumentation.log4j-appender.experimental.capture-code-attributes` | Boolean | `false` | Enable the capture of [source code attributes]. Note that capturing source code attributes at logging sites might add a performance overhead. | +| `otel.instrumentation.log4j-appender.experimental.capture-map-message-attributes` | Boolean | `false` | Enable the capture of `MapMessage` attributes. | +| `otel.instrumentation.log4j-appender.experimental.capture-marker-attribute` | Boolean | `false` | Enable the capture of Log4j markers as attributes. | +| `otel.instrumentation.log4j-appender.experimental.capture-mdc-attributes` | String | | Comma separated list of context data attributes to capture. Use the wildcard character `*` to capture all attributes. | +| `otel.instrumentation.log4j-appender.experimental.capture-event-name` | Boolean | `false` | Enable the capture of the log event name from the `event.name` attribute (captured via one of the above means). When true, the `event.name` attribute will be used as the log event name, and the `event.name` attribute will be removed. | [source code attributes]: https://github.com/open-telemetry/semantic-conventions/blob/main/docs/general/attributes.md#source-code-attributes diff --git a/instrumentation/log4j/log4j-appender-2.17/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/log4j/appender/v2_17/Log4jHelper.java b/instrumentation/log4j/log4j-appender-2.17/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/log4j/appender/v2_17/Log4jHelper.java index 6fc308cc1dd9..ee35aaa184a4 100644 --- a/instrumentation/log4j/log4j-appender-2.17/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/log4j/appender/v2_17/Log4jHelper.java +++ b/instrumentation/log4j/log4j-appender-2.17/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/log4j/appender/v2_17/Log4jHelper.java @@ -52,6 +52,9 @@ public final class Log4jHelper { List captureContextDataAttributes = config.getList( "otel.instrumentation.log4j-appender.experimental.capture-mdc-attributes", emptyList()); + boolean captureEventName = + config.getBoolean( + "otel.instrumentation.log4j-appender.experimental.capture-event-name", false); mapper = new LogEventMapper<>( @@ -60,7 +63,8 @@ public final class Log4jHelper { captureCodeAttributes, captureMapMessageAttributes, captureMarkerAttribute, - captureContextDataAttributes); + captureContextDataAttributes, + captureEventName); } public static void capture( diff --git a/instrumentation/log4j/log4j-appender-2.17/library/README.md b/instrumentation/log4j/log4j-appender-2.17/library/README.md index 07bd37bba715..c2c3fff4503e 100644 --- a/instrumentation/log4j/log4j-appender-2.17/library/README.md +++ b/instrumentation/log4j/log4j-appender-2.17/library/README.md @@ -92,13 +92,14 @@ Setting can be configured as XML attributes, for example: The available settings are: -| XML Attribute | Type | Default | Description | -|------------------------------------|---------|---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `captureExperimentalAttributes` | Boolean | `false` | Enable the capture of experimental log attributes `thread.name` and `thread.id`. | -| `captureCodeAttributes` | Boolean | `false` | Enable the capture of [source code attributes]. Note that capturing source code attributes at logging sites might add a performance overhead. | -| `captureMapMessageAttributes` | Boolean | `false` | Enable the capture of `MapMessage` attributes. | -| `captureMarkerAttribute` | Boolean | `false` | Enable the capture of Log4j markers as attributes. | -| `captureContextDataAttributes` | String | | Comma separated list of context data attributes to capture. Use the wildcard character `*` to capture all attributes. | -| `numLogsCapturedBeforeOtelInstall` | Integer | 1000 | Log telemetry is emitted after the initialization of the OpenTelemetry Log4j appender with an OpenTelemetry object. This setting allows you to modify the size of the cache used to replay the first logs. | +| XML Attribute | Type | Default | Description | +|------------------------------------|---------|---------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `captureExperimentalAttributes` | Boolean | `false` | Enable the capture of experimental log attributes `thread.name` and `thread.id`. | +| `captureCodeAttributes` | Boolean | `false` | Enable the capture of [source code attributes]. Note that capturing source code attributes at logging sites might add a performance overhead. | +| `captureMapMessageAttributes` | Boolean | `false` | Enable the capture of `MapMessage` attributes. | +| `captureMarkerAttribute` | Boolean | `false` | Enable the capture of Log4j markers as attributes. | +| `captureContextDataAttributes` | String | | Comma separated list of context data attributes to capture. Use the wildcard character `*` to capture all attributes. | +| `captureEventName` | Boolean | `false` | Enable the capture of the log event name from the `event.name` attribute (captured via one of the above means). When true, the `event.name` attribute will be used as the log event name, and the `event.name` attribute will be removed. | +| `numLogsCapturedBeforeOtelInstall` | Integer | 1000 | Log telemetry is emitted after the initialization of the OpenTelemetry Log4j appender with an OpenTelemetry object. This setting allows you to modify the size of the cache used to replay the first logs. | [source code attributes]: https://github.com/open-telemetry/semantic-conventions/blob/main/docs/general/attributes.md#source-code-attributes diff --git a/instrumentation/log4j/log4j-appender-2.17/library/src/main/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/OpenTelemetryAppender.java b/instrumentation/log4j/log4j-appender-2.17/library/src/main/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/OpenTelemetryAppender.java index ff980b3be35a..85eb7821ee92 100644 --- a/instrumentation/log4j/log4j-appender-2.17/library/src/main/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/OpenTelemetryAppender.java +++ b/instrumentation/log4j/log4j-appender-2.17/library/src/main/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/OpenTelemetryAppender.java @@ -101,6 +101,7 @@ public static class Builder> extends AbstractAppender.Build @PluginBuilderAttribute private boolean captureMapMessageAttributes; @PluginBuilderAttribute private boolean captureMarkerAttribute; @PluginBuilderAttribute private String captureContextDataAttributes; + @PluginBuilderAttribute private boolean captureEventName; @PluginBuilderAttribute private int numLogsCapturedBeforeOtelInstall; @Nullable private OpenTelemetry openTelemetry; @@ -155,6 +156,24 @@ public B setCaptureContextDataAttributes(String captureContextDataAttributes) { return asBuilder(); } + /** + * Sets whether the value of the {@code event.name} attribute is used as the log event name. + * + *

The {@code event.name} attribute is captured via any other mechanism supported by this + * appender, such as when {@code captureContextDataAttributes} includes {@code event.name}. + * + *

When {@code captureEventName} is true, then the value of the {@code event.name} attribute + * will be used as the log event name, and {@code event.name} attribute will be removed. + * + * @param captureEventName to enable or disable capturing the {@code event.name} attribute as + * the log event name + */ + @CanIgnoreReturnValue + public B setCaptureEventName(boolean captureEventName) { + this.captureEventName = captureEventName; + return asBuilder(); + } + /** * Log telemetry is emitted after the initialization of the OpenTelemetry Logback appender with * an {@link OpenTelemetry} object. This setting allows you to modify the size of the cache used @@ -188,6 +207,7 @@ public OpenTelemetryAppender build() { captureMapMessageAttributes, captureMarkerAttribute, captureContextDataAttributes, + captureEventName, numLogsCapturedBeforeOtelInstall, openTelemetry); } @@ -204,6 +224,7 @@ private OpenTelemetryAppender( boolean captureMapMessageAttributes, boolean captureMarkerAttribute, String captureContextDataAttributes, + boolean captureEventName, int numLogsCapturedBeforeOtelInstall, OpenTelemetry openTelemetry) { @@ -215,7 +236,8 @@ private OpenTelemetryAppender( captureCodeAttributes, captureMapMessageAttributes, captureMarkerAttribute, - splitAndFilterBlanksAndNulls(captureContextDataAttributes)); + splitAndFilterBlanksAndNulls(captureContextDataAttributes), + captureEventName); this.openTelemetry = openTelemetry; this.captureCodeAttributes = captureCodeAttributes; if (numLogsCapturedBeforeOtelInstall != 0) { diff --git a/instrumentation/log4j/log4j-appender-2.17/library/src/main/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/internal/LogEventMapper.java b/instrumentation/log4j/log4j-appender-2.17/library/src/main/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/internal/LogEventMapper.java index 36ec63fcd186..334bb4543666 100644 --- a/instrumentation/log4j/log4j-appender-2.17/library/src/main/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/internal/LogEventMapper.java +++ b/instrumentation/log4j/log4j-appender-2.17/library/src/main/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/internal/LogEventMapper.java @@ -42,6 +42,8 @@ public final class LogEventMapper { // copied from ThreadIncubatingAttributes private static final AttributeKey THREAD_ID = AttributeKey.longKey("thread.id"); private static final AttributeKey THREAD_NAME = AttributeKey.stringKey("thread.name"); + // copied from EventIncubatingAttributes + private static final AttributeKey EVENT_NAME = AttributeKey.stringKey("event.name"); private static final String SPECIAL_MAP_MESSAGE_ATTRIBUTE = "message"; @@ -60,6 +62,7 @@ public final class LogEventMapper { private final boolean captureMarkerAttribute; private final List captureContextDataAttributes; private final boolean captureAllContextDataAttributes; + private final boolean captureEventName; public LogEventMapper( ContextDataAccessor contextDataAccessor, @@ -67,7 +70,8 @@ public LogEventMapper( boolean captureCodeAttributes, boolean captureMapMessageAttributes, boolean captureMarkerAttribute, - List captureContextDataAttributes) { + List captureContextDataAttributes, + boolean captureEventName) { this.contextDataAccessor = contextDataAccessor; this.captureCodeAttributes = captureCodeAttributes; @@ -77,6 +81,7 @@ public LogEventMapper( this.captureContextDataAttributes = captureContextDataAttributes; this.captureAllContextDataAttributes = captureContextDataAttributes.size() == 1 && captureContextDataAttributes.get(0).equals("*"); + this.captureEventName = captureEventName; } /** @@ -162,7 +167,22 @@ public void mapLogEvent( } } - builder.setAllAttributes(attributes.build()); + Attributes realizedAttributes = attributes.build(); + if (captureEventName) { + realizedAttributes.forEach( + (attributeKey, value) -> { + if (attributeKey.equals(EVENT_NAME)) { + builder.setEventName(String.valueOf(value)); + } else { + @SuppressWarnings("unchecked") + AttributeKey attributeKeyAsObject = (AttributeKey) attributeKey; + builder.setAttribute(attributeKeyAsObject, value); + } + }); + } else { + builder.setAllAttributes(realizedAttributes); + } + builder.setContext(context); } diff --git a/instrumentation/log4j/log4j-appender-2.17/library/src/test/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/AbstractOpenTelemetryAppenderTest.java b/instrumentation/log4j/log4j-appender-2.17/library/src/test/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/AbstractOpenTelemetryAppenderTest.java index 9b51bf835faa..872f5dedc0f2 100644 --- a/instrumentation/log4j/log4j-appender-2.17/library/src/test/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/AbstractOpenTelemetryAppenderTest.java +++ b/instrumentation/log4j/log4j-appender-2.17/library/src/test/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/AbstractOpenTelemetryAppenderTest.java @@ -155,6 +155,7 @@ void logWithExtras() { void logContextData() { ThreadContext.put("key1", "val1"); ThreadContext.put("key2", "val2"); + ThreadContext.put("event.name", "MyEventName"); try { logger.info("log message 1"); } finally { @@ -170,6 +171,7 @@ void logContextData() { .hasResource(resource) .hasInstrumentationScope(instrumentationScopeInfo) .hasBody("log message 1") + .hasEventName("MyEventName") .hasAttributesSatisfyingExactly( addLocationAttributes( "logContextData", diff --git a/instrumentation/log4j/log4j-appender-2.17/library/src/test/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/internal/LogEventMapperTest.java b/instrumentation/log4j/log4j-appender-2.17/library/src/test/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/internal/LogEventMapperTest.java index ce374ff97b9b..7d7de1c64c1d 100644 --- a/instrumentation/log4j/log4j-appender-2.17/library/src/test/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/internal/LogEventMapperTest.java +++ b/instrumentation/log4j/log4j-appender-2.17/library/src/test/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/internal/LogEventMapperTest.java @@ -32,7 +32,7 @@ void testDefault() { // given LogEventMapper> mapper = new LogEventMapper<>( - ContextDataAccessorImpl.INSTANCE, false, false, false, false, emptyList()); + ContextDataAccessorImpl.INSTANCE, false, false, false, false, emptyList(), false); Map contextData = new HashMap<>(); contextData.put("key1", "value1"); contextData.put("key2", "value2"); @@ -50,7 +50,13 @@ void testSome() { // given LogEventMapper> mapper = new LogEventMapper<>( - ContextDataAccessorImpl.INSTANCE, false, false, false, false, singletonList("key2")); + ContextDataAccessorImpl.INSTANCE, + false, + false, + false, + false, + singletonList("key2"), + false); Map contextData = new HashMap<>(); contextData.put("key1", "value1"); contextData.put("key2", "value2"); @@ -68,7 +74,13 @@ void testAll() { // given LogEventMapper> mapper = new LogEventMapper<>( - ContextDataAccessorImpl.INSTANCE, false, false, false, false, singletonList("*")); + ContextDataAccessorImpl.INSTANCE, + false, + false, + false, + false, + singletonList("*"), + false); Map contextData = new HashMap<>(); contextData.put("key1", "value1"); contextData.put("key2", "value2"); @@ -87,7 +99,13 @@ void testCaptureMapMessageDisabled() { // given LogEventMapper> mapper = new LogEventMapper<>( - ContextDataAccessorImpl.INSTANCE, false, false, false, false, singletonList("*")); + ContextDataAccessorImpl.INSTANCE, + false, + false, + false, + false, + singletonList("*"), + false); StringMapMessage message = new StringMapMessage(); message.put("key1", "value1"); @@ -109,7 +127,7 @@ void testCaptureMapMessageWithSpecialAttribute() { // given LogEventMapper> mapper = new LogEventMapper<>( - ContextDataAccessorImpl.INSTANCE, false, false, true, false, singletonList("*")); + ContextDataAccessorImpl.INSTANCE, false, false, true, false, singletonList("*"), false); StringMapMessage message = new StringMapMessage(); message.put("key1", "value1"); @@ -131,7 +149,7 @@ void testCaptureMapMessageWithoutSpecialAttribute() { // given LogEventMapper> mapper = new LogEventMapper<>( - ContextDataAccessorImpl.INSTANCE, false, false, true, false, singletonList("*")); + ContextDataAccessorImpl.INSTANCE, false, false, true, false, singletonList("*"), false); StringMapMessage message = new StringMapMessage(); message.put("key1", "value1"); @@ -156,7 +174,7 @@ void testCaptureStructuredDataMessage() { // given LogEventMapper> mapper = new LogEventMapper<>( - ContextDataAccessorImpl.INSTANCE, false, false, true, false, singletonList("*")); + ContextDataAccessorImpl.INSTANCE, false, false, true, false, singletonList("*"), false); StructuredDataMessage message = new StructuredDataMessage("an id", "a message", "a type"); message.put("key1", "value1"); diff --git a/instrumentation/log4j/log4j-appender-2.17/library/src/test/resources/log4j2.xml b/instrumentation/log4j/log4j-appender-2.17/library/src/test/resources/log4j2.xml index 1953d256c0b7..ceb9351c5e81 100644 --- a/instrumentation/log4j/log4j-appender-2.17/library/src/test/resources/log4j2.xml +++ b/instrumentation/log4j/log4j-appender-2.17/library/src/test/resources/log4j2.xml @@ -6,7 +6,7 @@ pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} traceId: %X{trace_id} spanId: %X{span_id} flags: %X{trace_flags} - %msg%n"/> - + diff --git a/instrumentation/logback/logback-appender-1.0/javaagent/README.md b/instrumentation/logback/logback-appender-1.0/javaagent/README.md index 581211cddb91..a2e36a3d1d2b 100644 --- a/instrumentation/logback/logback-appender-1.0/javaagent/README.md +++ b/instrumentation/logback/logback-appender-1.0/javaagent/README.md @@ -1,14 +1,15 @@ # Settings for the Logback Appender instrumentation -| System property | Type | Default | Description | -|----------------------------------------------------------------------------------------|---------|---------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `otel.instrumentation.logback-appender.experimental-log-attributes` | Boolean | `false` | Enable the capture of experimental log attributes `thread.name` and `thread.id`. | -| `otel.instrumentation.logback-appender.experimental.capture-code-attributes` | Boolean | `false` | Enable the capture of [source code attributes]. Note that capturing source code attributes at logging sites might add a performance overhead. | -| `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-logstash-attributes` | Boolean | `false` | Enable the capture of Logstash attributes, supported are those added to logs via `Markers.append()`, `Markers.appendEntries()`, `Markers.appendArray()` and `Markers.appendRaw()` methods. | -| `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. | +| System property | Type | Default | Description | +|----------------------------------------------------------------------------------------|---------|---------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `otel.instrumentation.logback-appender.experimental-log-attributes` | Boolean | `false` | Enable the capture of experimental log attributes `thread.name` and `thread.id`. | +| `otel.instrumentation.logback-appender.experimental.capture-code-attributes` | Boolean | `false` | Enable the capture of [source code attributes]. Note that capturing source code attributes at logging sites might add a performance overhead. | +| `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-logstash-attributes` | Boolean | `false` | Enable the capture of Logstash attributes, supported are those added to logs via `Markers.append()`, `Markers.appendEntries()`, `Markers.appendArray()` and `Markers.appendRaw()` methods. | +| `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. | +| `otel.instrumentation.logback-appender.experimental.capture-event-name` | Boolean | `false` | Enable the capture of the log event name from the `event.name` attribute (captured via one of the above means). When true, the `event.name` attribute will be used as the log event name, and the `event.name` attribute will be removed. | [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 76a99383ec1b..7a5df143cf4e 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 @@ -47,6 +47,9 @@ public final class LogbackSingletons { config.getList( "otel.instrumentation.logback-appender.experimental.capture-mdc-attributes", emptyList()); + boolean captureEventName = + config.getBoolean( + "otel.instrumentation.logback-appender.experimental.capture-event-name", false); mapper = LoggingEventMapper.builder() @@ -58,6 +61,7 @@ public final class LogbackSingletons { .setCaptureLoggerContext(captureLoggerContext) .setCaptureArguments(captureArguments) .setCaptureLogstashAttributes(captureLogstashAttributes) + .setCaptureEventName(captureEventName) .build(); } diff --git a/instrumentation/logback/logback-appender-1.0/library/README.md b/instrumentation/logback/logback-appender-1.0/library/README.md index fe98849e6747..568a6294825f 100644 --- a/instrumentation/logback/logback-appender-1.0/library/README.md +++ b/instrumentation/logback/logback-appender-1.0/library/README.md @@ -103,6 +103,7 @@ The available settings are: | `captureArguments` | Boolean | `false` | Enable the capture of Logback logger arguments. | | `captureLogstashAttributes` | Boolean | `false` | Enable the capture of Logstash attributes, supported are those added to logs via `Markers.append()`, `Markers.appendEntries()`, `Markers.appendArray()` and `Markers.appendRaw()` methods. | | `captureMdcAttributes` | String | | Comma separated list of MDC attributes to capture. Use the wildcard character `*` to capture all attributes. | +| `captureEventName` | Boolean | `false` | Enable the capture of the log event name from the `event.name` attribute (captured via one of the above means). When true, the `event.name` attribute will be used as the log event name, and the `event.name` attribute will be removed. | | `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 ede0a1ebe010..19dc01bf398a 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 @@ -38,6 +38,7 @@ public class OpenTelemetryAppender extends UnsynchronizedAppenderBase captureMdcAttributes = emptyList(); + private boolean captureEventName = false; private volatile OpenTelemetry openTelemetry; private LoggingEventMapper mapper; @@ -88,6 +89,7 @@ public void start() { .setCaptureLoggerContext(captureLoggerContext) .setCaptureArguments(captureArguments) .setCaptureLogstashAttributes(captureLogstashAttributes) + .setCaptureEventName(captureEventName) .build(); eventsToReplay = new ArrayBlockingQueue<>(numLogsCapturedBeforeOtelInstall); super.start(); @@ -200,6 +202,22 @@ public void setCaptureMdcAttributes(String attributes) { } } + /** + * Sets whether the value of the {@code event.name} attribute is used as the log event name. + * + *

The {@code event.name} attribute is captured via any other mechanism supported by this + * appender, such as when {@code captureKeyValuePairAttributes} is true. + * + *

When {@code captureEventName} is true, then the value of the {@code event.name} attribute + * will be used as the log event name, and {@code event.name} attribute will be removed. + * + * @param captureEventName to enable or disable capturing the {@code event.name} attribute as the + * log event name + */ + public void setCaptureEventName(boolean captureEventName) { + this.captureEventName = captureEventName; + } + /** * Log telemetry is emitted after the initialization of the OpenTelemetry Logback appender with an * {@link OpenTelemetry} object. This setting allows you to modify the size of the cache used to 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 0819c73a93d1..6fe76b3c2dbf 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,8 @@ public final class LoggingEventMapper { // copied from ThreadIncubatingAttributes private static final AttributeKey THREAD_ID = AttributeKey.longKey("thread.id"); private static final AttributeKey THREAD_NAME = AttributeKey.stringKey("thread.name"); + // copied from EventIncubatingAttributes + private static final AttributeKey EVENT_NAME = AttributeKey.stringKey("event.name"); private static final boolean supportsInstant = supportsInstant(); private static final boolean supportsKeyValuePairs = supportsKeyValuePairs(); @@ -84,6 +86,7 @@ public final class LoggingEventMapper { private final boolean captureLoggerContext; private final boolean captureArguments; private final boolean captureLogstashAttributes; + private final boolean captureEventName; private LoggingEventMapper(Builder builder) { this.captureExperimentalAttributes = builder.captureExperimentalAttributes; @@ -96,6 +99,7 @@ private LoggingEventMapper(Builder builder) { this.captureLogstashAttributes = builder.captureLogstashAttributes; this.captureAllMdcAttributes = builder.captureMdcAttributes.size() == 1 && builder.captureMdcAttributes.get(0).equals("*"); + this.captureEventName = builder.captureEventName; } public static Builder builder() { @@ -213,8 +217,21 @@ private void mapLoggingEvent( if (supportsLogstashMarkers && captureLogstashAttributes) { captureLogstashAttributes(attributes, loggingEvent); } - - builder.setAllAttributes(attributes.build()); + Attributes realizedAttributes = attributes.build(); + if (captureEventName) { + realizedAttributes.forEach( + (attributeKey, value) -> { + if (attributeKey.equals(EVENT_NAME)) { + builder.setEventName(String.valueOf(value)); + } else { + @SuppressWarnings("unchecked") + AttributeKey attributeKeyAsObject = (AttributeKey) attributeKey; + builder.setAttribute(attributeKeyAsObject, value); + } + }); + } else { + builder.setAllAttributes(realizedAttributes); + } // span context builder.setContext(Context.current()); @@ -642,6 +659,7 @@ public static final class Builder { private boolean captureLoggerContext; private boolean captureArguments; private boolean captureLogstashAttributes; + private boolean captureEventName; Builder() {} @@ -693,6 +711,12 @@ public Builder setCaptureLogstashAttributes(boolean captureLogstashAttributes) { return this; } + @CanIgnoreReturnValue + public Builder setCaptureEventName(boolean captureEventName) { + this.captureEventName = captureEventName; + 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 a71e8460193b..a8ae2ab74fde 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 @@ -54,6 +54,7 @@ void keyValue() { .addKeyValue("long key", 4L) .addKeyValue("float key", 5.0f) .addKeyValue("double key", 6.0) + .addKeyValue("event.name", "MyEventName") .log(); testing.waitAndAssertLogRecords( @@ -63,6 +64,7 @@ void keyValue() { .hasInstrumentationScope(instrumentationScopeInfo) .hasBody("log message 1") .hasTotalAttributeCount(codeAttributesLogCount() + 8) // 8 key value pairs + .hasEventName("MyEventName") .hasAttributesSatisfying( equalTo(AttributeKey.stringKey("string key"), "string value"), equalTo(AttributeKey.booleanKey("boolean key"), true), @@ -140,6 +142,7 @@ void logstash() { .atInfo() .setMessage("log message 1") .addMarker(Markers.append("field1", "value1")) + .addMarker(Markers.append("event.name", "MyEventName")) .addMarker(Markers.appendEntries(entries)) .log(); @@ -150,6 +153,7 @@ void logstash() { .hasInstrumentationScope(instrumentationScopeInfo) .hasBody("log message 1") .hasTotalAttributeCount(codeAttributesLogCount() + 3) // 3 markers + .hasEventName("MyEventName") .hasAttributesSatisfying( equalTo(AttributeKey.stringKey("field1"), "value1"), equalTo(AttributeKey.longKey("field2"), 2L), 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 aa3a4517bd73..60a3589281bf 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 @@ -17,6 +17,7 @@ true true * + true diff --git a/instrumentation/logback/logback-appender-1.0/library/src/test/java/io/opentelemetry/instrumentation/logback/appender/v1_0/AbstractOpenTelemetryAppenderTest.java b/instrumentation/logback/logback-appender-1.0/library/src/test/java/io/opentelemetry/instrumentation/logback/appender/v1_0/AbstractOpenTelemetryAppenderTest.java index ca91e1cb4744..08dbec0f075d 100644 --- a/instrumentation/logback/logback-appender-1.0/library/src/test/java/io/opentelemetry/instrumentation/logback/appender/v1_0/AbstractOpenTelemetryAppenderTest.java +++ b/instrumentation/logback/logback-appender-1.0/library/src/test/java/io/opentelemetry/instrumentation/logback/appender/v1_0/AbstractOpenTelemetryAppenderTest.java @@ -142,6 +142,7 @@ void logWithExtras() { void logContextData() { MDC.put("key1", "val1"); MDC.put("key2", "val2"); + MDC.put("event.name", "MyEventName"); try { logger.info("log message 1"); } finally { @@ -158,6 +159,7 @@ void logContextData() { .hasInstrumentationScope(instrumentationScopeInfo) .hasBody("log message 1") .hasTotalAttributeCount(2 + codeAttributesLogCount()) // code attributes + .hasEventName("MyEventName") .hasAttributesSatisfying( equalTo(AttributeKey.stringKey("key1"), "val1"), equalTo(AttributeKey.stringKey("key2"), "val2"))); diff --git a/instrumentation/logback/logback-appender-1.0/library/src/test/resources/logback-test.xml b/instrumentation/logback/logback-appender-1.0/library/src/test/resources/logback-test.xml index 7bc078e69050..bf0d82349833 100644 --- a/instrumentation/logback/logback-appender-1.0/library/src/test/resources/logback-test.xml +++ b/instrumentation/logback/logback-appender-1.0/library/src/test/resources/logback-test.xml @@ -16,6 +16,7 @@ true * 1 + true From 85df2645b22111b58b13e0fb9c8c0a94323ef435 Mon Sep 17 00:00:00 2001 From: Phil Clay Date: Tue, 16 Sep 2025 14:02:28 -0600 Subject: [PATCH 2/9] Correct default value of capture-event-name in readme --- instrumentation/jboss-logmanager/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/jboss-logmanager/README.md b/instrumentation/jboss-logmanager/README.md index 67de476c590b..c066b6a8100c 100644 --- a/instrumentation/jboss-logmanager/README.md +++ b/instrumentation/jboss-logmanager/README.md @@ -4,4 +4,4 @@ |-----------------------------------------------------------------------------|---------|---------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `otel.instrumentation.jboss-logmanager.experimental-log-attributes` | Boolean | `false` | Enable the capture of experimental log attributes `thread.name` and `thread.id`. | | `otel.instrumentation.jboss-logmanager.experimental.capture-mdc-attributes` | String | | Comma separated list of MDC attributes to capture. Use the wildcard character `*` to capture all attributes. | -| `otel.instrumentation.jboss-logmanager.experimental.capture-event-name` | Boolean | `true` | Enable the capture of the log event name from the `event.name` attribute (captured via one of the above means). When true, the `event.name` attribute will be used as the log event name, and the `event.name` attribute will be removed. | +| `otel.instrumentation.jboss-logmanager.experimental.capture-event-name` | Boolean | `false` | Enable the capture of the log event name from the `event.name` attribute (captured via one of the above means). When true, the `event.name` attribute will be used as the log event name, and the `event.name` attribute will be removed. | From 7baa040f8d9d18a14cbf305b34cd68ed74f02f04 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Tue, 16 Sep 2025 13:49:19 -0700 Subject: [PATCH 3/9] Simplify --- .../appender/v1_1/LoggingEventMapper.java | 40 ++++++------------- 1 file changed, 12 insertions(+), 28 deletions(-) diff --git a/instrumentation/jboss-logmanager/jboss-logmanager-appender-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jbosslogmanager/appender/v1_1/LoggingEventMapper.java b/instrumentation/jboss-logmanager/jboss-logmanager-appender-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jbosslogmanager/appender/v1_1/LoggingEventMapper.java index bdc7ab8ae89b..4adb3231c5cf 100644 --- a/instrumentation/jboss-logmanager/jboss-logmanager-appender-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jbosslogmanager/appender/v1_1/LoggingEventMapper.java +++ b/instrumentation/jboss-logmanager/jboss-logmanager-appender-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jbosslogmanager/appender/v1_1/LoggingEventMapper.java @@ -10,8 +10,6 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.common.AttributeKey; -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.api.incubator.logs.ExtendedLogRecordBuilder; import io.opentelemetry.api.logs.LogRecordBuilder; import io.opentelemetry.api.logs.Severity; @@ -31,7 +29,7 @@ public final class LoggingEventMapper { public static final LoggingEventMapper INSTANCE = new LoggingEventMapper(); // copied from EventIncubatingAttributes - private static final AttributeKey EVENT_NAME = AttributeKey.stringKey("event.name"); + private static final String EVENT_NAME = "event.name"; private static final Cache> mdcAttributeKeys = Cache.bounded(100); private final List captureMdcAttributes; @@ -82,35 +80,17 @@ public void capture(Logger logger, ExtLogRecord record) { builder.setSeverityText(level.toString()); } - AttributesBuilder attributes = Attributes.builder(); - Throwable throwable = record.getThrown(); if (throwable != null) { // this cast is safe within java agent instrumentation ((ExtendedLogRecordBuilder) builder).setException(throwable); } - captureMdcAttributes(attributes); + captureMdcAttributes(builder); if (captureExperimentalAttributes) { Thread currentThread = Thread.currentThread(); - attributes.put(ThreadIncubatingAttributes.THREAD_NAME, currentThread.getName()); - attributes.put(ThreadIncubatingAttributes.THREAD_ID, currentThread.getId()); - } - - Attributes realizedAttributes = attributes.build(); - if (captureEventName) { - realizedAttributes.forEach( - (attributeKey, value) -> { - if (attributeKey.equals(EVENT_NAME)) { - builder.setEventName(String.valueOf(value)); - } else { - @SuppressWarnings("unchecked") - AttributeKey attributeKeyAsObject = (AttributeKey) attributeKey; - builder.setAttribute(attributeKeyAsObject, value); - } - }); - } else { - builder.setAllAttributes(realizedAttributes); + builder.setAttribute(ThreadIncubatingAttributes.THREAD_NAME, currentThread.getName()); + builder.setAttribute(ThreadIncubatingAttributes.THREAD_ID, currentThread.getId()); } builder.setContext(Context.current()); @@ -119,15 +99,19 @@ public void capture(Logger logger, ExtLogRecord record) { builder.emit(); } - private void captureMdcAttributes(AttributesBuilder attributes) { + private void captureMdcAttributes(LogRecordBuilder builder) { Map context = MDC.copy(); if (captureAllMdcAttributes) { if (context != null) { for (Map.Entry entry : context.entrySet()) { - attributes.put( - getMdcAttributeKey(String.valueOf(entry.getKey())), String.valueOf(entry.getValue())); + if (captureEventName && entry.getKey().equals(EVENT_NAME)) { + builder.setEventName(entry.getValue()); + } else { + builder.setAttribute( + getMdcAttributeKey(String.valueOf(entry.getKey())), String.valueOf(entry.getValue())); + } } } return; @@ -136,7 +120,7 @@ private void captureMdcAttributes(AttributesBuilder attributes) { for (String key : captureMdcAttributes) { Object value = context.get(key); if (value != null) { - attributes.put(key, value.toString()); + builder.setAttribute(key, value.toString()); } } } From 0465fb5852f48b031263446aef3e03830f09533b Mon Sep 17 00:00:00 2001 From: Phil Clay Date: Tue, 16 Sep 2025 19:36:54 -0600 Subject: [PATCH 4/9] add attributes to LogRecordBuilder instead of AttributesBuilder --- .../appender/v1_1/LoggingEventMapper.java | 24 +-- .../v2_17/internal/LogEventMapper.java | 86 +++----- .../v2_17/internal/LogEventMapperTest.java | 76 ++++--- .../v1_0/internal/LoggingEventMapper.java | 196 ++++++++---------- .../v1_0/internal/LoggingEventMapperTest.java | 124 +++++------ 5 files changed, 230 insertions(+), 276 deletions(-) diff --git a/instrumentation/jboss-logmanager/jboss-logmanager-appender-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jbosslogmanager/appender/v1_1/LoggingEventMapper.java b/instrumentation/jboss-logmanager/jboss-logmanager-appender-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jbosslogmanager/appender/v1_1/LoggingEventMapper.java index 4adb3231c5cf..706c3523511f 100644 --- a/instrumentation/jboss-logmanager/jboss-logmanager-appender-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jbosslogmanager/appender/v1_1/LoggingEventMapper.java +++ b/instrumentation/jboss-logmanager/jboss-logmanager-appender-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jbosslogmanager/appender/v1_1/LoggingEventMapper.java @@ -9,12 +9,10 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS; import io.opentelemetry.api.GlobalOpenTelemetry; -import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.incubator.logs.ExtendedLogRecordBuilder; import io.opentelemetry.api.logs.LogRecordBuilder; import io.opentelemetry.api.logs.Severity; import io.opentelemetry.context.Context; -import io.opentelemetry.instrumentation.api.internal.cache.Cache; import io.opentelemetry.javaagent.bootstrap.internal.AgentInstrumentationConfig; import io.opentelemetry.semconv.incubating.ThreadIncubatingAttributes; import java.util.List; @@ -30,7 +28,6 @@ public final class LoggingEventMapper { // copied from EventIncubatingAttributes private static final String EVENT_NAME = "event.name"; - private static final Cache> mdcAttributeKeys = Cache.bounded(100); private final List captureMdcAttributes; @@ -106,12 +103,7 @@ private void captureMdcAttributes(LogRecordBuilder builder) { if (captureAllMdcAttributes) { if (context != null) { for (Map.Entry entry : context.entrySet()) { - if (captureEventName && entry.getKey().equals(EVENT_NAME)) { - builder.setEventName(entry.getValue()); - } else { - builder.setAttribute( - getMdcAttributeKey(String.valueOf(entry.getKey())), String.valueOf(entry.getValue())); - } + setAttributeMaybeEventName(builder, entry.getKey(), String.valueOf(entry.getValue())); } } return; @@ -119,14 +111,18 @@ private void captureMdcAttributes(LogRecordBuilder builder) { for (String key : captureMdcAttributes) { Object value = context.get(key); - if (value != null) { - builder.setAttribute(key, value.toString()); - } + setAttributeMaybeEventName(builder, key, value.toString()); } } - public static AttributeKey getMdcAttributeKey(String key) { - return mdcAttributeKeys.computeIfAbsent(key, AttributeKey::stringKey); + private void setAttributeMaybeEventName(LogRecordBuilder builder, String key, String value) { + if (value != null) { + if (captureEventName && key.equals(EVENT_NAME)) { + builder.setEventName(value); + } else { + builder.setAttribute(key, value); + } + } } private static Severity levelToSeverity(java.util.logging.Level level) { diff --git a/instrumentation/log4j/log4j-appender-2.17/library/src/main/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/internal/LogEventMapper.java b/instrumentation/log4j/log4j-appender-2.17/library/src/main/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/internal/LogEventMapper.java index 334bb4543666..6c5c444d2a2c 100644 --- a/instrumentation/log4j/log4j-appender-2.17/library/src/main/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/internal/LogEventMapper.java +++ b/instrumentation/log4j/log4j-appender-2.17/library/src/main/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/internal/LogEventMapper.java @@ -6,8 +6,6 @@ package io.opentelemetry.instrumentation.log4j.appender.v2_17.internal; import io.opentelemetry.api.common.AttributeKey; -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.api.incubator.logs.ExtendedLogRecordBuilder; import io.opentelemetry.api.logs.LogRecordBuilder; import io.opentelemetry.api.logs.Severity; @@ -43,12 +41,10 @@ public final class LogEventMapper { private static final AttributeKey THREAD_ID = AttributeKey.longKey("thread.id"); private static final AttributeKey THREAD_NAME = AttributeKey.stringKey("thread.name"); // copied from EventIncubatingAttributes - private static final AttributeKey EVENT_NAME = AttributeKey.stringKey("event.name"); + private static final String EVENT_NAME = "event.name"; private static final String SPECIAL_MAP_MESSAGE_ATTRIBUTE = "message"; - private static final Cache> contextDataAttributeKeyCache = - Cache.bounded(100); private static final Cache> mapMessageAttributeKeyCache = Cache.bounded(100); @@ -106,14 +102,12 @@ public void mapLogEvent( Supplier sourceSupplier, Context context) { - AttributesBuilder attributes = Attributes.builder(); - - captureMessage(builder, attributes, message); + captureMessage(builder, message); if (captureMarkerAttribute) { if (marker != null) { String markerName = marker.getName(); - attributes.put(LOG_MARKER, markerName); + builder.setAttribute(LOG_MARKER, markerName); } } @@ -123,14 +117,14 @@ public void mapLogEvent( } if (throwable != null) { - setThrowable(builder, attributes, throwable); + setThrowable(builder, throwable); } - captureContextDataAttributes(attributes, contextData); + captureContextDataAttributes(builder, contextData); if (captureExperimentalAttributes) { - attributes.put(THREAD_NAME, threadName); - attributes.put(THREAD_ID, threadId); + builder.setAttribute(THREAD_NAME, threadName); + builder.setAttribute(THREAD_ID, threadId); } if (captureCodeAttributes) { @@ -139,55 +133,39 @@ public void mapLogEvent( String fileName = source.getFileName(); if (fileName != null) { if (SemconvStability.isEmitStableCodeSemconv()) { - attributes.put(CodeAttributes.CODE_FILE_PATH, fileName); + builder.setAttribute(CodeAttributes.CODE_FILE_PATH, fileName); } if (SemconvStability.isEmitOldCodeSemconv()) { - attributes.put(CODE_FILEPATH, fileName); + builder.setAttribute(CODE_FILEPATH, fileName); } } if (SemconvStability.isEmitStableCodeSemconv()) { - attributes.put( + builder.setAttribute( CodeAttributes.CODE_FUNCTION_NAME, source.getClassName() + "." + source.getMethodName()); } if (SemconvStability.isEmitOldCodeSemconv()) { - attributes.put(CODE_NAMESPACE, source.getClassName()); - attributes.put(CODE_FUNCTION, source.getMethodName()); + builder.setAttribute(CODE_NAMESPACE, source.getClassName()); + builder.setAttribute(CODE_FUNCTION, source.getMethodName()); } int lineNumber = source.getLineNumber(); if (lineNumber > 0) { if (SemconvStability.isEmitStableCodeSemconv()) { - attributes.put(CodeAttributes.CODE_LINE_NUMBER, lineNumber); + builder.setAttribute(CodeAttributes.CODE_LINE_NUMBER, (long) lineNumber); } if (SemconvStability.isEmitOldCodeSemconv()) { - attributes.put(CODE_LINENO, lineNumber); + builder.setAttribute(CODE_LINENO, (long) lineNumber); } } } } - Attributes realizedAttributes = attributes.build(); - if (captureEventName) { - realizedAttributes.forEach( - (attributeKey, value) -> { - if (attributeKey.equals(EVENT_NAME)) { - builder.setEventName(String.valueOf(value)); - } else { - @SuppressWarnings("unchecked") - AttributeKey attributeKeyAsObject = (AttributeKey) attributeKey; - builder.setAttribute(attributeKeyAsObject, value); - } - }); - } else { - builder.setAllAttributes(realizedAttributes); - } - builder.setContext(context); } // visible for testing - void captureMessage(LogRecordBuilder builder, AttributesBuilder attributes, Message message) { + void captureMessage(LogRecordBuilder builder, Message message) { if (message == null) { return; } @@ -216,37 +194,37 @@ void captureMessage(LogRecordBuilder builder, AttributesBuilder attributes, Mess (key, value) -> { if (value != null && (!checkSpecialMapMessageAttribute - || !key.equals(SPECIAL_MAP_MESSAGE_ATTRIBUTE))) { - attributes.put(getMapMessageAttributeKey(key), value.toString()); + || !key.equals(SPECIAL_MAP_MESSAGE_ATTRIBUTE))) { + builder.setAttribute(getMapMessageAttributeKey(key), value.toString()); } }); } } // visible for testing - void captureContextDataAttributes(AttributesBuilder attributes, T contextData) { + void captureContextDataAttributes(LogRecordBuilder builder, T contextData) { if (captureAllContextDataAttributes) { contextDataAccessor.forEach( contextData, - (key, value) -> { - if (value != null) { - attributes.put(getContextDataAttributeKey(key), value); - } - }); + (key, value) -> setAttributeMaybeEventName(builder, key, value)); return; } for (String key : captureContextDataAttributes) { String value = contextDataAccessor.getValue(contextData, key); - if (value != null) { - attributes.put(getContextDataAttributeKey(key), value); - } + setAttributeMaybeEventName(builder, key, value); } } - public static AttributeKey getContextDataAttributeKey(String key) { - return contextDataAttributeKeyCache.computeIfAbsent(key, AttributeKey::stringKey); + private void setAttributeMaybeEventName(LogRecordBuilder builder, String key, String value) { + if (value != null) { + if (captureEventName && key.equals(EVENT_NAME)) { + builder.setEventName(value); + } else { + builder.setAttribute(key, value); + } + } } public static AttributeKey getMapMessageAttributeKey(String key) { @@ -255,15 +233,15 @@ public static AttributeKey getMapMessageAttributeKey(String key) { } private static void setThrowable( - LogRecordBuilder builder, AttributesBuilder attributes, Throwable throwable) { + LogRecordBuilder builder, Throwable throwable) { if (builder instanceof ExtendedLogRecordBuilder) { ((ExtendedLogRecordBuilder) builder).setException(throwable); } else { - attributes.put(ExceptionAttributes.EXCEPTION_TYPE, throwable.getClass().getName()); - attributes.put(ExceptionAttributes.EXCEPTION_MESSAGE, throwable.getMessage()); + builder.setAttribute(ExceptionAttributes.EXCEPTION_TYPE, throwable.getClass().getName()); + builder.setAttribute(ExceptionAttributes.EXCEPTION_MESSAGE, throwable.getMessage()); StringWriter writer = new StringWriter(); throwable.printStackTrace(new PrintWriter(writer)); - attributes.put(ExceptionAttributes.EXCEPTION_STACKTRACE, writer.toString()); + builder.setAttribute(ExceptionAttributes.EXCEPTION_STACKTRACE, writer.toString()); } } diff --git a/instrumentation/log4j/log4j-appender-2.17/library/src/test/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/internal/LogEventMapperTest.java b/instrumentation/log4j/log4j-appender-2.17/library/src/test/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/internal/LogEventMapperTest.java index 7d7de1c64c1d..00fd90624eaa 100644 --- a/instrumentation/log4j/log4j-appender-2.17/library/src/test/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/internal/LogEventMapperTest.java +++ b/instrumentation/log4j/log4j-appender-2.17/library/src/test/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/internal/LogEventMapperTest.java @@ -5,17 +5,16 @@ package io.opentelemetry.instrumentation.log4j.appender.v2_17.internal; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.attributeEntry; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.verifyNoMoreInteractions; -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.logs.LogRecordBuilder; import java.util.HashMap; import java.util.Map; @@ -36,13 +35,13 @@ void testDefault() { Map contextData = new HashMap<>(); contextData.put("key1", "value1"); contextData.put("key2", "value2"); - AttributesBuilder attributes = Attributes.builder(); + LogRecordBuilder builder = mock(LogRecordBuilder.class); // when - mapper.captureContextDataAttributes(attributes, contextData); + mapper.captureContextDataAttributes(builder, contextData); // then - assertThat(attributes.build()).isEmpty(); + verifyNoInteractions(builder); } @Test @@ -60,13 +59,14 @@ void testSome() { Map contextData = new HashMap<>(); contextData.put("key1", "value1"); contextData.put("key2", "value2"); - AttributesBuilder attributes = Attributes.builder(); + LogRecordBuilder builder = mock(LogRecordBuilder.class); // when - mapper.captureContextDataAttributes(attributes, contextData); + mapper.captureContextDataAttributes(builder, contextData); // then - assertThat(attributes.build()).containsOnly(attributeEntry("key2", "value2")); + verify(builder).setAttribute("key2", "value2"); + verifyNoMoreInteractions(builder); } @Test @@ -84,14 +84,15 @@ void testAll() { Map contextData = new HashMap<>(); contextData.put("key1", "value1"); contextData.put("key2", "value2"); - AttributesBuilder attributes = Attributes.builder(); + LogRecordBuilder builder = mock(LogRecordBuilder.class); // when - mapper.captureContextDataAttributes(attributes, contextData); + mapper.captureContextDataAttributes(builder, contextData); // then - assertThat(attributes.build()) - .containsOnly(attributeEntry("key1", "value1"), attributeEntry("key2", "value2")); + verify(builder).setAttribute("key1", "value1"); + verify(builder).setAttribute("key2", "value2"); + verifyNoMoreInteractions(builder); } @Test @@ -111,15 +112,14 @@ void testCaptureMapMessageDisabled() { message.put("key1", "value1"); message.put("message", "value2"); - LogRecordBuilder logRecordBuilder = mock(LogRecordBuilder.class); - AttributesBuilder attributes = Attributes.builder(); + LogRecordBuilder builder = mock(LogRecordBuilder.class); // when - mapper.captureMessage(logRecordBuilder, attributes, message); + mapper.captureMessage(builder, message); // then - verify(logRecordBuilder).setBody("value2"); - assertThat(attributes.build()).isEmpty(); + verify(builder).setBody("value2"); + verifyNoMoreInteractions(builder); } @Test @@ -133,15 +133,15 @@ void testCaptureMapMessageWithSpecialAttribute() { message.put("key1", "value1"); message.put("message", "value2"); - LogRecordBuilder logRecordBuilder = mock(LogRecordBuilder.class); - AttributesBuilder attributes = Attributes.builder(); + LogRecordBuilder builder = mock(LogRecordBuilder.class); // when - mapper.captureMessage(logRecordBuilder, attributes, message); + mapper.captureMessage(builder, message); // then - verify(logRecordBuilder).setBody("value2"); - assertThat(attributes.build()).containsOnly(attributeEntry("log4j.map_message.key1", "value1")); + verify(builder).setBody("value2"); + verify(builder).setAttribute(AttributeKey.stringKey("log4j.map_message.key1"), "value1"); + verifyNoMoreInteractions(builder); } @Test @@ -155,18 +155,16 @@ void testCaptureMapMessageWithoutSpecialAttribute() { message.put("key1", "value1"); message.put("key2", "value2"); - LogRecordBuilder logRecordBuilder = mock(LogRecordBuilder.class); - AttributesBuilder attributes = Attributes.builder(); + LogRecordBuilder builder = mock(LogRecordBuilder.class); // when - mapper.captureMessage(logRecordBuilder, attributes, message); + mapper.captureMessage(builder, message); // then - verify(logRecordBuilder, never()).setBody(anyString()); - assertThat(attributes.build()) - .containsOnly( - attributeEntry("log4j.map_message.key1", "value1"), - attributeEntry("log4j.map_message.key2", "value2")); + verify(builder, never()).setBody(anyString()); + verify(builder).setAttribute(AttributeKey.stringKey("log4j.map_message.key1"), "value1"); + verify(builder).setAttribute(AttributeKey.stringKey("log4j.map_message.key2"), "value2"); + verifyNoMoreInteractions(builder); } @Test @@ -180,18 +178,16 @@ void testCaptureStructuredDataMessage() { message.put("key1", "value1"); message.put("message", "value2"); - LogRecordBuilder logRecordBuilder = mock(LogRecordBuilder.class); - AttributesBuilder attributes = Attributes.builder(); + LogRecordBuilder builder = mock(LogRecordBuilder.class); // when - mapper.captureMessage(logRecordBuilder, attributes, message); + mapper.captureMessage(builder, message); // then - verify(logRecordBuilder).setBody("a message"); - assertThat(attributes.build()) - .containsOnly( - attributeEntry("log4j.map_message.key1", "value1"), - attributeEntry("log4j.map_message.message", "value2")); + verify(builder).setBody("a message"); + verify(builder).setAttribute(AttributeKey.stringKey("log4j.map_message.key1"), "value1"); + verify(builder).setAttribute(AttributeKey.stringKey("log4j.map_message.message"), "value2"); + verifyNoMoreInteractions(builder); } private enum ContextDataAccessorImpl implements ContextDataAccessor> { 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 6fe76b3c2dbf..dc3f735874e5 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 @@ -15,15 +15,12 @@ import ch.qos.logback.classic.spi.ThrowableProxy; import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.common.AttributeKey; -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.api.incubator.logs.ExtendedLogRecordBuilder; import io.opentelemetry.api.logs.LogRecordBuilder; import io.opentelemetry.api.logs.LoggerProvider; import io.opentelemetry.api.logs.Severity; import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.api.internal.SemconvStability; -import io.opentelemetry.instrumentation.api.internal.cache.Cache; import io.opentelemetry.javaagent.tooling.muzzle.NoMuzzle; import io.opentelemetry.semconv.ExceptionAttributes; import java.io.PrintWriter; @@ -33,6 +30,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -61,14 +59,12 @@ public final class LoggingEventMapper { private static final AttributeKey THREAD_ID = AttributeKey.longKey("thread.id"); private static final AttributeKey THREAD_NAME = AttributeKey.stringKey("thread.name"); // copied from EventIncubatingAttributes - private static final AttributeKey EVENT_NAME = AttributeKey.stringKey("event.name"); + private static final String EVENT_NAME = "event.name"; private static final boolean supportsInstant = supportsInstant(); private static final boolean supportsKeyValuePairs = supportsKeyValuePairs(); private static final boolean supportsMultipleMarkers = supportsMultipleMarkers(); private static final boolean supportsLogstashMarkers = supportsLogstashMarkers(); - private static final Cache> mdcAttributeKeys = Cache.bounded(100); - private static final Cache> attributeKeys = Cache.bounded(100); private static final AttributeKey> LOG_MARKER = AttributeKey.stringArrayKey("logback.marker"); @@ -141,8 +137,6 @@ private void mapLoggingEvent( builder.setSeverityText(level.levelStr); } - AttributesBuilder attributes = Attributes.builder(); - // throwable Object throwableProxy = loggingEvent.getThrowableProxy(); Throwable throwable = null; @@ -152,15 +146,15 @@ private void mapLoggingEvent( throwable = ((ThrowableProxy) throwableProxy).getThrowable(); } if (throwable != null) { - setThrowable(builder, attributes, throwable); + setThrowable(builder, throwable); } - captureMdcAttributes(attributes, loggingEvent.getMDCPropertyMap()); + captureMdcAttributes(builder, loggingEvent.getMDCPropertyMap()); if (captureExperimentalAttributes) { - attributes.put(THREAD_NAME, loggingEvent.getThreadName()); + builder.setAttribute(THREAD_NAME, loggingEvent.getThreadName()); if (threadId != -1) { - attributes.put(THREAD_ID, threadId); + builder.setAttribute(THREAD_ID, threadId); } } @@ -173,23 +167,23 @@ private void mapLoggingEvent( if (SemconvStability.isEmitOldCodeSemconv()) { if (fileName != null) { - attributes.put(CODE_FILEPATH, fileName); + builder.setAttribute(CODE_FILEPATH, fileName); } - attributes.put(CODE_NAMESPACE, firstStackElement.getClassName()); - attributes.put(CODE_FUNCTION, firstStackElement.getMethodName()); + builder.setAttribute(CODE_NAMESPACE, firstStackElement.getClassName()); + builder.setAttribute(CODE_FUNCTION, firstStackElement.getMethodName()); if (lineNumber > 0) { - attributes.put(CODE_LINENO, lineNumber); + builder.setAttribute(CODE_LINENO, (long) lineNumber); } } if (SemconvStability.isEmitStableCodeSemconv()) { if (fileName != null) { - attributes.put(CODE_FILE_PATH, fileName); + builder.setAttribute(CODE_FILE_PATH, fileName); } - attributes.put( + builder.setAttribute( CODE_FUNCTION_NAME, firstStackElement.getClassName() + "." + firstStackElement.getMethodName()); if (lineNumber > 0) { - attributes.put(CODE_LINE_NUMBER, lineNumber); + builder.setAttribute(CODE_LINE_NUMBER, (long) lineNumber); } } } @@ -197,42 +191,26 @@ private void mapLoggingEvent( if (captureMarkerAttribute) { boolean skipLogstashMarkers = supportsLogstashMarkers && captureLogstashAttributes; - captureMarkerAttribute(attributes, loggingEvent, skipLogstashMarkers); + captureMarkerAttribute(builder, loggingEvent, skipLogstashMarkers); } if (supportsKeyValuePairs && captureKeyValuePairAttributes) { - captureKeyValuePairAttributes(attributes, loggingEvent); + captureKeyValuePairAttributes(builder, loggingEvent); } if (captureLoggerContext) { - captureLoggerContext(attributes, loggingEvent.getLoggerContextVO().getPropertyMap()); + captureLoggerContext(builder, loggingEvent.getLoggerContextVO().getPropertyMap()); } if (captureArguments && loggingEvent.getArgumentArray() != null && loggingEvent.getArgumentArray().length > 0) { - captureArguments(attributes, loggingEvent.getMessage(), loggingEvent.getArgumentArray()); + captureArguments(builder, loggingEvent.getMessage(), loggingEvent.getArgumentArray()); } if (supportsLogstashMarkers && captureLogstashAttributes) { - captureLogstashAttributes(attributes, loggingEvent); - } - Attributes realizedAttributes = attributes.build(); - if (captureEventName) { - realizedAttributes.forEach( - (attributeKey, value) -> { - if (attributeKey.equals(EVENT_NAME)) { - builder.setEventName(String.valueOf(value)); - } else { - @SuppressWarnings("unchecked") - AttributeKey attributeKeyAsObject = (AttributeKey) attributeKey; - builder.setAttribute(attributeKeyAsObject, value); - } - }); - } else { - builder.setAllAttributes(realizedAttributes); + captureLogstashAttributes(builder, loggingEvent); } - // span context builder.setContext(Context.current()); } @@ -260,43 +238,37 @@ private static void setTimestampFromInstant( } // visible for testing - void captureMdcAttributes(AttributesBuilder attributes, Map mdcProperties) { + void captureMdcAttributes(LogRecordBuilder builder, Map mdcProperties) { if (captureAllMdcAttributes) { for (Map.Entry entry : mdcProperties.entrySet()) { - attributes.put(getMdcAttributeKey(entry.getKey()), entry.getValue()); + setAttributeMaybeEventName(builder, entry.getKey(), entry.getValue()); } return; } for (String key : captureMdcAttributes) { String value = mdcProperties.get(key); - if (value != null) { - attributes.put(getMdcAttributeKey(key), value); - } + setAttributeMaybeEventName(builder, key, value); } } - void captureArguments(AttributesBuilder attributes, String message, Object[] arguments) { - attributes.put(LOG_BODY_TEMPLATE, message); - attributes.put( + void captureArguments(LogRecordBuilder builder, String message, Object[] arguments) { + builder.setAttribute(LOG_BODY_TEMPLATE, message); + builder.setAttribute( LOG_BODY_PARAMETERS, Arrays.stream(arguments).map(String::valueOf).collect(Collectors.toList())); } - public static AttributeKey getMdcAttributeKey(String key) { - return mdcAttributeKeys.computeIfAbsent(key, AttributeKey::stringKey); - } - private static void setThrowable( - LogRecordBuilder builder, AttributesBuilder attributes, Throwable throwable) { + LogRecordBuilder builder, Throwable throwable) { if (builder instanceof ExtendedLogRecordBuilder) { ((ExtendedLogRecordBuilder) builder).setException(throwable); } else { - attributes.put(ExceptionAttributes.EXCEPTION_TYPE, throwable.getClass().getName()); - attributes.put(ExceptionAttributes.EXCEPTION_MESSAGE, throwable.getMessage()); + builder.setAttribute(ExceptionAttributes.EXCEPTION_TYPE, throwable.getClass().getName()); + builder.setAttribute(ExceptionAttributes.EXCEPTION_MESSAGE, throwable.getMessage()); StringWriter writer = new StringWriter(); throwable.printStackTrace(new PrintWriter(writer)); - attributes.put(ExceptionAttributes.EXCEPTION_STACKTRACE, writer.toString()); + builder.setAttribute(ExceptionAttributes.EXCEPTION_STACKTRACE, writer.toString()); } } @@ -320,35 +292,36 @@ private static Severity levelToSeverity(Level level) { } @NoMuzzle - private static void captureKeyValuePairAttributes( - AttributesBuilder attributes, ILoggingEvent loggingEvent) { + private void captureKeyValuePairAttributes( + LogRecordBuilder builder, ILoggingEvent loggingEvent) { List keyValuePairs = loggingEvent.getKeyValuePairs(); if (keyValuePairs != null) { for (KeyValuePair keyValuePair : keyValuePairs) { - captureAttribute(attributes, keyValuePair.key, keyValuePair.value); + captureAttribute(builder, this.captureEventName, keyValuePair.key, keyValuePair.value); } } } // visible for testing - static void captureAttribute(AttributesBuilder attributes, Object key, Object value) { + static void captureAttribute(LogRecordBuilder builder, boolean captureEventName, Object key, + Object value) { // empty values are not serialized if (key != null && value != null) { String keyStr = key.toString(); // preserve type for boolean and numeric values, everything else is converted to String if (value instanceof Boolean) { - attributes.put(keyStr, (Boolean) value); + builder.setAttribute(keyStr, (Boolean) value); } else if (value instanceof Byte || value instanceof Integer || value instanceof Long || value instanceof Short) { - attributes.put(keyStr, ((Number) value).longValue()); + builder.setAttribute(keyStr, ((Number) value).longValue()); } else if (value instanceof Double || value instanceof Float) { - attributes.put(keyStr, ((Number) value).doubleValue()); + builder.setAttribute(keyStr, ((Number) value).doubleValue()); } else if (value.getClass().isArray()) { if (value instanceof boolean[] || value instanceof Boolean[]) { captureArrayValueAttribute( - attributes, AttributeKey.booleanArrayKey(keyStr), value, o -> (Boolean) o); + builder, AttributeKey.booleanArrayKey(keyStr), value, o -> (Boolean) o); } else if (value instanceof byte[] || value instanceof Byte[] || value instanceof int[] @@ -358,34 +331,49 @@ static void captureAttribute(AttributesBuilder attributes, Object key, Object va || value instanceof short[] || value instanceof Short[]) { captureArrayValueAttribute( - attributes, AttributeKey.longArrayKey(keyStr), value, o -> ((Number) o).longValue()); + builder, AttributeKey.longArrayKey(keyStr), value, o -> ((Number) o).longValue()); } else if (value instanceof float[] || value instanceof Float[] || value instanceof double[] || value instanceof Double[]) { captureArrayValueAttribute( - attributes, + builder, AttributeKey.doubleArrayKey(keyStr), value, o -> ((Number) o).doubleValue()); } else { captureArrayValueAttribute( - attributes, AttributeKey.stringArrayKey(keyStr), value, String::valueOf); + builder, AttributeKey.stringArrayKey(keyStr), value, String::valueOf); } } else if (value instanceof Collection) { captureArrayValueAttribute( - attributes, + builder, AttributeKey.stringArrayKey(keyStr), ((Collection) value).toArray(), String::valueOf); } else { - attributes.put(getAttributeKey(keyStr), String.valueOf(value)); + setAttributeMaybeEventName(builder, captureEventName, keyStr, String.valueOf(value)); + } + } + } + + private void setAttributeMaybeEventName(LogRecordBuilder builder, String key, String value) { + setAttributeMaybeEventName(builder, this.captureEventName, key, value); + } + + private static void setAttributeMaybeEventName(LogRecordBuilder builder, boolean captureEventName, + String key, String value) { + if (value != null) { + if (captureEventName && key.equals(EVENT_NAME)) { + builder.setEventName(value); + } else { + builder.setAttribute(key, value); } } } private static void captureArrayValueAttribute( - AttributesBuilder attributes, + LogRecordBuilder builder, AttributeKey> key, Object array, Function extractor) { @@ -399,21 +387,17 @@ private static void captureArrayValueAttribute( } // empty lists are not serialized if (!list.isEmpty()) { - attributes.put(key, list); + builder.setAttribute(key, list); } } - private static void captureLoggerContext( - AttributesBuilder attributes, Map loggerContextProperties) { + private void captureLoggerContext( + LogRecordBuilder builder, Map loggerContextProperties) { for (Map.Entry entry : loggerContextProperties.entrySet()) { - attributes.put(getAttributeKey(entry.getKey()), entry.getValue()); + setAttributeMaybeEventName(builder, entry.getKey(), entry.getValue()); } } - public static AttributeKey getAttributeKey(String key) { - return attributeKeys.computeIfAbsent(key, AttributeKey::stringKey); - } - private static boolean supportsKeyValuePairs() { try { Class.forName("org.slf4j.event.KeyValuePair"); @@ -430,26 +414,26 @@ private static boolean supportsKeyValuePairs() { } private static void captureMarkerAttribute( - AttributesBuilder attributes, ILoggingEvent loggingEvent, boolean skipLogstashMarkers) { + LogRecordBuilder builder, ILoggingEvent loggingEvent, boolean skipLogstashMarkers) { if (supportsMultipleMarkers && hasMultipleMarkers(loggingEvent)) { - captureMultipleMarkerAttributes(attributes, loggingEvent, skipLogstashMarkers); + captureMultipleMarkerAttributes(builder, loggingEvent, skipLogstashMarkers); } else { - captureSingleMarkerAttribute(attributes, loggingEvent, skipLogstashMarkers); + captureSingleMarkerAttribute(builder, loggingEvent, skipLogstashMarkers); } } @SuppressWarnings("deprecation") // getMarker is deprecate since 1.3.0 private static void captureSingleMarkerAttribute( - AttributesBuilder attributes, ILoggingEvent loggingEvent, boolean skipLogstashMarkers) { + LogRecordBuilder builder, ILoggingEvent loggingEvent, boolean skipLogstashMarkers) { Marker marker = loggingEvent.getMarker(); if (marker != null && (!skipLogstashMarkers || !isLogstashMarker(marker))) { - attributes.put(LOG_MARKER, marker.getName()); + builder.setAttribute(LOG_MARKER, Collections.singletonList(marker.getName())); } } @NoMuzzle private static void captureMultipleMarkerAttributes( - AttributesBuilder attributes, ILoggingEvent loggingEvent, boolean skipLogstashMarkers) { + LogRecordBuilder builder, ILoggingEvent loggingEvent, boolean skipLogstashMarkers) { List markerNames = new ArrayList<>(loggingEvent.getMarkerList().size()); for (Marker marker : loggingEvent.getMarkerList()) { if (!skipLogstashMarkers || !isLogstashMarker(marker)) { @@ -457,7 +441,7 @@ private static void captureMultipleMarkerAttributes( } } if (!markerNames.isEmpty()) { - attributes.put(LOG_MARKER, markerNames.toArray(new String[0])); + builder.setAttribute(LOG_MARKER, markerNames); } } @@ -477,12 +461,12 @@ private static boolean supportsMultipleMarkers() { return true; } - private static void captureLogstashAttributes( - AttributesBuilder attributes, ILoggingEvent loggingEvent) { + private void captureLogstashAttributes( + LogRecordBuilder builder, ILoggingEvent loggingEvent) { if (supportsMultipleMarkers && hasMultipleMarkers(loggingEvent)) { - captureMultipleLogstashAttributes(attributes, loggingEvent); + captureMultipleLogstashAttributes(builder, loggingEvent); } else { - captureSingleLogstashAttribute(attributes, loggingEvent); + captureSingleLogstashAttribute(builder, loggingEvent); } } @@ -492,44 +476,44 @@ private static boolean isLogstashMarker(Marker marker) { } @SuppressWarnings("deprecation") // getMarker is deprecate since 1.3.0 - private static void captureSingleLogstashAttribute( - AttributesBuilder attributes, ILoggingEvent loggingEvent) { + private void captureSingleLogstashAttribute( + LogRecordBuilder builder, ILoggingEvent loggingEvent) { Marker marker = loggingEvent.getMarker(); if (isLogstashMarker(marker)) { - captureLogstashMarker(attributes, marker); + captureLogstashMarker(builder, marker); } } @NoMuzzle - private static void captureMultipleLogstashAttributes( - AttributesBuilder attributes, ILoggingEvent loggingEvent) { + private void captureMultipleLogstashAttributes( + LogRecordBuilder builder, ILoggingEvent loggingEvent) { for (Marker marker : loggingEvent.getMarkerList()) { if (isLogstashMarker(marker)) { - captureLogstashMarker(attributes, marker); + captureLogstashMarker(builder, marker); } } } @NoMuzzle - private static void captureLogstashMarker(AttributesBuilder attributes, Marker marker) { + private void captureLogstashMarker(LogRecordBuilder builder, Marker marker) { LogstashMarker logstashMarker = (LogstashMarker) marker; - captureLogstashMarkerAttributes(attributes, logstashMarker); + captureLogstashMarkerAttributes(builder, logstashMarker); if (logstashMarker.hasReferences()) { for (Iterator it = logstashMarker.iterator(); it.hasNext(); ) { Marker referenceMarker = it.next(); if (isLogstashMarker(referenceMarker)) { - captureLogstashMarker(attributes, referenceMarker); + captureLogstashMarker(builder, referenceMarker); } } } } - private static void captureLogstashMarkerAttributes( - AttributesBuilder attributes, Object logstashMarker) { + private void captureLogstashMarkerAttributes( + LogRecordBuilder builder, Object logstashMarker) { FieldReader fieldReader = LogstashFieldReaderHolder.valueField.get(logstashMarker.getClass()); if (fieldReader != null) { - fieldReader.read(attributes, logstashMarker); + fieldReader.read(builder, logstashMarker, this.captureEventName); } } @@ -568,10 +552,10 @@ private static FieldReader createStringReader(Field field) { if (field == null) { return null; } - return (attributes, logstashMarker) -> { + return (builder, logstashMarker, captureEventName) -> { String fieldName = getSingleFieldAppendingMarkerName(logstashMarker); Object fieldValue = extractFieldValue(field, logstashMarker); - captureAttribute(attributes, fieldName, fieldValue); + captureAttribute(builder, captureEventName, fieldName, fieldValue); }; } @@ -580,14 +564,14 @@ private static FieldReader createMapReader(Field field) { if (field == null) { return null; } - return (attributes, logstashMarker) -> { + return (attributes, logstashMarker, captureEventName) -> { Object fieldValue = extractFieldValue(field, logstashMarker); if (fieldValue instanceof Map) { Map map = (Map) fieldValue; for (Map.Entry entry : map.entrySet()) { Object key = entry.getKey(); Object value = entry.getValue(); - captureAttribute(attributes, key, value); + captureAttribute(attributes, captureEventName, key, value); } } }; @@ -632,7 +616,7 @@ private static boolean supportsLogstashMarkers() { } private interface FieldReader { - void read(AttributesBuilder attributes, Object logstashMarker); + void read(LogRecordBuilder builder, Object logstashMarker, boolean captureEventName); } private static class LogstashFieldReaderHolder { diff --git a/instrumentation/logback/logback-appender-1.0/library/src/test/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapperTest.java b/instrumentation/logback/logback-appender-1.0/library/src/test/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapperTest.java index 2a1ec8e0d96f..fac7f023ecb0 100644 --- a/instrumentation/logback/logback-appender-1.0/library/src/test/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapperTest.java +++ b/instrumentation/logback/logback-appender-1.0/library/src/test/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapperTest.java @@ -5,13 +5,14 @@ package io.opentelemetry.instrumentation.logback.appender.v1_0.internal; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static java.util.Collections.singletonList; -import static org.assertj.core.api.Assertions.entry; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.verifyNoMoreInteractions; import io.opentelemetry.api.common.AttributeKey; -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.api.logs.LogRecordBuilder; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -26,13 +27,13 @@ void testDefault() { Map contextData = new HashMap<>(); contextData.put("key1", "value1"); contextData.put("key2", "value2"); - AttributesBuilder attributes = Attributes.builder(); + LogRecordBuilder builder = mock(LogRecordBuilder.class); // when - mapper.captureMdcAttributes(attributes, contextData); + mapper.captureMdcAttributes(builder, contextData); // then - assertThat(attributes.build()).isEmpty(); + verifyNoInteractions(builder); } @Test @@ -43,13 +44,14 @@ void testSome() { Map contextData = new HashMap<>(); contextData.put("key1", "value1"); contextData.put("key2", "value2"); - AttributesBuilder attributes = Attributes.builder(); + LogRecordBuilder builder = mock(LogRecordBuilder.class); // when - mapper.captureMdcAttributes(attributes, contextData); + mapper.captureMdcAttributes(builder, contextData); // then - assertThat(attributes.build()).containsOnly(entry(AttributeKey.stringKey("key2"), "value2")); + verify(builder).setAttribute("key2", "value2"); + verifyNoMoreInteractions(builder); } @Test @@ -60,65 +62,63 @@ void testAll() { Map contextData = new HashMap<>(); contextData.put("key1", "value1"); contextData.put("key2", "value2"); - AttributesBuilder attributes = Attributes.builder(); + LogRecordBuilder builder = mock(LogRecordBuilder.class); // when - mapper.captureMdcAttributes(attributes, contextData); + mapper.captureMdcAttributes(builder, contextData); // then - assertThat(attributes.build()) - .containsOnly( - entry(AttributeKey.stringKey("key1"), "value1"), - entry(AttributeKey.stringKey("key2"), "value2")); + verify(builder).setAttribute("key1", "value1"); + verify(builder).setAttribute("key2", "value2"); + verifyNoMoreInteractions(builder); } @Test void testCaptureAttributeArray() { - AttributesBuilder builder = Attributes.builder(); - - LoggingEventMapper.captureAttribute(builder, "booleanArray", new boolean[] {true}); - LoggingEventMapper.captureAttribute(builder, "BooleanArray", new Boolean[] {true}); - - LoggingEventMapper.captureAttribute(builder, "byteArray", new byte[] {2}); - LoggingEventMapper.captureAttribute(builder, "ByteArray", new Byte[] {2}); - - LoggingEventMapper.captureAttribute(builder, "shortArray", new short[] {2}); - LoggingEventMapper.captureAttribute(builder, "ShortArray", new Short[] {2}); - - LoggingEventMapper.captureAttribute(builder, "intArray", new int[] {2}); - LoggingEventMapper.captureAttribute(builder, "IntegerArray", new Integer[] {2}); - - LoggingEventMapper.captureAttribute(builder, "longArray", new long[] {2}); - LoggingEventMapper.captureAttribute(builder, "LongArray", new Long[] {2L}); - - LoggingEventMapper.captureAttribute(builder, "floatArray", new float[] {2.0f}); - LoggingEventMapper.captureAttribute(builder, "FloatArray", new Float[] {2.0f}); - - LoggingEventMapper.captureAttribute(builder, "doubleArray", new double[] {2.0}); - LoggingEventMapper.captureAttribute(builder, "DoubleArray", new Double[] {2.0}); - - LoggingEventMapper.captureAttribute(builder, "ObjectArray", new Object[] {"test"}); - LoggingEventMapper.captureAttribute(builder, "List", Collections.singletonList("test")); - LoggingEventMapper.captureAttribute(builder, "Set", Collections.singleton("test")); - - assertThat(builder.build()) - .containsOnly( - entry(AttributeKey.booleanArrayKey("booleanArray"), singletonList(true)), - entry(AttributeKey.booleanArrayKey("BooleanArray"), singletonList(true)), - entry(AttributeKey.longArrayKey("byteArray"), singletonList(2L)), - entry(AttributeKey.longArrayKey("ByteArray"), singletonList(2L)), - entry(AttributeKey.longArrayKey("shortArray"), singletonList(2L)), - entry(AttributeKey.longArrayKey("ShortArray"), singletonList(2L)), - entry(AttributeKey.longArrayKey("intArray"), singletonList(2L)), - entry(AttributeKey.longArrayKey("IntegerArray"), singletonList(2L)), - entry(AttributeKey.longArrayKey("longArray"), singletonList(2L)), - entry(AttributeKey.longArrayKey("LongArray"), singletonList(2L)), - entry(AttributeKey.doubleArrayKey("floatArray"), singletonList(2.0)), - entry(AttributeKey.doubleArrayKey("FloatArray"), singletonList(2.0)), - entry(AttributeKey.doubleArrayKey("doubleArray"), singletonList(2.0)), - entry(AttributeKey.doubleArrayKey("DoubleArray"), singletonList(2.0)), - entry(AttributeKey.stringArrayKey("ObjectArray"), singletonList("test")), - entry(AttributeKey.stringArrayKey("List"), singletonList("test")), - entry(AttributeKey.stringArrayKey("Set"), singletonList("test"))); + LogRecordBuilder builder = mock(LogRecordBuilder.class); + + LoggingEventMapper.captureAttribute(builder, false, "booleanArray", new boolean[] {true}); + LoggingEventMapper.captureAttribute(builder, false, "BooleanArray", new Boolean[] {true}); + + LoggingEventMapper.captureAttribute(builder, false, "byteArray", new byte[] {2}); + LoggingEventMapper.captureAttribute(builder, false, "ByteArray", new Byte[] {2}); + + LoggingEventMapper.captureAttribute(builder, false, "shortArray", new short[] {2}); + LoggingEventMapper.captureAttribute(builder, false, "ShortArray", new Short[] {2}); + + LoggingEventMapper.captureAttribute(builder, false, "intArray", new int[] {2}); + LoggingEventMapper.captureAttribute(builder, false, "IntegerArray", new Integer[] {2}); + + LoggingEventMapper.captureAttribute(builder, false, "longArray", new long[] {2}); + LoggingEventMapper.captureAttribute(builder, false, "LongArray", new Long[] {2L}); + + LoggingEventMapper.captureAttribute(builder, false, "floatArray", new float[] {2.0f}); + LoggingEventMapper.captureAttribute(builder, false, "FloatArray", new Float[] {2.0f}); + + LoggingEventMapper.captureAttribute(builder, false, "doubleArray", new double[] {2.0}); + LoggingEventMapper.captureAttribute(builder, false, "DoubleArray", new Double[] {2.0}); + + LoggingEventMapper.captureAttribute(builder, false, "ObjectArray", new Object[] {"test"}); + LoggingEventMapper.captureAttribute(builder, false, "List", Collections.singletonList("test")); + LoggingEventMapper.captureAttribute(builder, false, "Set", Collections.singleton("test")); + + verify(builder).setAttribute(AttributeKey.booleanArrayKey("booleanArray"), singletonList(true)); + verify(builder).setAttribute(AttributeKey.booleanArrayKey("BooleanArray"), singletonList(true)); + verify(builder).setAttribute(AttributeKey.longArrayKey("byteArray"), singletonList(2L)); + verify(builder).setAttribute(AttributeKey.longArrayKey("ByteArray"), singletonList(2L)); + verify(builder).setAttribute(AttributeKey.longArrayKey("shortArray"), singletonList(2L)); + verify(builder).setAttribute(AttributeKey.longArrayKey("ShortArray"), singletonList(2L)); + verify(builder).setAttribute(AttributeKey.longArrayKey("intArray"), singletonList(2L)); + verify(builder).setAttribute(AttributeKey.longArrayKey("IntegerArray"), singletonList(2L)); + verify(builder).setAttribute(AttributeKey.longArrayKey("longArray"), singletonList(2L)); + verify(builder).setAttribute(AttributeKey.longArrayKey("LongArray"), singletonList(2L)); + verify(builder).setAttribute(AttributeKey.doubleArrayKey("floatArray"), singletonList(2.0)); + verify(builder).setAttribute(AttributeKey.doubleArrayKey("FloatArray"), singletonList(2.0)); + verify(builder).setAttribute(AttributeKey.doubleArrayKey("doubleArray"), singletonList(2.0)); + verify(builder).setAttribute(AttributeKey.doubleArrayKey("DoubleArray"), singletonList(2.0)); + verify(builder).setAttribute(AttributeKey.stringArrayKey("ObjectArray"), singletonList("test")); + verify(builder).setAttribute(AttributeKey.stringArrayKey("List"), singletonList("test")); + verify(builder).setAttribute(AttributeKey.stringArrayKey("Set"), singletonList("test")); + verifyNoMoreInteractions(builder); } } From 54f2c021f5f20978ff8969b9310e9fa88c1a3a88 Mon Sep 17 00:00:00 2001 From: otelbot <197425009+otelbot@users.noreply.github.com> Date: Wed, 17 Sep 2025 01:45:48 +0000 Subject: [PATCH 5/9] ./gradlew spotlessApply --- .../v2_17/internal/LogEventMapper.java | 8 +++--- .../v1_0/internal/LoggingEventMapper.java | 25 +++++++------------ 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/instrumentation/log4j/log4j-appender-2.17/library/src/main/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/internal/LogEventMapper.java b/instrumentation/log4j/log4j-appender-2.17/library/src/main/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/internal/LogEventMapper.java index 6c5c444d2a2c..9333724cefba 100644 --- a/instrumentation/log4j/log4j-appender-2.17/library/src/main/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/internal/LogEventMapper.java +++ b/instrumentation/log4j/log4j-appender-2.17/library/src/main/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/internal/LogEventMapper.java @@ -194,7 +194,7 @@ void captureMessage(LogRecordBuilder builder, Message message) { (key, value) -> { if (value != null && (!checkSpecialMapMessageAttribute - || !key.equals(SPECIAL_MAP_MESSAGE_ATTRIBUTE))) { + || !key.equals(SPECIAL_MAP_MESSAGE_ATTRIBUTE))) { builder.setAttribute(getMapMessageAttributeKey(key), value.toString()); } }); @@ -206,8 +206,7 @@ void captureContextDataAttributes(LogRecordBuilder builder, T contextData) { if (captureAllContextDataAttributes) { contextDataAccessor.forEach( - contextData, - (key, value) -> setAttributeMaybeEventName(builder, key, value)); + contextData, (key, value) -> setAttributeMaybeEventName(builder, key, value)); return; } @@ -232,8 +231,7 @@ public static AttributeKey getMapMessageAttributeKey(String key) { key, k -> AttributeKey.stringKey("log4j.map_message." + k)); } - private static void setThrowable( - LogRecordBuilder builder, Throwable throwable) { + private static void setThrowable(LogRecordBuilder builder, Throwable throwable) { if (builder instanceof ExtendedLogRecordBuilder) { ((ExtendedLogRecordBuilder) builder).setException(throwable); } else { 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 dc3f735874e5..25144ab776ab 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 @@ -259,8 +259,7 @@ void captureArguments(LogRecordBuilder builder, String message, Object[] argumen Arrays.stream(arguments).map(String::valueOf).collect(Collectors.toList())); } - private static void setThrowable( - LogRecordBuilder builder, Throwable throwable) { + private static void setThrowable(LogRecordBuilder builder, Throwable throwable) { if (builder instanceof ExtendedLogRecordBuilder) { ((ExtendedLogRecordBuilder) builder).setException(throwable); } else { @@ -292,8 +291,7 @@ private static Severity levelToSeverity(Level level) { } @NoMuzzle - private void captureKeyValuePairAttributes( - LogRecordBuilder builder, ILoggingEvent loggingEvent) { + private void captureKeyValuePairAttributes(LogRecordBuilder builder, ILoggingEvent loggingEvent) { List keyValuePairs = loggingEvent.getKeyValuePairs(); if (keyValuePairs != null) { for (KeyValuePair keyValuePair : keyValuePairs) { @@ -303,8 +301,8 @@ private void captureKeyValuePairAttributes( } // visible for testing - static void captureAttribute(LogRecordBuilder builder, boolean captureEventName, Object key, - Object value) { + static void captureAttribute( + LogRecordBuilder builder, boolean captureEventName, Object key, Object value) { // empty values are not serialized if (key != null && value != null) { String keyStr = key.toString(); @@ -337,10 +335,7 @@ static void captureAttribute(LogRecordBuilder builder, boolean captureEventName, || value instanceof double[] || value instanceof Double[]) { captureArrayValueAttribute( - builder, - AttributeKey.doubleArrayKey(keyStr), - value, - o -> ((Number) o).doubleValue()); + builder, AttributeKey.doubleArrayKey(keyStr), value, o -> ((Number) o).doubleValue()); } else { captureArrayValueAttribute( builder, AttributeKey.stringArrayKey(keyStr), value, String::valueOf); @@ -361,8 +356,8 @@ private void setAttributeMaybeEventName(LogRecordBuilder builder, String key, St setAttributeMaybeEventName(builder, this.captureEventName, key, value); } - private static void setAttributeMaybeEventName(LogRecordBuilder builder, boolean captureEventName, - String key, String value) { + private static void setAttributeMaybeEventName( + LogRecordBuilder builder, boolean captureEventName, String key, String value) { if (value != null) { if (captureEventName && key.equals(EVENT_NAME)) { builder.setEventName(value); @@ -461,8 +456,7 @@ private static boolean supportsMultipleMarkers() { return true; } - private void captureLogstashAttributes( - LogRecordBuilder builder, ILoggingEvent loggingEvent) { + private void captureLogstashAttributes(LogRecordBuilder builder, ILoggingEvent loggingEvent) { if (supportsMultipleMarkers && hasMultipleMarkers(loggingEvent)) { captureMultipleLogstashAttributes(builder, loggingEvent); } else { @@ -509,8 +503,7 @@ private void captureLogstashMarker(LogRecordBuilder builder, Marker marker) { } } - private void captureLogstashMarkerAttributes( - LogRecordBuilder builder, Object logstashMarker) { + private void captureLogstashMarkerAttributes(LogRecordBuilder builder, Object logstashMarker) { FieldReader fieldReader = LogstashFieldReaderHolder.valueField.get(logstashMarker.getClass()); if (fieldReader != null) { fieldReader.read(builder, logstashMarker, this.captureEventName); From a48b5c050d99854511dc6b28331928ef822aca94 Mon Sep 17 00:00:00 2001 From: Phil Clay Date: Wed, 17 Sep 2025 12:52:29 -0600 Subject: [PATCH 6/9] Address review comments Use cached AttributeKeys for dynamic key names in log mappers. Rename setAttributeMaybeEventName to setAttributeOrEventName Clarify capture event name description in readme --- instrumentation/jboss-logmanager/README.md | 10 +++---- .../appender/v1_1/LoggingEventMapper.java | 21 ++++++++++---- .../log4j-appender-2.17/javaagent/README.md | 16 +++++----- .../log4j-appender-2.17/library/README.md | 18 ++++++------ .../v2_17/internal/LogEventMapper.java | 20 +++++++++---- .../v2_17/internal/LogEventMapperTest.java | 6 ++-- .../logback-appender-1.0/javaagent/README.md | 22 +++++++------- .../logback-appender-1.0/library/README.md | 2 +- .../v1_0/internal/LoggingEventMapper.java | 29 ++++++++++++------- .../v1_0/internal/LoggingEventMapperTest.java | 6 ++-- 10 files changed, 87 insertions(+), 63 deletions(-) diff --git a/instrumentation/jboss-logmanager/README.md b/instrumentation/jboss-logmanager/README.md index c066b6a8100c..e303c2648b8c 100644 --- a/instrumentation/jboss-logmanager/README.md +++ b/instrumentation/jboss-logmanager/README.md @@ -1,7 +1,7 @@ # Settings for the JBoss Log Manager instrumentation -| System property | Type | Default | Description | -|-----------------------------------------------------------------------------|---------|---------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `otel.instrumentation.jboss-logmanager.experimental-log-attributes` | Boolean | `false` | Enable the capture of experimental log attributes `thread.name` and `thread.id`. | -| `otel.instrumentation.jboss-logmanager.experimental.capture-mdc-attributes` | String | | Comma separated list of MDC attributes to capture. Use the wildcard character `*` to capture all attributes. | -| `otel.instrumentation.jboss-logmanager.experimental.capture-event-name` | Boolean | `false` | Enable the capture of the log event name from the `event.name` attribute (captured via one of the above means). When true, the `event.name` attribute will be used as the log event name, and the `event.name` attribute will be removed. | +| System property | Type | Default | Description | +|-----------------------------------------------------------------------------|---------|---------|------------------------------------------------------------------------------------------------------------------------------------| +| `otel.instrumentation.jboss-logmanager.experimental-log-attributes` | Boolean | `false` | Enable the capture of experimental log attributes `thread.name` and `thread.id`. | +| `otel.instrumentation.jboss-logmanager.experimental.capture-mdc-attributes` | String | | Comma separated list of MDC attributes to capture. Use the wildcard character `*` to capture all attributes. | +| `otel.instrumentation.jboss-logmanager.experimental.capture-event-name` | Boolean | `false` | Enable moving the `event.name` attribute (captured by one of the other mechanisms of capturing attributes) to the log event name. | diff --git a/instrumentation/jboss-logmanager/jboss-logmanager-appender-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jbosslogmanager/appender/v1_1/LoggingEventMapper.java b/instrumentation/jboss-logmanager/jboss-logmanager-appender-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jbosslogmanager/appender/v1_1/LoggingEventMapper.java index 706c3523511f..c9df4e328c04 100644 --- a/instrumentation/jboss-logmanager/jboss-logmanager-appender-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jbosslogmanager/appender/v1_1/LoggingEventMapper.java +++ b/instrumentation/jboss-logmanager/jboss-logmanager-appender-1.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jbosslogmanager/appender/v1_1/LoggingEventMapper.java @@ -9,10 +9,12 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS; import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.incubator.logs.ExtendedLogRecordBuilder; import io.opentelemetry.api.logs.LogRecordBuilder; import io.opentelemetry.api.logs.Severity; import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.api.internal.cache.Cache; import io.opentelemetry.javaagent.bootstrap.internal.AgentInstrumentationConfig; import io.opentelemetry.semconv.incubating.ThreadIncubatingAttributes; import java.util.List; @@ -26,8 +28,10 @@ public final class LoggingEventMapper { public static final LoggingEventMapper INSTANCE = new LoggingEventMapper(); + private static final Cache> mdcAttributeKeys = Cache.bounded(100); + // copied from EventIncubatingAttributes - private static final String EVENT_NAME = "event.name"; + private static final AttributeKey EVENT_NAME = AttributeKey.stringKey("event.name"); private final List captureMdcAttributes; @@ -103,7 +107,7 @@ private void captureMdcAttributes(LogRecordBuilder builder) { if (captureAllMdcAttributes) { if (context != null) { for (Map.Entry entry : context.entrySet()) { - setAttributeMaybeEventName(builder, entry.getKey(), String.valueOf(entry.getValue())); + setAttributeOrEventName(builder, getMdcAttributeKey(entry.getKey()), entry.getValue()); } } return; @@ -111,16 +115,21 @@ private void captureMdcAttributes(LogRecordBuilder builder) { for (String key : captureMdcAttributes) { Object value = context.get(key); - setAttributeMaybeEventName(builder, key, value.toString()); + setAttributeOrEventName(builder, getMdcAttributeKey(key), value); } } - private void setAttributeMaybeEventName(LogRecordBuilder builder, String key, String value) { + public static AttributeKey getMdcAttributeKey(String key) { + return mdcAttributeKeys.computeIfAbsent(key, AttributeKey::stringKey); + } + + private void setAttributeOrEventName( + LogRecordBuilder builder, AttributeKey key, Object value) { if (value != null) { if (captureEventName && key.equals(EVENT_NAME)) { - builder.setEventName(value); + builder.setEventName(value.toString()); } else { - builder.setAttribute(key, value); + builder.setAttribute(key, value.toString()); } } } diff --git a/instrumentation/log4j/log4j-appender-2.17/javaagent/README.md b/instrumentation/log4j/log4j-appender-2.17/javaagent/README.md index c794ec7ba612..eb3d5b3771d7 100644 --- a/instrumentation/log4j/log4j-appender-2.17/javaagent/README.md +++ b/instrumentation/log4j/log4j-appender-2.17/javaagent/README.md @@ -1,12 +1,12 @@ # Settings for the Log4j Appender instrumentation -| System property | Type | Default | Description | -|-----------------------------------------------------------------------------------|---------|---------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `otel.instrumentation.log4j-appender.experimental-log-attributes` | Boolean | `false` | Enable the capture of experimental log attributes `thread.name` and `thread.id`. | -| `otel.instrumentation.log4j-appender.experimental.capture-code-attributes` | Boolean | `false` | Enable the capture of [source code attributes]. Note that capturing source code attributes at logging sites might add a performance overhead. | -| `otel.instrumentation.log4j-appender.experimental.capture-map-message-attributes` | Boolean | `false` | Enable the capture of `MapMessage` attributes. | -| `otel.instrumentation.log4j-appender.experimental.capture-marker-attribute` | Boolean | `false` | Enable the capture of Log4j markers as attributes. | -| `otel.instrumentation.log4j-appender.experimental.capture-mdc-attributes` | String | | Comma separated list of context data attributes to capture. Use the wildcard character `*` to capture all attributes. | -| `otel.instrumentation.log4j-appender.experimental.capture-event-name` | Boolean | `false` | Enable the capture of the log event name from the `event.name` attribute (captured via one of the above means). When true, the `event.name` attribute will be used as the log event name, and the `event.name` attribute will be removed. | +| System property | Type | Default | Description | +|-----------------------------------------------------------------------------------|---------|---------|-----------------------------------------------------------------------------------------------------------------------------------------------| +| `otel.instrumentation.log4j-appender.experimental-log-attributes` | Boolean | `false` | Enable the capture of experimental log attributes `thread.name` and `thread.id`. | +| `otel.instrumentation.log4j-appender.experimental.capture-code-attributes` | Boolean | `false` | Enable the capture of [source code attributes]. Note that capturing source code attributes at logging sites might add a performance overhead. | +| `otel.instrumentation.log4j-appender.experimental.capture-map-message-attributes` | Boolean | `false` | Enable the capture of `MapMessage` attributes. | +| `otel.instrumentation.log4j-appender.experimental.capture-marker-attribute` | Boolean | `false` | Enable the capture of Log4j markers as attributes. | +| `otel.instrumentation.log4j-appender.experimental.capture-mdc-attributes` | String | | Comma separated list of context data attributes to capture. Use the wildcard character `*` to capture all attributes. | +| `otel.instrumentation.log4j-appender.experimental.capture-event-name` | Boolean | `false` | Enable moving the `event.name` attribute (captured by one of the other mechanisms of capturing attributes) to the log event name. | [source code attributes]: https://github.com/open-telemetry/semantic-conventions/blob/main/docs/general/attributes.md#source-code-attributes diff --git a/instrumentation/log4j/log4j-appender-2.17/library/README.md b/instrumentation/log4j/log4j-appender-2.17/library/README.md index c2c3fff4503e..c51475b5b116 100644 --- a/instrumentation/log4j/log4j-appender-2.17/library/README.md +++ b/instrumentation/log4j/log4j-appender-2.17/library/README.md @@ -92,14 +92,14 @@ Setting can be configured as XML attributes, for example: The available settings are: -| XML Attribute | Type | Default | Description | -|------------------------------------|---------|---------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `captureExperimentalAttributes` | Boolean | `false` | Enable the capture of experimental log attributes `thread.name` and `thread.id`. | -| `captureCodeAttributes` | Boolean | `false` | Enable the capture of [source code attributes]. Note that capturing source code attributes at logging sites might add a performance overhead. | -| `captureMapMessageAttributes` | Boolean | `false` | Enable the capture of `MapMessage` attributes. | -| `captureMarkerAttribute` | Boolean | `false` | Enable the capture of Log4j markers as attributes. | -| `captureContextDataAttributes` | String | | Comma separated list of context data attributes to capture. Use the wildcard character `*` to capture all attributes. | -| `captureEventName` | Boolean | `false` | Enable the capture of the log event name from the `event.name` attribute (captured via one of the above means). When true, the `event.name` attribute will be used as the log event name, and the `event.name` attribute will be removed. | -| `numLogsCapturedBeforeOtelInstall` | Integer | 1000 | Log telemetry is emitted after the initialization of the OpenTelemetry Log4j appender with an OpenTelemetry object. This setting allows you to modify the size of the cache used to replay the first logs. | +| XML Attribute | Type | Default | Description | +|------------------------------------|---------|---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `captureExperimentalAttributes` | Boolean | `false` | Enable the capture of experimental log attributes `thread.name` and `thread.id`. | +| `captureCodeAttributes` | Boolean | `false` | Enable the capture of [source code attributes]. Note that capturing source code attributes at logging sites might add a performance overhead. | +| `captureMapMessageAttributes` | Boolean | `false` | Enable the capture of `MapMessage` attributes. | +| `captureMarkerAttribute` | Boolean | `false` | Enable the capture of Log4j markers as attributes. | +| `captureContextDataAttributes` | String | | Comma separated list of context data attributes to capture. Use the wildcard character `*` to capture all attributes. | +| `captureEventName` | Boolean | `false` | Enable moving the `event.name` attribute (captured by one of the other mechanisms of capturing attributes) to the log event name. | +| `numLogsCapturedBeforeOtelInstall` | Integer | 1000 | Log telemetry is emitted after the initialization of the OpenTelemetry Log4j appender with an OpenTelemetry object. This setting allows you to modify the size of the cache used to replay the first logs. | [source code attributes]: https://github.com/open-telemetry/semantic-conventions/blob/main/docs/general/attributes.md#source-code-attributes diff --git a/instrumentation/log4j/log4j-appender-2.17/library/src/main/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/internal/LogEventMapper.java b/instrumentation/log4j/log4j-appender-2.17/library/src/main/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/internal/LogEventMapper.java index 9333724cefba..6663107e1074 100644 --- a/instrumentation/log4j/log4j-appender-2.17/library/src/main/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/internal/LogEventMapper.java +++ b/instrumentation/log4j/log4j-appender-2.17/library/src/main/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/internal/LogEventMapper.java @@ -41,10 +41,12 @@ public final class LogEventMapper { private static final AttributeKey THREAD_ID = AttributeKey.longKey("thread.id"); private static final AttributeKey THREAD_NAME = AttributeKey.stringKey("thread.name"); // copied from EventIncubatingAttributes - private static final String EVENT_NAME = "event.name"; + private static final AttributeKey EVENT_NAME = AttributeKey.stringKey("event.name"); private static final String SPECIAL_MAP_MESSAGE_ATTRIBUTE = "message"; + private static final Cache> contextDataAttributeKeyCache = + Cache.bounded(100); private static final Cache> mapMessageAttributeKeyCache = Cache.bounded(100); @@ -206,26 +208,32 @@ void captureContextDataAttributes(LogRecordBuilder builder, T contextData) { if (captureAllContextDataAttributes) { contextDataAccessor.forEach( - contextData, (key, value) -> setAttributeMaybeEventName(builder, key, value)); + contextData, + (key, value) -> setAttributeOrEventName(builder, getContextDataAttributeKey(key), value)); return; } for (String key : captureContextDataAttributes) { String value = contextDataAccessor.getValue(contextData, key); - setAttributeMaybeEventName(builder, key, value); + setAttributeOrEventName(builder, getContextDataAttributeKey(key), value); } } - private void setAttributeMaybeEventName(LogRecordBuilder builder, String key, String value) { + private void setAttributeOrEventName( + LogRecordBuilder builder, AttributeKey key, Object value) { if (value != null) { if (captureEventName && key.equals(EVENT_NAME)) { - builder.setEventName(value); + builder.setEventName(value.toString()); } else { - builder.setAttribute(key, value); + builder.setAttribute(key, value.toString()); } } } + public static AttributeKey getContextDataAttributeKey(String key) { + return contextDataAttributeKeyCache.computeIfAbsent(key, AttributeKey::stringKey); + } + public static AttributeKey getMapMessageAttributeKey(String key) { return mapMessageAttributeKeyCache.computeIfAbsent( key, k -> AttributeKey.stringKey("log4j.map_message." + k)); diff --git a/instrumentation/log4j/log4j-appender-2.17/library/src/test/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/internal/LogEventMapperTest.java b/instrumentation/log4j/log4j-appender-2.17/library/src/test/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/internal/LogEventMapperTest.java index 00fd90624eaa..4309848cf2a0 100644 --- a/instrumentation/log4j/log4j-appender-2.17/library/src/test/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/internal/LogEventMapperTest.java +++ b/instrumentation/log4j/log4j-appender-2.17/library/src/test/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/internal/LogEventMapperTest.java @@ -65,7 +65,7 @@ void testSome() { mapper.captureContextDataAttributes(builder, contextData); // then - verify(builder).setAttribute("key2", "value2"); + verify(builder).setAttribute(AttributeKey.stringKey("key2"), "value2"); verifyNoMoreInteractions(builder); } @@ -90,8 +90,8 @@ void testAll() { mapper.captureContextDataAttributes(builder, contextData); // then - verify(builder).setAttribute("key1", "value1"); - verify(builder).setAttribute("key2", "value2"); + verify(builder).setAttribute(AttributeKey.stringKey("key1"), "value1"); + verify(builder).setAttribute(AttributeKey.stringKey("key2"), "value2"); verifyNoMoreInteractions(builder); } diff --git a/instrumentation/logback/logback-appender-1.0/javaagent/README.md b/instrumentation/logback/logback-appender-1.0/javaagent/README.md index a2e36a3d1d2b..e83844e560db 100644 --- a/instrumentation/logback/logback-appender-1.0/javaagent/README.md +++ b/instrumentation/logback/logback-appender-1.0/javaagent/README.md @@ -1,15 +1,15 @@ # Settings for the Logback Appender instrumentation -| System property | Type | Default | Description | -|----------------------------------------------------------------------------------------|---------|---------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `otel.instrumentation.logback-appender.experimental-log-attributes` | Boolean | `false` | Enable the capture of experimental log attributes `thread.name` and `thread.id`. | -| `otel.instrumentation.logback-appender.experimental.capture-code-attributes` | Boolean | `false` | Enable the capture of [source code attributes]. Note that capturing source code attributes at logging sites might add a performance overhead. | -| `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-logstash-attributes` | Boolean | `false` | Enable the capture of Logstash attributes, supported are those added to logs via `Markers.append()`, `Markers.appendEntries()`, `Markers.appendArray()` and `Markers.appendRaw()` methods. | -| `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. | -| `otel.instrumentation.logback-appender.experimental.capture-event-name` | Boolean | `false` | Enable the capture of the log event name from the `event.name` attribute (captured via one of the above means). When true, the `event.name` attribute will be used as the log event name, and the `event.name` attribute will be removed. | +| System property | Type | Default | Description | +|----------------------------------------------------------------------------------------|---------|---------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `otel.instrumentation.logback-appender.experimental-log-attributes` | Boolean | `false` | Enable the capture of experimental log attributes `thread.name` and `thread.id`. | +| `otel.instrumentation.logback-appender.experimental.capture-code-attributes` | Boolean | `false` | Enable the capture of [source code attributes]. Note that capturing source code attributes at logging sites might add a performance overhead. | +| `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-logstash-attributes` | Boolean | `false` | Enable the capture of Logstash attributes, supported are those added to logs via `Markers.append()`, `Markers.appendEntries()`, `Markers.appendArray()` and `Markers.appendRaw()` methods. | +| `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. | +| `otel.instrumentation.logback-appender.experimental.capture-event-name` | Boolean | `false` | Enable moving the `event.name` attribute (captured by one of the other mechanisms of capturing attributes) to the log event name. | [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/library/README.md b/instrumentation/logback/logback-appender-1.0/library/README.md index 568a6294825f..6de16a108ce5 100644 --- a/instrumentation/logback/logback-appender-1.0/library/README.md +++ b/instrumentation/logback/logback-appender-1.0/library/README.md @@ -103,7 +103,7 @@ The available settings are: | `captureArguments` | Boolean | `false` | Enable the capture of Logback logger arguments. | | `captureLogstashAttributes` | Boolean | `false` | Enable the capture of Logstash attributes, supported are those added to logs via `Markers.append()`, `Markers.appendEntries()`, `Markers.appendArray()` and `Markers.appendRaw()` methods. | | `captureMdcAttributes` | String | | Comma separated list of MDC attributes to capture. Use the wildcard character `*` to capture all attributes. | -| `captureEventName` | Boolean | `false` | Enable the capture of the log event name from the `event.name` attribute (captured via one of the above means). When true, the `event.name` attribute will be used as the log event name, and the `event.name` attribute will be removed. | +| `captureEventName` | Boolean | `false` | Enable moving the `event.name` attribute (captured by one of the other mechanisms of capturing attributes) to the log event name. | | `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/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 25144ab776ab..fe385550d305 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 @@ -21,6 +21,7 @@ import io.opentelemetry.api.logs.Severity; import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.api.internal.SemconvStability; +import io.opentelemetry.instrumentation.api.internal.cache.Cache; import io.opentelemetry.javaagent.tooling.muzzle.NoMuzzle; import io.opentelemetry.semconv.ExceptionAttributes; import java.io.PrintWriter; @@ -59,12 +60,13 @@ public final class LoggingEventMapper { private static final AttributeKey THREAD_ID = AttributeKey.longKey("thread.id"); private static final AttributeKey THREAD_NAME = AttributeKey.stringKey("thread.name"); // copied from EventIncubatingAttributes - private static final String EVENT_NAME = "event.name"; + private static final AttributeKey EVENT_NAME = AttributeKey.stringKey("event.name"); private static final boolean supportsInstant = supportsInstant(); private static final boolean supportsKeyValuePairs = supportsKeyValuePairs(); private static final boolean supportsMultipleMarkers = supportsMultipleMarkers(); private static final boolean supportsLogstashMarkers = supportsLogstashMarkers(); + private static final Cache> attributeKeys = Cache.bounded(100); private static final AttributeKey> LOG_MARKER = AttributeKey.stringArrayKey("logback.marker"); @@ -241,14 +243,14 @@ private static void setTimestampFromInstant( void captureMdcAttributes(LogRecordBuilder builder, Map mdcProperties) { if (captureAllMdcAttributes) { for (Map.Entry entry : mdcProperties.entrySet()) { - setAttributeMaybeEventName(builder, entry.getKey(), entry.getValue()); + setAttributeOrEventName(builder, getAttributeKey(entry.getKey()), entry.getValue()); } return; } for (String key : captureMdcAttributes) { String value = mdcProperties.get(key); - setAttributeMaybeEventName(builder, key, value); + setAttributeOrEventName(builder, getAttributeKey(key), value); } } @@ -347,22 +349,23 @@ static void captureAttribute( ((Collection) value).toArray(), String::valueOf); } else { - setAttributeMaybeEventName(builder, captureEventName, keyStr, String.valueOf(value)); + setAttributeOrEventName(builder, captureEventName, getAttributeKey(keyStr), value); } } } - private void setAttributeMaybeEventName(LogRecordBuilder builder, String key, String value) { - setAttributeMaybeEventName(builder, this.captureEventName, key, value); + private void setAttributeOrEventName( + LogRecordBuilder builder, AttributeKey key, Object value) { + setAttributeOrEventName(builder, this.captureEventName, key, value); } - private static void setAttributeMaybeEventName( - LogRecordBuilder builder, boolean captureEventName, String key, String value) { + private static void setAttributeOrEventName( + LogRecordBuilder builder, boolean captureEventName, AttributeKey key, Object value) { if (value != null) { if (captureEventName && key.equals(EVENT_NAME)) { - builder.setEventName(value); + builder.setEventName(value.toString()); } else { - builder.setAttribute(key, value); + builder.setAttribute(key, value.toString()); } } } @@ -389,10 +392,14 @@ private static void captureArrayValueAttribute( private void captureLoggerContext( LogRecordBuilder builder, Map loggerContextProperties) { for (Map.Entry entry : loggerContextProperties.entrySet()) { - setAttributeMaybeEventName(builder, entry.getKey(), entry.getValue()); + setAttributeOrEventName(builder, getAttributeKey(entry.getKey()), entry.getValue()); } } + public static AttributeKey getAttributeKey(String key) { + return attributeKeys.computeIfAbsent(key, AttributeKey::stringKey); + } + private static boolean supportsKeyValuePairs() { try { Class.forName("org.slf4j.event.KeyValuePair"); diff --git a/instrumentation/logback/logback-appender-1.0/library/src/test/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapperTest.java b/instrumentation/logback/logback-appender-1.0/library/src/test/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapperTest.java index fac7f023ecb0..072f348add64 100644 --- a/instrumentation/logback/logback-appender-1.0/library/src/test/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapperTest.java +++ b/instrumentation/logback/logback-appender-1.0/library/src/test/java/io/opentelemetry/instrumentation/logback/appender/v1_0/internal/LoggingEventMapperTest.java @@ -50,7 +50,7 @@ void testSome() { mapper.captureMdcAttributes(builder, contextData); // then - verify(builder).setAttribute("key2", "value2"); + verify(builder).setAttribute(AttributeKey.stringKey("key2"), "value2"); verifyNoMoreInteractions(builder); } @@ -68,8 +68,8 @@ void testAll() { mapper.captureMdcAttributes(builder, contextData); // then - verify(builder).setAttribute("key1", "value1"); - verify(builder).setAttribute("key2", "value2"); + verify(builder).setAttribute(AttributeKey.stringKey("key1"), "value1"); + verify(builder).setAttribute(AttributeKey.stringKey("key2"), "value2"); verifyNoMoreInteractions(builder); } From 7f0264819025730f3da07b2fd071c79d828e28c2 Mon Sep 17 00:00:00 2001 From: Phil Clay Date: Thu, 18 Sep 2025 09:24:26 -0600 Subject: [PATCH 7/9] Add event naming capturing for log4j-appender-1.2 --- .../log4j-appender-1.2/javaagent/README.md | 10 +++ .../javaagent/build.gradle.kts | 2 +- .../log4j/appender/v1_2/LogEventMapper.java | 61 +++++++++++-------- .../log4j/appender/v1_2/Log4j1Test.java | 3 + 4 files changed, 50 insertions(+), 26 deletions(-) create mode 100644 instrumentation/log4j/log4j-appender-1.2/javaagent/README.md diff --git a/instrumentation/log4j/log4j-appender-1.2/javaagent/README.md b/instrumentation/log4j/log4j-appender-1.2/javaagent/README.md new file mode 100644 index 000000000000..8b8abb4deb7d --- /dev/null +++ b/instrumentation/log4j/log4j-appender-1.2/javaagent/README.md @@ -0,0 +1,10 @@ +# Settings for the Log4j Appender instrumentation + +| System property | Type | Default | Description | +|-----------------------------------------------------------------------------------|---------|---------|-----------------------------------------------------------------------------------------------------------------------------------------------| +| `otel.instrumentation.log4j-appender.experimental-log-attributes` | Boolean | `false` | Enable the capture of experimental log attributes `thread.name` and `thread.id`. | +| `otel.instrumentation.log4j-appender.experimental.capture-code-attributes` | Boolean | `false` | Enable the capture of [source code attributes]. Note that capturing source code attributes at logging sites might add a performance overhead. | +| `otel.instrumentation.log4j-appender.experimental.capture-mdc-attributes` | String | | Comma separated list of context data attributes to capture. Use the wildcard character `*` to capture all attributes. | +| `otel.instrumentation.log4j-appender.experimental.capture-event-name` | Boolean | `false` | Enable moving the `event.name` attribute (captured by one of the other mechanisms of capturing attributes) to the log event name. | + +[source code attributes]: https://github.com/open-telemetry/semantic-conventions/blob/main/docs/general/attributes.md#source-code-attributes diff --git a/instrumentation/log4j/log4j-appender-1.2/javaagent/build.gradle.kts b/instrumentation/log4j/log4j-appender-1.2/javaagent/build.gradle.kts index f31453561e78..f126e1d06caa 100644 --- a/instrumentation/log4j/log4j-appender-1.2/javaagent/build.gradle.kts +++ b/instrumentation/log4j/log4j-appender-1.2/javaagent/build.gradle.kts @@ -35,7 +35,7 @@ tasks.withType().configureEach { // TODO run tests both with and without experimental log attributes jvmArgs("-Dotel.instrumentation.log4j-appender.experimental.capture-mdc-attributes=*") jvmArgs("-Dotel.instrumentation.log4j-appender.experimental.capture-code-attributes=true") - jvmArgs("-Dotel.instrumentation.log4j-appender.experimental-log-attributes=true") + jvmArgs("-Dotel.instrumentation.log4j-appender.experimental.capture-event-name=true") jvmArgs("-Dotel.instrumentation.log4j-appender.experimental-log-attributes=true") jvmArgs("-Dotel.instrumentation.common.experimental.controller-telemetry.enabled=true") } diff --git a/instrumentation/log4j/log4j-appender-1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/log4j/appender/v1_2/LogEventMapper.java b/instrumentation/log4j/log4j-appender-1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/log4j/appender/v1_2/LogEventMapper.java index 1f6966503fde..29614d9f4fc3 100644 --- a/instrumentation/log4j/log4j-appender-1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/log4j/appender/v1_2/LogEventMapper.java +++ b/instrumentation/log4j/log4j-appender-1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/log4j/appender/v1_2/LogEventMapper.java @@ -9,8 +9,6 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.common.AttributeKey; -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.api.incubator.logs.ExtendedLogRecordBuilder; import io.opentelemetry.api.logs.LogRecordBuilder; import io.opentelemetry.api.logs.Severity; @@ -44,6 +42,8 @@ public final class LogEventMapper { private static final AttributeKey CODE_LINENO = AttributeKey.longKey("code.lineno"); private static final AttributeKey CODE_NAMESPACE = AttributeKey.stringKey("code.namespace"); + // copied from EventIncubatingAttributes + private static final AttributeKey EVENT_NAME = AttributeKey.stringKey("event.name"); // copied from org.apache.log4j.Level because it was only introduced in 1.2.12 private static final int TRACE_INT = 5000; @@ -56,6 +56,10 @@ public final class LogEventMapper { // cached as an optimization private final boolean captureAllMdcAttributes; + private final boolean captureEventName = + AgentInstrumentationConfig.get() + .getBoolean("otel.instrumentation.log4j-appender.experimental.capture-event-name", false); + private LogEventMapper() { List captureMdcAttributes = AgentInstrumentationConfig.get() @@ -98,27 +102,25 @@ public void capture( builder.setSeverityText(level.toString()); } - AttributesBuilder attributes = Attributes.builder(); - // throwable if (throwable != null) { if (builder instanceof ExtendedLogRecordBuilder) { ((ExtendedLogRecordBuilder) builder).setException(throwable); } else { - attributes.put(ExceptionAttributes.EXCEPTION_TYPE, throwable.getClass().getName()); - attributes.put(ExceptionAttributes.EXCEPTION_MESSAGE, throwable.getMessage()); + builder.setAttribute(ExceptionAttributes.EXCEPTION_TYPE, throwable.getClass().getName()); + builder.setAttribute(ExceptionAttributes.EXCEPTION_MESSAGE, throwable.getMessage()); StringWriter writer = new StringWriter(); throwable.printStackTrace(new PrintWriter(writer)); - attributes.put(ExceptionAttributes.EXCEPTION_STACKTRACE, writer.toString()); + builder.setAttribute(ExceptionAttributes.EXCEPTION_STACKTRACE, writer.toString()); } } - captureMdcAttributes(attributes); + captureMdcAttributes(builder); if (captureExperimentalAttributes) { Thread currentThread = Thread.currentThread(); - attributes.put(ThreadIncubatingAttributes.THREAD_NAME, currentThread.getName()); - attributes.put(ThreadIncubatingAttributes.THREAD_ID, currentThread.getId()); + builder.setAttribute(ThreadIncubatingAttributes.THREAD_NAME, currentThread.getName()); + builder.setAttribute(ThreadIncubatingAttributes.THREAD_ID, currentThread.getId()); } if (captureCodeAttributes) { @@ -126,21 +128,21 @@ public void capture( String fileName = locationInfo.getFileName(); if (fileName != null) { if (SemconvStability.isEmitStableCodeSemconv()) { - attributes.put(CodeAttributes.CODE_FILE_PATH, fileName); + builder.setAttribute(CodeAttributes.CODE_FILE_PATH, fileName); } if (SemconvStability.isEmitOldCodeSemconv()) { - attributes.put(CODE_FILEPATH, fileName); + builder.setAttribute(CODE_FILEPATH, fileName); } } if (SemconvStability.isEmitStableCodeSemconv()) { - attributes.put( + builder.setAttribute( CodeAttributes.CODE_FUNCTION_NAME, locationInfo.getClassName() + "." + locationInfo.getMethodName()); } if (SemconvStability.isEmitOldCodeSemconv()) { - attributes.put(CODE_NAMESPACE, locationInfo.getClassName()); - attributes.put(CODE_FUNCTION, locationInfo.getMethodName()); + builder.setAttribute(CODE_NAMESPACE, locationInfo.getClassName()); + builder.setAttribute(CODE_FUNCTION, locationInfo.getMethodName()); } String lineNumber = locationInfo.getLineNumber(); @@ -154,16 +156,14 @@ public void capture( } if (codeLineNo >= 0) { if (SemconvStability.isEmitStableCodeSemconv()) { - attributes.put(CodeAttributes.CODE_LINE_NUMBER, codeLineNo); + builder.setAttribute(CodeAttributes.CODE_LINE_NUMBER, (long) codeLineNo); } if (SemconvStability.isEmitOldCodeSemconv()) { - attributes.put(CODE_LINENO, codeLineNo); + builder.setAttribute(CODE_LINENO, (long) codeLineNo); } } } - builder.setAllAttributes(attributes.build()); - // span context builder.setContext(Context.current()); @@ -171,15 +171,17 @@ public void capture( builder.emit(); } - private void captureMdcAttributes(AttributesBuilder attributes) { + private void captureMdcAttributes(LogRecordBuilder builder) { Hashtable context = MDC.getContext(); if (captureAllMdcAttributes) { if (context != null) { for (Map.Entry entry : context.entrySet()) { - attributes.put( - getMdcAttributeKey(String.valueOf(entry.getKey())), String.valueOf(entry.getValue())); + setAttributeOrEventName( + builder, + getMdcAttributeKey(String.valueOf(entry.getKey())), + String.valueOf(entry.getValue())); } } return; @@ -187,9 +189,7 @@ private void captureMdcAttributes(AttributesBuilder attributes) { for (Map.Entry> entry : captureMdcAttributes.entrySet()) { Object value = context.get(entry.getKey()); - if (value != null) { - attributes.put(entry.getValue(), value.toString()); - } + setAttributeOrEventName(builder, getMdcAttributeKey(entry.getKey()), value); } } @@ -197,6 +197,17 @@ private static AttributeKey getMdcAttributeKey(String key) { return mdcAttributeKeys.computeIfAbsent(key, AttributeKey::stringKey); } + private void setAttributeOrEventName( + LogRecordBuilder builder, AttributeKey key, Object value) { + if (value != null) { + if (captureEventName && key.equals(EVENT_NAME)) { + builder.setEventName(value.toString()); + } else { + builder.setAttribute(key, value.toString()); + } + } + } + private static Severity levelToSeverity(Priority level) { int lev = level.toInt(); if (lev <= TRACE_INT) { diff --git a/instrumentation/log4j/log4j-appender-1.2/javaagent/src/test/java/io/opentelemetry/instrumentation/log4j/appender/v1_2/Log4j1Test.java b/instrumentation/log4j/log4j-appender-1.2/javaagent/src/test/java/io/opentelemetry/instrumentation/log4j/appender/v1_2/Log4j1Test.java index b43d402e3c24..6f088c19d62a 100644 --- a/instrumentation/log4j/log4j-appender-1.2/javaagent/src/test/java/io/opentelemetry/instrumentation/log4j/appender/v1_2/Log4j1Test.java +++ b/instrumentation/log4j/log4j-appender-1.2/javaagent/src/test/java/io/opentelemetry/instrumentation/log4j/appender/v1_2/Log4j1Test.java @@ -176,11 +176,13 @@ private static void test( void testMdc() { MDC.put("key1", "val1"); MDC.put("key2", "val2"); + MDC.put("event.name", "MyEventName"); try { logger.info("xyz"); } finally { MDC.remove("key1"); MDC.remove("key2"); + MDC.remove("event.name"); } List assertions = @@ -195,6 +197,7 @@ void testMdc() { logRecord -> logRecord .hasBody("xyz") + .hasEventName("MyEventName") .hasInstrumentationScope(InstrumentationScopeInfo.builder("abc").build()) .hasSeverity(Severity.INFO) .hasSeverityText("INFO") From 3b0231bc4d08d612848bcbc706f90d424a167392 Mon Sep 17 00:00:00 2001 From: Phil Clay Date: Thu, 18 Sep 2025 10:47:03 -0600 Subject: [PATCH 8/9] use mdc AttributeKey from captureMdcAttributes --- .../instrumentation/log4j/appender/v1_2/LogEventMapper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/log4j/log4j-appender-1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/log4j/appender/v1_2/LogEventMapper.java b/instrumentation/log4j/log4j-appender-1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/log4j/appender/v1_2/LogEventMapper.java index 29614d9f4fc3..9589d9cb6fe0 100644 --- a/instrumentation/log4j/log4j-appender-1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/log4j/appender/v1_2/LogEventMapper.java +++ b/instrumentation/log4j/log4j-appender-1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/log4j/appender/v1_2/LogEventMapper.java @@ -189,7 +189,7 @@ private void captureMdcAttributes(LogRecordBuilder builder) { for (Map.Entry> entry : captureMdcAttributes.entrySet()) { Object value = context.get(entry.getKey()); - setAttributeOrEventName(builder, getMdcAttributeKey(entry.getKey()), value); + setAttributeOrEventName(builder, entry.getValue(), value); } } From e4e5ce6b6bbab2aeb4c80c4ac24e57b1d756b7a2 Mon Sep 17 00:00:00 2001 From: Lauri Tulmin Date: Fri, 19 Sep 2025 14:07:36 +0300 Subject: [PATCH 9/9] review --- .../instrumentation/log4j/appender/v1_2/LogEventMapper.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/instrumentation/log4j/log4j-appender-1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/log4j/appender/v1_2/LogEventMapper.java b/instrumentation/log4j/log4j-appender-1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/log4j/appender/v1_2/LogEventMapper.java index 9589d9cb6fe0..22c28ad19fbb 100644 --- a/instrumentation/log4j/log4j-appender-1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/log4j/appender/v1_2/LogEventMapper.java +++ b/instrumentation/log4j/log4j-appender-1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/log4j/appender/v1_2/LogEventMapper.java @@ -179,9 +179,7 @@ private void captureMdcAttributes(LogRecordBuilder builder) { if (context != null) { for (Map.Entry entry : context.entrySet()) { setAttributeOrEventName( - builder, - getMdcAttributeKey(String.valueOf(entry.getKey())), - String.valueOf(entry.getValue())); + builder, getMdcAttributeKey(String.valueOf(entry.getKey())), entry.getValue()); } } return;