diff --git a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/ExtendedSdkLogRecordBuilder.java b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/ExtendedSdkLogRecordBuilder.java index 41596afa3da..a05ea8d520b 100644 --- a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/ExtendedSdkLogRecordBuilder.java +++ b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/ExtendedSdkLogRecordBuilder.java @@ -44,7 +44,7 @@ public ExtendedSdkLogRecordBuilder setException(Throwable throwable) { loggerSharedState .getExceptionAttributeResolver() .setExceptionAttributes( - this::setAttribute, + this::setExceptionAttribute, throwable, loggerSharedState.getLogLimits().getMaxAttributeValueLength()); @@ -154,4 +154,17 @@ public void emit() { body, extendedAttributes)); } + + /** + * Sets an exception-derived attribute only if it hasn't already been set by the user. This + * ensures user-set attributes take precedence over exception-derived attributes. + */ + private void setExceptionAttribute(AttributeKey key, @Nullable T value) { + if (key == null || key.getKey().isEmpty() || value == null) { + return; + } + if (extendedAttributes == null || extendedAttributes.get(key) == null) { + setAttribute(key, value); + } + } } diff --git a/sdk/logs/src/testIncubating/java/io/opentelemetry/sdk/logs/ExtendedLoggerBuilderTest.java b/sdk/logs/src/testIncubating/java/io/opentelemetry/sdk/logs/ExtendedLoggerBuilderTest.java index 17e1eb0ddff..b27fd21d641 100644 --- a/sdk/logs/src/testIncubating/java/io/opentelemetry/sdk/logs/ExtendedLoggerBuilderTest.java +++ b/sdk/logs/src/testIncubating/java/io/opentelemetry/sdk/logs/ExtendedLoggerBuilderTest.java @@ -76,4 +76,27 @@ public void setExceptionAttributes( equalTo(EXCEPTION_TYPE, "type"), equalTo(EXCEPTION_STACKTRACE, "stacktrace"))); } + + @Test + void setException_UserAttributesTakePrecedence() { + Logger logger = loggerProviderBuilder.build().get("logger"); + + ((ExtendedLogRecordBuilder) logger.logRecordBuilder()) + .setAttribute(EXCEPTION_MESSAGE, "custom message") + .setException(new Exception("error")) + .emit(); + + assertThat(exporter.getFinishedLogRecordItems()) + .satisfiesExactly( + logRecord -> + assertThat(logRecord) + .hasAttributesSatisfyingExactly( + equalTo(EXCEPTION_TYPE, "java.lang.Exception"), + equalTo(EXCEPTION_MESSAGE, "custom message"), + satisfies( + EXCEPTION_STACKTRACE, + stacktrace -> + stacktrace.startsWith( + "java.lang.Exception: error" + System.lineSeparator())))); + } }