From 9f2ecdebb720d31613da42b70bb27118411eeaa8 Mon Sep 17 00:00:00 2001 From: Jack Berg Date: Thu, 6 Mar 2025 12:37:55 -0600 Subject: [PATCH 1/4] Add ExtendedLogRecordBuilder#setException, make exception.* resolution configurable --- .../incubator/logs/ExtendedDefaultLogger.java | 5 + .../logs/ExtendedLogRecordBuilder.java | 3 + .../kotlin/otel.java-conventions.gradle.kts | 4 +- .../DefaultExceptionAttributeResolver.java | 49 +++++++ .../internal/ExceptionAttributeResolver.java | 44 +++++++ .../sdk/logs/ExtendedSdkLogRecordBuilder.java | 6 + .../sdk/logs/LoggerSharedState.java | 10 +- .../sdk/logs/SdkLogRecordBuilder.java | 27 ++++ .../sdk/logs/SdkLoggerProvider.java | 7 +- .../sdk/logs/SdkLoggerProviderBuilder.java | 26 +++- .../logs/internal/SdkLoggerProviderUtil.java | 17 +++ .../sdk/logs/LoggerSharedStateTest.java | 7 +- .../sdk/logs/ExtendedLoggerBuilderTest.java | 86 +++++++++++++ .../io/opentelemetry/sdk/trace/SdkSpan.java | 43 +++---- .../sdk/trace/SdkSpanBuilder.java | 1 + .../sdk/trace/SdkTracerProvider.java | 12 +- .../sdk/trace/SdkTracerProviderBuilder.java | 42 ++++-- .../sdk/trace/TracerSharedState.java | 13 +- .../trace/internal/SdkTracerProviderUtil.java | 17 +++ .../opentelemetry/sdk/trace/SdkSpanTest.java | 120 +++++++++++++----- .../sdk/trace/SdkTracerProviderTest.java | 74 +++++++---- 21 files changed, 511 insertions(+), 102 deletions(-) create mode 100644 sdk/common/src/main/java/io/opentelemetry/sdk/internal/DefaultExceptionAttributeResolver.java create mode 100644 sdk/common/src/main/java/io/opentelemetry/sdk/internal/ExceptionAttributeResolver.java create mode 100644 sdk/logs/src/testIncubating/java/io/opentelemetry/sdk/logs/ExtendedLoggerBuilderTest.java diff --git a/api/incubator/src/main/java/io/opentelemetry/api/incubator/logs/ExtendedDefaultLogger.java b/api/incubator/src/main/java/io/opentelemetry/api/incubator/logs/ExtendedDefaultLogger.java index 378af1c6b00..d9a95a6d2d4 100644 --- a/api/incubator/src/main/java/io/opentelemetry/api/incubator/logs/ExtendedDefaultLogger.java +++ b/api/incubator/src/main/java/io/opentelemetry/api/incubator/logs/ExtendedDefaultLogger.java @@ -40,6 +40,11 @@ public ExtendedLogRecordBuilder setEventName(String eventName) { return this; } + @Override + public ExtendedLogRecordBuilder setException(Throwable throwable) { + return this; + } + @Override public LogRecordBuilder setTimestamp(long timestamp, TimeUnit unit) { return this; diff --git a/api/incubator/src/main/java/io/opentelemetry/api/incubator/logs/ExtendedLogRecordBuilder.java b/api/incubator/src/main/java/io/opentelemetry/api/incubator/logs/ExtendedLogRecordBuilder.java index 4a18baa07ed..d6fde3699b1 100644 --- a/api/incubator/src/main/java/io/opentelemetry/api/incubator/logs/ExtendedLogRecordBuilder.java +++ b/api/incubator/src/main/java/io/opentelemetry/api/incubator/logs/ExtendedLogRecordBuilder.java @@ -19,4 +19,7 @@ public interface ExtendedLogRecordBuilder extends LogRecordBuilder { * record with a non-empty event name is an Event. */ ExtendedLogRecordBuilder setEventName(String eventName); + + /** Set standard {@code exception.*} attributes based on the {@code throwable}. */ + ExtendedLogRecordBuilder setException(Throwable throwable); } diff --git a/buildSrc/src/main/kotlin/otel.java-conventions.gradle.kts b/buildSrc/src/main/kotlin/otel.java-conventions.gradle.kts index ea6230fda36..95ca6c9026c 100644 --- a/buildSrc/src/main/kotlin/otel.java-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/otel.java-conventions.gradle.kts @@ -87,9 +87,7 @@ tasks { // https://groups.google.com/forum/#!topic/bazel-discuss/_R3A9TJSoPM "-Xlint:-processing", // We suppress the "options" warning because it prevents compilation on modern JDKs - "-Xlint:-options", - // Fail build on any warning - "-Werror", + "-Xlint:-options" ), ) } diff --git a/sdk/common/src/main/java/io/opentelemetry/sdk/internal/DefaultExceptionAttributeResolver.java b/sdk/common/src/main/java/io/opentelemetry/sdk/internal/DefaultExceptionAttributeResolver.java new file mode 100644 index 00000000000..af5fb8f77f8 --- /dev/null +++ b/sdk/common/src/main/java/io/opentelemetry/sdk/internal/DefaultExceptionAttributeResolver.java @@ -0,0 +1,49 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.internal; + +import java.io.PrintWriter; +import java.io.StringWriter; +import javax.annotation.Nullable; + +/** + * This class is internal and experimental. Its APIs are unstable and can change at any time. Its + * APIs (or a version of them) may be promoted to the public stable API in the future, but no + * guarantees are made. + */ +public final class DefaultExceptionAttributeResolver implements ExceptionAttributeResolver { + + private static final DefaultExceptionAttributeResolver INSTANCE = + new DefaultExceptionAttributeResolver(); + + private DefaultExceptionAttributeResolver() {} + + public static ExceptionAttributeResolver getInstance() { + return INSTANCE; + } + + @Override + @Nullable + public String getExceptionType(Throwable throwable) { + return throwable.getClass().getCanonicalName(); + } + + @Override + @Nullable + public String getExceptionMessage(Throwable throwable) { + return throwable.getMessage(); + } + + @Override + @Nullable + public String getExceptionStacktrace(Throwable throwable) { + StringWriter stringWriter = new StringWriter(); + try (PrintWriter printWriter = new PrintWriter(stringWriter)) { + throwable.printStackTrace(printWriter); + } + return stringWriter.toString(); + } +} diff --git a/sdk/common/src/main/java/io/opentelemetry/sdk/internal/ExceptionAttributeResolver.java b/sdk/common/src/main/java/io/opentelemetry/sdk/internal/ExceptionAttributeResolver.java new file mode 100644 index 00000000000..b5c26fc56a6 --- /dev/null +++ b/sdk/common/src/main/java/io/opentelemetry/sdk/internal/ExceptionAttributeResolver.java @@ -0,0 +1,44 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.internal; + +import io.opentelemetry.api.common.AttributeKey; +import javax.annotation.Nullable; + +/** + * Implementations resolve {@code exception.*} attributes attached to span events, logs, etc. + * + *

This class is internal and experimental. Its APIs are unstable and can change at any time. Its + * APIs (or a version of them) may be promoted to the public stable API in the future, but no + * guarantees are made. + */ +public interface ExceptionAttributeResolver { + + AttributeKey EXCEPTION_TYPE = AttributeKey.stringKey("exception.type"); + AttributeKey EXCEPTION_MESSAGE = AttributeKey.stringKey("exception.message"); + AttributeKey EXCEPTION_STACKTRACE = AttributeKey.stringKey("exception.stacktrace"); + + /** + * Resolve the {@link #EXCEPTION_TYPE} attribute from the {@code throwable}, or {@code null} if no + * value should be set. + */ + @Nullable + String getExceptionType(Throwable throwable); + + /** + * Resolve the {@link #EXCEPTION_MESSAGE} attribute from the {@code throwable}, or {@code null} if + * no value should be set. + */ + @Nullable + String getExceptionMessage(Throwable throwable); + + /** + * Resolve the {@link #EXCEPTION_STACKTRACE} attribute from the {@code throwable}, or {@code null} + * if no value should be set. + */ + @Nullable + String getExceptionStacktrace(Throwable throwable); +} 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 9f874d1f4b3..97f337cc194 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 @@ -29,6 +29,12 @@ public ExtendedSdkLogRecordBuilder setEventName(String eventName) { return this; } + @Override + public ExtendedSdkLogRecordBuilder setException(Throwable throwable) { + super.setException(throwable); + return this; + } + @Override public ExtendedSdkLogRecordBuilder setTimestamp(long timestamp, TimeUnit unit) { super.setTimestamp(timestamp, unit); diff --git a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/LoggerSharedState.java b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/LoggerSharedState.java index 768871e1e57..5b40f897a32 100644 --- a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/LoggerSharedState.java +++ b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/LoggerSharedState.java @@ -7,6 +7,7 @@ import io.opentelemetry.sdk.common.Clock; import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.internal.ExceptionAttributeResolver; import io.opentelemetry.sdk.resources.Resource; import java.util.function.Supplier; import javax.annotation.Nullable; @@ -21,17 +22,20 @@ final class LoggerSharedState { private final Supplier logLimitsSupplier; private final LogRecordProcessor logRecordProcessor; private final Clock clock; + private final ExceptionAttributeResolver exceptionAttributeResolver; @Nullable private volatile CompletableResultCode shutdownResult = null; LoggerSharedState( Resource resource, Supplier logLimitsSupplier, LogRecordProcessor logRecordProcessor, - Clock clock) { + Clock clock, + ExceptionAttributeResolver exceptionAttributeResolver) { this.resource = resource; this.logLimitsSupplier = logLimitsSupplier; this.logRecordProcessor = logRecordProcessor; this.clock = clock; + this.exceptionAttributeResolver = exceptionAttributeResolver; } Resource getResource() { @@ -50,6 +54,10 @@ Clock getClock() { return clock; } + ExceptionAttributeResolver getExceptionAttributeResolver() { + return exceptionAttributeResolver; + } + boolean hasBeenShutdown() { return shutdownResult != null; } diff --git a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLogRecordBuilder.java b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLogRecordBuilder.java index da7e1f45e7e..48af664b4f2 100644 --- a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLogRecordBuilder.java +++ b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLogRecordBuilder.java @@ -5,6 +5,10 @@ package io.opentelemetry.sdk.logs; +import static io.opentelemetry.sdk.internal.ExceptionAttributeResolver.EXCEPTION_MESSAGE; +import static io.opentelemetry.sdk.internal.ExceptionAttributeResolver.EXCEPTION_STACKTRACE; +import static io.opentelemetry.sdk.internal.ExceptionAttributeResolver.EXCEPTION_TYPE; + import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Value; import io.opentelemetry.api.logs.LogRecordBuilder; @@ -13,6 +17,7 @@ import io.opentelemetry.context.Context; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; import io.opentelemetry.sdk.internal.AttributesMap; +import io.opentelemetry.sdk.internal.ExceptionAttributeResolver; import java.time.Instant; import java.util.concurrent.TimeUnit; import javax.annotation.Nullable; @@ -46,6 +51,28 @@ SdkLogRecordBuilder setEventName(String eventName) { return this; } + // accessible via ExtendedSdkLogRecordBuilder + SdkLogRecordBuilder setException(Throwable throwable) { + if (throwable == null) { + return this; + } + ExceptionAttributeResolver exceptionAttributeResolver = + loggerSharedState.getExceptionAttributeResolver(); + String type = exceptionAttributeResolver.getExceptionType(throwable); + if (type != null) { + setAttribute(EXCEPTION_TYPE, type); + } + String message = exceptionAttributeResolver.getExceptionMessage(throwable); + if (message != null) { + setAttribute(EXCEPTION_MESSAGE, message); + } + String stacktrace = exceptionAttributeResolver.getExceptionStacktrace(throwable); + if (stacktrace != null) { + setAttribute(EXCEPTION_STACKTRACE, stacktrace); + } + return this; + } + @Override public SdkLogRecordBuilder setTimestamp(long timestamp, TimeUnit unit) { this.timestampEpochNanos = unit.toNanos(timestamp); diff --git a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLoggerProvider.java b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLoggerProvider.java index ea68a6c2a5c..b698318cf16 100644 --- a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLoggerProvider.java +++ b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLoggerProvider.java @@ -13,6 +13,7 @@ import io.opentelemetry.sdk.common.CompletableResultCode; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; import io.opentelemetry.sdk.internal.ComponentRegistry; +import io.opentelemetry.sdk.internal.ExceptionAttributeResolver; import io.opentelemetry.sdk.internal.ScopeConfigurator; import io.opentelemetry.sdk.logs.internal.LoggerConfig; import io.opentelemetry.sdk.resources.Resource; @@ -53,10 +54,12 @@ public static SdkLoggerProviderBuilder builder() { Supplier logLimitsSupplier, List processors, Clock clock, - ScopeConfigurator loggerConfigurator) { + ScopeConfigurator loggerConfigurator, + ExceptionAttributeResolver exceptionAttributeResolver) { LogRecordProcessor logRecordProcessor = LogRecordProcessor.composite(processors); this.sharedState = - new LoggerSharedState(resource, logLimitsSupplier, logRecordProcessor, clock); + new LoggerSharedState( + resource, logLimitsSupplier, logRecordProcessor, clock, exceptionAttributeResolver); this.loggerComponentRegistry = new ComponentRegistry<>( instrumentationScopeInfo -> diff --git a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLoggerProviderBuilder.java b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLoggerProviderBuilder.java index 94d990c8a4b..298a1511f37 100644 --- a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLoggerProviderBuilder.java +++ b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLoggerProviderBuilder.java @@ -12,6 +12,8 @@ import io.opentelemetry.context.Context; import io.opentelemetry.sdk.common.Clock; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; +import io.opentelemetry.sdk.internal.DefaultExceptionAttributeResolver; +import io.opentelemetry.sdk.internal.ExceptionAttributeResolver; import io.opentelemetry.sdk.internal.ScopeConfigurator; import io.opentelemetry.sdk.internal.ScopeConfiguratorBuilder; import io.opentelemetry.sdk.logs.data.LogRecordData; @@ -37,6 +39,8 @@ public final class SdkLoggerProviderBuilder { private Clock clock = Clock.getDefault(); private ScopeConfiguratorBuilder loggerConfiguratorBuilder = LoggerConfig.configuratorBuilder(); + private ExceptionAttributeResolver exceptionAttributeResolver = + DefaultExceptionAttributeResolver.getInstance(); SdkLoggerProviderBuilder() {} @@ -149,6 +153,21 @@ SdkLoggerProviderBuilder addLoggerConfiguratorCondition( return this; } + /** + * Set the exception attribute resolver, which resolves {@code exception.*} attributes an + * exception is set on a log. + * + *

This method is experimental so not public. You may reflectively call it using {@link + * SdkLoggerProviderUtil#setExceptionAttributeResolver(SdkLoggerProviderBuilder, + * ExceptionAttributeResolver)}. + */ + SdkLoggerProviderBuilder setExceptionAttributeResolver( + ExceptionAttributeResolver exceptionAttributeResolver) { + requireNonNull(exceptionAttributeResolver, "exceptionAttributeResolver"); + this.exceptionAttributeResolver = exceptionAttributeResolver; + return this; + } + /** * Create a {@link SdkLoggerProvider} instance. * @@ -156,6 +175,11 @@ SdkLoggerProviderBuilder addLoggerConfiguratorCondition( */ public SdkLoggerProvider build() { return new SdkLoggerProvider( - resource, logLimitsSupplier, logRecordProcessors, clock, loggerConfiguratorBuilder.build()); + resource, + logLimitsSupplier, + logRecordProcessors, + clock, + loggerConfiguratorBuilder.build(), + exceptionAttributeResolver); } } diff --git a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/internal/SdkLoggerProviderUtil.java b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/internal/SdkLoggerProviderUtil.java index eb4fbb4ec29..979e62c5c06 100644 --- a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/internal/SdkLoggerProviderUtil.java +++ b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/internal/SdkLoggerProviderUtil.java @@ -6,6 +6,7 @@ package io.opentelemetry.sdk.logs.internal; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; +import io.opentelemetry.sdk.internal.ExceptionAttributeResolver; import io.opentelemetry.sdk.internal.ScopeConfigurator; import io.opentelemetry.sdk.logs.SdkLoggerProviderBuilder; import java.lang.reflect.InvocationTargetException; @@ -55,4 +56,20 @@ public static void addLoggerConfiguratorCondition( "Error calling addLoggerConfiguratorCondition on SdkLoggerProviderBuilder", e); } } + + /** Reflectively set exception attribute resolver to the {@link SdkLoggerProviderBuilder}. */ + public static void setExceptionAttributeResolver( + SdkLoggerProviderBuilder sdkLoggerProviderBuilder, + ExceptionAttributeResolver exceptionAttributeResolver) { + try { + Method method = + SdkLoggerProviderBuilder.class.getDeclaredMethod( + "setExceptionAttributeResolver", ExceptionAttributeResolver.class); + method.setAccessible(true); + method.invoke(sdkLoggerProviderBuilder, exceptionAttributeResolver); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + throw new IllegalStateException( + "Error calling setExceptionAttributeResolver on SdkLoggerProviderBuilder", e); + } + } } diff --git a/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/LoggerSharedStateTest.java b/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/LoggerSharedStateTest.java index 29a3a846a56..31b46c65206 100644 --- a/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/LoggerSharedStateTest.java +++ b/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/LoggerSharedStateTest.java @@ -12,6 +12,7 @@ import io.opentelemetry.sdk.common.Clock; import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.internal.DefaultExceptionAttributeResolver; import io.opentelemetry.sdk.resources.Resource; import org.junit.jupiter.api.Test; @@ -24,7 +25,11 @@ void shutdown() { when(logRecordProcessor.shutdown()).thenReturn(code); LoggerSharedState state = new LoggerSharedState( - Resource.empty(), LogLimits::getDefault, logRecordProcessor, Clock.getDefault()); + Resource.empty(), + LogLimits::getDefault, + logRecordProcessor, + Clock.getDefault(), + DefaultExceptionAttributeResolver.getInstance()); state.shutdown(); state.shutdown(); verify(logRecordProcessor, times(1)).shutdown(); 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 new file mode 100644 index 00000000000..92c36c27f06 --- /dev/null +++ b/sdk/logs/src/testIncubating/java/io/opentelemetry/sdk/logs/ExtendedLoggerBuilderTest.java @@ -0,0 +1,86 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.logs; + +import static io.opentelemetry.sdk.internal.ExceptionAttributeResolver.EXCEPTION_MESSAGE; +import static io.opentelemetry.sdk.internal.ExceptionAttributeResolver.EXCEPTION_STACKTRACE; +import static io.opentelemetry.sdk.internal.ExceptionAttributeResolver.EXCEPTION_TYPE; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; + +import io.opentelemetry.api.incubator.logs.ExtendedLogRecordBuilder; +import io.opentelemetry.api.logs.Logger; +import io.opentelemetry.sdk.internal.ExceptionAttributeResolver; +import io.opentelemetry.sdk.logs.export.SimpleLogRecordProcessor; +import io.opentelemetry.sdk.logs.internal.SdkLoggerProviderUtil; +import io.opentelemetry.sdk.testing.exporter.InMemoryLogRecordExporter; +import javax.annotation.Nullable; +import org.junit.jupiter.api.Test; + +class ExtendedLoggerBuilderTest { + + private final InMemoryLogRecordExporter exporter = InMemoryLogRecordExporter.create(); + private final SdkLoggerProviderBuilder loggerProviderBuilder = + SdkLoggerProvider.builder().addLogRecordProcessor(SimpleLogRecordProcessor.create(exporter)); + + @Test + void setException_DefaultResolver() { + Logger logger = loggerProviderBuilder.build().get("logger"); + + ((ExtendedLogRecordBuilder) logger.logRecordBuilder()) + .setException(new Exception("error")) + .emit(); + + assertThat(exporter.getFinishedLogRecordItems()) + .satisfiesExactly( + logRecord -> + assertThat(logRecord) + .hasAttributesSatisfyingExactly( + equalTo(EXCEPTION_TYPE, "java.lang.Exception"), + equalTo(EXCEPTION_MESSAGE, "error"), + satisfies( + EXCEPTION_STACKTRACE, + stacktrace -> stacktrace.startsWith("java.lang.Exception: error\n")))); + } + + @Test + void setException_CustomResolver() { + SdkLoggerProviderUtil.setExceptionAttributeResolver( + loggerProviderBuilder, + new ExceptionAttributeResolver() { + @Override + public String getExceptionType(Throwable throwable) { + return "type"; + } + + @Nullable + @Override + public String getExceptionMessage(Throwable throwable) { + return null; + } + + @Override + public String getExceptionStacktrace(Throwable throwable) { + return "stacktrace"; + } + }); + + Logger logger = loggerProviderBuilder.build().get("logger"); + + ((ExtendedLogRecordBuilder) logger.logRecordBuilder()) + .setException(new Exception("error")) + .emit(); + + assertThat(exporter.getFinishedLogRecordItems()) + .satisfiesExactly( + logRecord -> + assertThat(logRecord) + .hasAttributesSatisfyingExactly( + equalTo(EXCEPTION_TYPE, "type"), + equalTo(EXCEPTION_STACKTRACE, "stacktrace"))); + } +} diff --git a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkSpan.java b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkSpan.java index c1c788bf931..cf140d1fc96 100644 --- a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkSpan.java +++ b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkSpan.java @@ -5,6 +5,10 @@ package io.opentelemetry.sdk.trace; +import static io.opentelemetry.sdk.internal.ExceptionAttributeResolver.EXCEPTION_MESSAGE; +import static io.opentelemetry.sdk.internal.ExceptionAttributeResolver.EXCEPTION_STACKTRACE; +import static io.opentelemetry.sdk.internal.ExceptionAttributeResolver.EXCEPTION_TYPE; + import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.internal.GuardedBy; @@ -17,6 +21,7 @@ import io.opentelemetry.sdk.common.InstrumentationScopeInfo; import io.opentelemetry.sdk.internal.AttributeUtil; import io.opentelemetry.sdk.internal.AttributesMap; +import io.opentelemetry.sdk.internal.ExceptionAttributeResolver; import io.opentelemetry.sdk.internal.InstrumentationScopeUtil; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.trace.data.EventData; @@ -25,8 +30,6 @@ import io.opentelemetry.sdk.trace.data.SpanData; import io.opentelemetry.sdk.trace.data.StatusData; import io.opentelemetry.sdk.trace.internal.ExtendedSpanProcessor; -import java.io.PrintWriter; -import java.io.StringWriter; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -50,6 +53,8 @@ final class SdkSpan implements ReadWriteSpan { private final SpanContext parentSpanContext; // Handler called when the span starts and ends. private final SpanProcessor spanProcessor; + // Resolves exception.* when an recordException is called + private final ExceptionAttributeResolver exceptionAttributeResolver; // The kind of the span. private final SpanKind kind; // The clock used to get the time. @@ -117,13 +122,6 @@ private enum EndState { @Nullable private Thread spanEndingThread; - private static final AttributeKey EXCEPTION_TYPE = - AttributeKey.stringKey("exception.type"); - private static final AttributeKey EXCEPTION_MESSAGE = - AttributeKey.stringKey("exception.message"); - private static final AttributeKey EXCEPTION_STACKTRACE = - AttributeKey.stringKey("exception.stacktrace"); - private SdkSpan( SpanContext context, String name, @@ -132,6 +130,7 @@ private SdkSpan( SpanContext parentSpanContext, SpanLimits spanLimits, SpanProcessor spanProcessor, + ExceptionAttributeResolver exceptionAttributeResolver, AnchoredClock clock, Resource resource, @Nullable AttributesMap attributes, @@ -146,6 +145,7 @@ private SdkSpan( this.name = name; this.kind = kind; this.spanProcessor = spanProcessor; + this.exceptionAttributeResolver = exceptionAttributeResolver; this.resource = resource; this.hasEnded = EndState.NOT_ENDED; this.clock = clock; @@ -178,6 +178,7 @@ static SdkSpan startSpan( Context parentContext, SpanLimits spanLimits, SpanProcessor spanProcessor, + ExceptionAttributeResolver exceptionAttributeResolver, Clock tracerClock, Resource resource, @Nullable AttributesMap attributes, @@ -216,6 +217,7 @@ static SdkSpan startSpan( parentSpan.getSpanContext(), spanLimits, spanProcessor, + exceptionAttributeResolver, clock, resource, attributes, @@ -466,7 +468,6 @@ public ReadWriteSpan recordException(Throwable exception) { } @Override - @SuppressWarnings("unchecked") public ReadWriteSpan recordException(Throwable exception, Attributes additionalAttributes) { if (exception == null) { return this; @@ -478,22 +479,18 @@ public ReadWriteSpan recordException(Throwable exception, Attributes additionalA AttributesMap attributes = AttributesMap.create( spanLimits.getMaxNumberOfAttributes(), spanLimits.getMaxAttributeValueLength()); - String exceptionName = exception.getClass().getCanonicalName(); - String exceptionMessage = exception.getMessage(); - StringWriter stringWriter = new StringWriter(); - try (PrintWriter printWriter = new PrintWriter(stringWriter)) { - exception.printStackTrace(printWriter); - } - String stackTrace = stringWriter.toString(); - if (exceptionName != null) { - attributes.put(EXCEPTION_TYPE, exceptionName); + String type = exceptionAttributeResolver.getExceptionType(exception); + if (type != null) { + attributes.put(EXCEPTION_TYPE, type); } - if (exceptionMessage != null) { - attributes.put(EXCEPTION_MESSAGE, exceptionMessage); + String message = exceptionAttributeResolver.getExceptionMessage(exception); + if (message != null) { + attributes.put(EXCEPTION_MESSAGE, message); } - if (stackTrace != null) { - attributes.put(EXCEPTION_STACKTRACE, stackTrace); + String stacktrace = exceptionAttributeResolver.getExceptionStacktrace(exception); + if (stacktrace != null) { + attributes.put(EXCEPTION_STACKTRACE, stacktrace); } additionalAttributes.forEach(attributes::put); diff --git a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkSpanBuilder.java b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkSpanBuilder.java index b12107c7d43..c0f872265ec 100644 --- a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkSpanBuilder.java +++ b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkSpanBuilder.java @@ -226,6 +226,7 @@ public Span startSpan() { parentContext, spanLimits, tracerSharedState.getActiveSpanProcessor(), + tracerSharedState.getExceptionAttributesResolver(), tracerSharedState.getClock(), tracerSharedState.getResource(), recordedAttributes, diff --git a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkTracerProvider.java b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkTracerProvider.java index 7bd69a24236..deee9024151 100644 --- a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkTracerProvider.java +++ b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkTracerProvider.java @@ -12,6 +12,7 @@ import io.opentelemetry.sdk.common.CompletableResultCode; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; import io.opentelemetry.sdk.internal.ComponentRegistry; +import io.opentelemetry.sdk.internal.ExceptionAttributeResolver; import io.opentelemetry.sdk.internal.ScopeConfigurator; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.trace.internal.SdkTracerProviderUtil; @@ -52,10 +53,17 @@ public static SdkTracerProviderBuilder builder() { Supplier spanLimitsSupplier, Sampler sampler, List spanProcessors, - ScopeConfigurator tracerConfigurator) { + ScopeConfigurator tracerConfigurator, + ExceptionAttributeResolver exceptionAttributeResolver) { this.sharedState = new TracerSharedState( - clock, idsGenerator, resource, spanLimitsSupplier, sampler, spanProcessors); + clock, + idsGenerator, + resource, + spanLimitsSupplier, + sampler, + spanProcessors, + exceptionAttributeResolver); this.tracerSdkComponentRegistry = new ComponentRegistry<>( instrumentationScopeInfo -> diff --git a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkTracerProviderBuilder.java b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkTracerProviderBuilder.java index 531cd1a6384..f83b21fcbbf 100644 --- a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkTracerProviderBuilder.java +++ b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkTracerProviderBuilder.java @@ -7,8 +7,11 @@ import static java.util.Objects.requireNonNull; +import io.opentelemetry.api.trace.Span; import io.opentelemetry.sdk.common.Clock; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; +import io.opentelemetry.sdk.internal.DefaultExceptionAttributeResolver; +import io.opentelemetry.sdk.internal.ExceptionAttributeResolver; import io.opentelemetry.sdk.internal.ScopeConfigurator; import io.opentelemetry.sdk.internal.ScopeConfiguratorBuilder; import io.opentelemetry.sdk.resources.Resource; @@ -34,10 +37,12 @@ public final class SdkTracerProviderBuilder { private Sampler sampler = DEFAULT_SAMPLER; private ScopeConfiguratorBuilder tracerConfiguratorBuilder = TracerConfig.configuratorBuilder(); + private ExceptionAttributeResolver exceptionAttributeResolver = + DefaultExceptionAttributeResolver.getInstance(); /** - * Assign a {@link Clock}. {@link Clock} will be used each time a {@link - * io.opentelemetry.api.trace.Span} is started, ended or any event is recorded. + * Assign a {@link Clock}. {@link Clock} will be used each time a {@link Span} is started, ended + * or any event is recorded. * *

The {@code clock} must be thread-safe and return immediately (no remote calls, as contention * free as possible). @@ -52,8 +57,8 @@ public SdkTracerProviderBuilder setClock(Clock clock) { } /** - * Assign an {@link IdGenerator}. {@link IdGenerator} will be used each time a {@link - * io.opentelemetry.api.trace.Span} is started. + * Assign an {@link IdGenerator}. {@link IdGenerator} will be used each time a {@link Span} is + * started. * *

The {@code idGenerator} must be thread-safe and return immediately (no remote calls, as * contention free as possible). @@ -97,8 +102,7 @@ public SdkTracerProviderBuilder addResource(Resource resource) { *

This method is equivalent to calling {@link #setSpanLimits(Supplier)} like this {@code * #setSpanLimits(() -> spanLimits)}. * - * @param spanLimits the limits that will be used for every {@link - * io.opentelemetry.api.trace.Span}. + * @param spanLimits the limits that will be used for every {@link Span}. * @return this */ public SdkTracerProviderBuilder setSpanLimits(SpanLimits spanLimits) { @@ -109,13 +113,13 @@ public SdkTracerProviderBuilder setSpanLimits(SpanLimits spanLimits) { /** * Assign a {@link Supplier} of {@link SpanLimits}. {@link SpanLimits} will be retrieved each time - * a {@link io.opentelemetry.api.trace.Span} is started. + * a {@link Span} is started. * *

The {@code spanLimitsSupplier} must be thread-safe and return immediately (no remote calls, * as contention free as possible). * * @param spanLimitsSupplier the supplier that will be used to retrieve the {@link SpanLimits} for - * every {@link io.opentelemetry.api.trace.Span}. + * every {@link Span}. * @return this */ public SdkTracerProviderBuilder setSpanLimits(Supplier spanLimitsSupplier) { @@ -126,7 +130,7 @@ public SdkTracerProviderBuilder setSpanLimits(Supplier spanLimitsSup /** * Assign a {@link Sampler} to use for sampling traces. {@link Sampler} will be called each time a - * {@link io.opentelemetry.api.trace.Span} is started. + * {@link Span} is started. * *

The {@code sampler} must be thread-safe and return immediately (no remote calls, as * contention free as possible). @@ -142,7 +146,7 @@ public SdkTracerProviderBuilder setSampler(Sampler sampler) { /** * Add a SpanProcessor to the span pipeline that will be built. {@link SpanProcessor} will be - * called each time a {@link io.opentelemetry.api.trace.Span} is started or ended. + * called each time a {@link Span} is started or ended. * *

The {@code spanProcessor} must be thread-safe and return immediately (no remote calls, as * contention free as possible). @@ -196,6 +200,21 @@ SdkTracerProviderBuilder addTracerConfiguratorCondition( return this; } + /** + * Set the exception attribute resolver, which resolves {@code exception.*} attributes when {@link + * Span#recordException(Throwable)} + * + *

This method is experimental so not public. You may reflectively call it using {@link + * SdkTracerProviderUtil#setExceptionAttributeResolver(SdkTracerProviderBuilder, + * ExceptionAttributeResolver)}. + */ + SdkTracerProviderBuilder setExceptionAttributeResolver( + ExceptionAttributeResolver exceptionAttributeResolver) { + requireNonNull(exceptionAttributeResolver, "exceptionAttributeResolver"); + this.exceptionAttributeResolver = exceptionAttributeResolver; + return this; + } + /** * Create a new {@link SdkTracerProvider} instance with the configuration. * @@ -209,7 +228,8 @@ public SdkTracerProvider build() { spanLimitsSupplier, sampler, spanProcessors, - tracerConfiguratorBuilder.build()); + tracerConfiguratorBuilder.build(), + exceptionAttributeResolver); } SdkTracerProviderBuilder() {} diff --git a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/TracerSharedState.java b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/TracerSharedState.java index 3d07a2853f8..74d43076b97 100644 --- a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/TracerSharedState.java +++ b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/TracerSharedState.java @@ -7,6 +7,7 @@ import io.opentelemetry.sdk.common.Clock; import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.internal.ExceptionAttributeResolver; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.trace.samplers.Sampler; import java.util.List; @@ -26,6 +27,7 @@ final class TracerSharedState { private final Supplier spanLimitsSupplier; private final Sampler sampler; private final SpanProcessor activeSpanProcessor; + private final ExceptionAttributeResolver exceptionAttributeResolver; @Nullable private volatile CompletableResultCode shutdownResult = null; @@ -35,14 +37,16 @@ final class TracerSharedState { Resource resource, Supplier spanLimitsSupplier, Sampler sampler, - List spanProcessors) { + List spanProcessors, + ExceptionAttributeResolver exceptionAttributeResolver) { this.clock = clock; this.idGenerator = idGenerator; this.idGeneratorSafeToSkipIdValidation = idGenerator instanceof RandomIdGenerator; this.resource = resource; this.spanLimitsSupplier = spanLimitsSupplier; this.sampler = sampler; - activeSpanProcessor = SpanProcessor.composite(spanProcessors); + this.activeSpanProcessor = SpanProcessor.composite(spanProcessors); + this.exceptionAttributeResolver = exceptionAttributeResolver; } Clock getClock() { @@ -89,6 +93,11 @@ boolean hasBeenShutdown() { return shutdownResult != null; } + /** Return the {@link ExceptionAttributeResolver}. */ + ExceptionAttributeResolver getExceptionAttributesResolver() { + return exceptionAttributeResolver; + } + /** * Stops tracing, including shutting down processors and set to {@code true} {@link * #hasBeenShutdown()}. diff --git a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/internal/SdkTracerProviderUtil.java b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/internal/SdkTracerProviderUtil.java index 753a312fa1f..f9ca8fafc56 100644 --- a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/internal/SdkTracerProviderUtil.java +++ b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/internal/SdkTracerProviderUtil.java @@ -6,6 +6,7 @@ package io.opentelemetry.sdk.trace.internal; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; +import io.opentelemetry.sdk.internal.ExceptionAttributeResolver; import io.opentelemetry.sdk.internal.ScopeConfigurator; import io.opentelemetry.sdk.trace.SdkTracerProvider; import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder; @@ -72,4 +73,20 @@ public static void addTracerConfiguratorCondition( "Error calling addTracerConfiguratorCondition on SdkTracerProviderBuilder", e); } } + + /** Reflectively set exception attribute resolver to the {@link SdkTracerProviderBuilder}. */ + public static void setExceptionAttributeResolver( + SdkTracerProviderBuilder sdkTracerProviderBuilder, + ExceptionAttributeResolver exceptionAttributeResolver) { + try { + Method method = + SdkTracerProviderBuilder.class.getDeclaredMethod( + "setExceptionAttributeResolver", ExceptionAttributeResolver.class); + method.setAccessible(true); + method.invoke(sdkTracerProviderBuilder, exceptionAttributeResolver); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + throw new IllegalStateException( + "Error calling setExceptionAttributeResolver on SdkTracerProviderBuilder", e); + } + } } diff --git a/sdk/trace/src/test/java/io/opentelemetry/sdk/trace/SdkSpanTest.java b/sdk/trace/src/test/java/io/opentelemetry/sdk/trace/SdkSpanTest.java index fb387cf2a18..bd7fc11ec06 100644 --- a/sdk/trace/src/test/java/io/opentelemetry/sdk/trace/SdkSpanTest.java +++ b/sdk/trace/src/test/java/io/opentelemetry/sdk/trace/SdkSpanTest.java @@ -13,6 +13,8 @@ import static io.opentelemetry.api.common.AttributeKey.longKey; import static io.opentelemetry.api.common.AttributeKey.stringArrayKey; import static io.opentelemetry.api.common.AttributeKey.stringKey; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static java.util.stream.Collectors.joining; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; @@ -37,6 +39,8 @@ import io.opentelemetry.context.Context; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; import io.opentelemetry.sdk.internal.AttributesMap; +import io.opentelemetry.sdk.internal.DefaultExceptionAttributeResolver; +import io.opentelemetry.sdk.internal.ExceptionAttributeResolver; import io.opentelemetry.sdk.internal.InstrumentationScopeUtil; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.testing.time.TestClock; @@ -67,6 +71,7 @@ import java.util.stream.IntStream; import java.util.stream.Stream; import javax.annotation.Nullable; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -441,7 +446,7 @@ void getAttribute() { void getAttributes() { SdkSpan span = createTestSpanWithAttributes(attributes); try { - assertThat(span.getAttributes()) + Assertions.assertThat(span.getAttributes()) .isEqualTo( Attributes.builder() .put("MyBooleanAttributeKey", false) @@ -457,7 +462,7 @@ void getAttributes() { void getAttributes_Empty() { SdkSpan span = createTestSpan(SpanKind.INTERNAL); try { - assertThat(span.getAttributes()).isEqualTo(Attributes.empty()); + Assertions.assertThat(span.getAttributes()).isEqualTo(Attributes.empty()); } finally { span.end(); } @@ -791,48 +796,48 @@ void addEvent() { } List events = span.toSpanData().getEvents(); assertThat(events).hasSize(6); - assertThat(events.get(0)) + Assertions.assertThat(events.get(0)) .satisfies( event -> { assertThat(event.getName()).isEqualTo("event1"); - assertThat(event.getAttributes()).isEqualTo(Attributes.empty()); + Assertions.assertThat(event.getAttributes()).isEqualTo(Attributes.empty()); assertThat(event.getEpochNanos()).isEqualTo(START_EPOCH_NANOS); }); - assertThat(events.get(1)) + Assertions.assertThat(events.get(1)) .satisfies( event -> { assertThat(event.getName()).isEqualTo("event2"); - assertThat(event.getAttributes()) + Assertions.assertThat(event.getAttributes()) .isEqualTo(Attributes.of(stringKey("e1key"), "e1Value")); assertThat(event.getEpochNanos()).isEqualTo(START_EPOCH_NANOS); }); - assertThat(events.get(2)) + Assertions.assertThat(events.get(2)) .satisfies( event -> { assertThat(event.getName()).isEqualTo("event3"); - assertThat(event.getAttributes()).isEqualTo(Attributes.empty()); + Assertions.assertThat(event.getAttributes()).isEqualTo(Attributes.empty()); assertThat(event.getEpochNanos()).isEqualTo(TimeUnit.SECONDS.toNanos(10)); }); - assertThat(events.get(3)) + Assertions.assertThat(events.get(3)) .satisfies( event -> { assertThat(event.getName()).isEqualTo("event4"); - assertThat(event.getAttributes()).isEqualTo(Attributes.empty()); + Assertions.assertThat(event.getAttributes()).isEqualTo(Attributes.empty()); assertThat(event.getEpochNanos()).isEqualTo(TimeUnit.SECONDS.toNanos(20)); }); - assertThat(events.get(4)) + Assertions.assertThat(events.get(4)) .satisfies( event -> { assertThat(event.getName()).isEqualTo("event5"); - assertThat(event.getAttributes()) + Assertions.assertThat(event.getAttributes()) .isEqualTo(Attributes.builder().put("foo", "bar").build()); assertThat(event.getEpochNanos()).isEqualTo(TimeUnit.MILLISECONDS.toNanos(30)); }); - assertThat(events.get(5)) + Assertions.assertThat(events.get(5)) .satisfies( event -> { assertThat(event.getName()).isEqualTo("event6"); - assertThat(event.getAttributes()) + Assertions.assertThat(event.getAttributes()) .isEqualTo(Attributes.builder().put("foo", "bar").build()); assertThat(event.getEpochNanos()).isEqualTo(TimeUnit.MILLISECONDS.toNanos(1000)); }); @@ -942,7 +947,8 @@ void addLink() { .build(), parentSpanId, null, - null); + null, + DefaultExceptionAttributeResolver.getInstance()); try { Span span1 = createTestSpan(SpanKind.INTERNAL); Span span2 = createTestSpan(SpanKind.INTERNAL); @@ -981,11 +987,11 @@ void addLink() { .satisfiesExactly( link -> { assertThat(link.getSpanContext()).isEqualTo(span1.getSpanContext()); - assertThat(link.getAttributes()).isEqualTo(Attributes.empty()); + Assertions.assertThat(link.getAttributes()).isEqualTo(Attributes.empty()); }, link -> { assertThat(link.getSpanContext()).isEqualTo(span2.getSpanContext()); - assertThat(link.getAttributes()) + Assertions.assertThat(link.getAttributes()) .isEqualTo( Attributes.builder() .put("key1", true) @@ -1032,6 +1038,7 @@ void addLink_FaultIn() { Context.root(), SpanLimits.getDefault(), spanProcessor, + DefaultExceptionAttributeResolver.getInstance(), testClock, resource, null, @@ -1137,7 +1144,7 @@ void droppingEvents() { EventData expectedEvent = EventData.create( START_EPOCH_NANOS + i * NANOS_PER_SECOND, "event2", Attributes.empty(), 0); - assertThat(spanData.getEvents().get(i)).isEqualTo(expectedEvent); + Assertions.assertThat(spanData.getEvents().get(i)).isEqualTo(expectedEvent); assertThat(spanData.getTotalRecordedEvents()).isEqualTo(2 * maxNumberOfEvents); } } finally { @@ -1149,7 +1156,7 @@ void droppingEvents() { EventData expectedEvent = EventData.create( START_EPOCH_NANOS + i * NANOS_PER_SECOND, "event2", Attributes.empty(), 0); - assertThat(spanData.getEvents().get(i)).isEqualTo(expectedEvent); + Assertions.assertThat(spanData.getEvents().get(i)).isEqualTo(expectedEvent); } } @@ -1181,7 +1188,7 @@ void recordException() { .isEqualTo(exception.getClass().getName()); assertThat(event.getAttributes().get(stringKey("exception.stacktrace"))).isEqualTo(stacktrace); assertThat(event.getAttributes().size()).isEqualTo(3); - assertThat(event) + Assertions.assertThat(event) .isInstanceOfSatisfying( ExceptionEventData.class, exceptionEvent -> { @@ -1269,7 +1276,7 @@ void recordException_additionalAttributes() { assertThat(event.getAttributes().get(stringKey("exception.stacktrace"))).isEqualTo(stacktrace); assertThat(event.getAttributes().size()).isEqualTo(4); - assertThat(event) + Assertions.assertThat(event) .isInstanceOfSatisfying( ExceptionEventData.class, exceptionEvent -> { @@ -1292,6 +1299,47 @@ void recordException_SpanLimits() { assertThat(event.getTotalAttributeCount() - event.getAttributes().size()).isPositive(); } + @Test + void recordException_CustomResolver() { + ExceptionAttributeResolver exceptionAttributeResolver = + new ExceptionAttributeResolver() { + @Override + public String getExceptionType(Throwable throwable) { + return "type"; + } + + @Nullable + @Override + public String getExceptionMessage(Throwable throwable) { + return null; + } + + @Override + public String getExceptionStacktrace(Throwable throwable) { + return "stacktrace"; + } + }; + + SdkSpan span = + createTestSpan( + SpanKind.INTERNAL, + SpanLimits.getDefault(), + parentSpanId, + null, + Collections.singletonList(link), + exceptionAttributeResolver); + + span.recordException(new IllegalStateException("error")); + + List events = span.toSpanData().getEvents(); + assertThat(events.size()).isEqualTo(1); + EventData event = events.get(0); + assertThat(event) + .hasAttributesSatisfyingExactly( + equalTo(ExceptionAttributeResolver.EXCEPTION_TYPE, "type"), + equalTo(ExceptionAttributeResolver.EXCEPTION_STACKTRACE, "stacktrace")); + } + @Test void badArgsIgnored() { SdkSpan span = createTestRootSpan(); @@ -1343,6 +1391,7 @@ void onStartOnEndNotRequired() { Context.root(), spanLimits, spanProcessor, + DefaultExceptionAttributeResolver.getInstance(), testClock, resource, AttributesMap.create( @@ -1424,7 +1473,8 @@ private SdkSpan createTestSpanWithAttributes(Map attribute SpanLimits.getDefault(), null, attributesMap, - Collections.singletonList(link)); + Collections.singletonList(link), + DefaultExceptionAttributeResolver.getInstance()); } private SdkSpan createTestRootSpan() { @@ -1433,17 +1483,28 @@ private SdkSpan createTestRootSpan() { SpanLimits.getDefault(), SpanId.getInvalid(), null, - Collections.singletonList(link)); + Collections.singletonList(link), + DefaultExceptionAttributeResolver.getInstance()); } private SdkSpan createTestSpan(SpanKind kind) { return createTestSpan( - kind, SpanLimits.getDefault(), parentSpanId, null, Collections.singletonList(link)); + kind, + SpanLimits.getDefault(), + parentSpanId, + null, + Collections.singletonList(link), + DefaultExceptionAttributeResolver.getInstance()); } private SdkSpan createTestSpan(SpanLimits config) { return createTestSpan( - SpanKind.INTERNAL, config, parentSpanId, null, Collections.singletonList(link)); + SpanKind.INTERNAL, + config, + parentSpanId, + null, + Collections.singletonList(link), + DefaultExceptionAttributeResolver.getInstance()); } private SdkSpan createTestSpan( @@ -1451,7 +1512,8 @@ private SdkSpan createTestSpan( SpanLimits config, @Nullable String parentSpanId, @Nullable AttributesMap attributes, - @Nullable List links) { + @Nullable List links, + ExceptionAttributeResolver exceptionAttributeResolver) { List linksCopy = links == null ? new ArrayList<>() : new ArrayList<>(links); SdkSpan span = @@ -1468,6 +1530,7 @@ private SdkSpan createTestSpan( Context.root(), config, spanProcessor, + exceptionAttributeResolver, testClock, resource, attributes, @@ -1534,9 +1597,7 @@ void testAsSpanData() { Resource resource = this.resource; Attributes attributes = TestUtils.generateRandomAttributes(); AttributesMap attributesWithCapacity = AttributesMap.create(32, Integer.MAX_VALUE); - attributes.forEach( - (attributeKey, object) -> - attributesWithCapacity.put((AttributeKey) attributeKey, object)); + attributes.forEach(attributesWithCapacity::put); Attributes event1Attributes = TestUtils.generateRandomAttributes(); Attributes event2Attributes = TestUtils.generateRandomAttributes(); SpanContext context = @@ -1557,6 +1618,7 @@ void testAsSpanData() { Context.root(), spanLimits, spanProcessor, + DefaultExceptionAttributeResolver.getInstance(), clock, resource, attributesWithCapacity, diff --git a/sdk/trace/src/test/java/io/opentelemetry/sdk/trace/SdkTracerProviderTest.java b/sdk/trace/src/test/java/io/opentelemetry/sdk/trace/SdkTracerProviderTest.java index 61b64b4ebdf..191b6ad6fec 100644 --- a/sdk/trace/src/test/java/io/opentelemetry/sdk/trace/SdkTracerProviderTest.java +++ b/sdk/trace/src/test/java/io/opentelemetry/sdk/trace/SdkTracerProviderTest.java @@ -9,6 +9,8 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import io.opentelemetry.api.common.Attributes; @@ -18,6 +20,8 @@ import io.opentelemetry.sdk.common.Clock; import io.opentelemetry.sdk.common.CompletableResultCode; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; +import io.opentelemetry.sdk.internal.DefaultExceptionAttributeResolver; +import io.opentelemetry.sdk.internal.ExceptionAttributeResolver; import io.opentelemetry.sdk.internal.ScopeConfigurator; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.trace.internal.SdkTracerProviderUtil; @@ -38,11 +42,11 @@ @MockitoSettings(strictness = Strictness.LENIENT) class SdkTracerProviderTest { @Mock private SpanProcessor spanProcessor; - private SdkTracerProvider tracerFactory; + private SdkTracerProvider tracerProvider; @BeforeEach void setUp() { - tracerFactory = SdkTracerProvider.builder().addSpanProcessor(spanProcessor).build(); + tracerProvider = SdkTracerProvider.builder().addSpanProcessor(spanProcessor).build(); when(spanProcessor.forceFlush()).thenReturn(CompletableResultCode.ofSuccess()); when(spanProcessor.shutdown()).thenReturn(CompletableResultCode.ofSuccess()); } @@ -137,34 +141,35 @@ void builder_NullIdsGenerator() { @Test void defaultGet() { - assertThat(tracerFactory.get("test")).isInstanceOf(SdkTracer.class); + assertThat(tracerProvider.get("test")).isInstanceOf(SdkTracer.class); } @Test void getSameInstanceForSameName_WithoutVersion() { - assertThat(tracerFactory.get("test")).isSameAs(tracerFactory.get("test")); - assertThat(tracerFactory.get("test")) - .isSameAs(tracerFactory.get("test", null)) - .isSameAs(tracerFactory.tracerBuilder("test").build()); + assertThat(tracerProvider.get("test")).isSameAs(tracerProvider.get("test")); + assertThat(tracerProvider.get("test")) + .isSameAs(tracerProvider.get("test", null)) + .isSameAs(tracerProvider.tracerBuilder("test").build()); } @Test void getSameInstanceForSameName_WithVersion() { - assertThat(tracerFactory.get("test", "version")) - .isSameAs(tracerFactory.get("test", "version")) - .isSameAs(tracerFactory.tracerBuilder("test").setInstrumentationVersion("version").build()); + assertThat(tracerProvider.get("test", "version")) + .isSameAs(tracerProvider.get("test", "version")) + .isSameAs( + tracerProvider.tracerBuilder("test").setInstrumentationVersion("version").build()); } @Test void getSameInstanceForSameName_WithVersionAndSchema() { assertThat( - tracerFactory + tracerProvider .tracerBuilder("test") .setInstrumentationVersion("version") .setSchemaUrl("http://url") .build()) .isSameAs( - tracerFactory + tracerProvider .tracerBuilder("test") .setInstrumentationVersion("version") .setSchemaUrl("http://url") @@ -179,7 +184,7 @@ void propagatesInstrumentationScopeInfoToTracer() { .setSchemaUrl("http://url") .build(); Tracer tracer = - tracerFactory + tracerProvider .tracerBuilder(expected.getName()) .setInstrumentationVersion(expected.getVersion()) .setSchemaUrl(expected.getSchemaUrl()) @@ -198,7 +203,7 @@ void propagatesEnablementToTracerByUtil() { } void propagatesEnablementToTracer(boolean directly) { - SdkTracer tracer = (SdkTracer) tracerFactory.get("test"); + SdkTracer tracer = (SdkTracer) tracerProvider.get("test"); boolean isEnabled = tracer.isEnabled(); ScopeConfigurator flipConfigurator = new ScopeConfigurator() { @@ -209,9 +214,9 @@ public TracerConfig apply(InstrumentationScopeInfo scopeInfo) { }; // all in the same thread, so should see enablement change immediately if (directly) { - tracerFactory.setTracerConfigurator(flipConfigurator); + tracerProvider.setTracerConfigurator(flipConfigurator); } else { - SdkTracerProviderUtil.setTracerConfigurator(tracerFactory, flipConfigurator); + SdkTracerProviderUtil.setTracerConfigurator(tracerProvider, flipConfigurator); } assertThat(tracer.isEnabled()).isEqualTo(!isEnabled); } @@ -227,58 +232,73 @@ void build_SpanLimits() { @Test void shutdown() { - tracerFactory.shutdown(); + tracerProvider.shutdown(); Mockito.verify(spanProcessor, Mockito.times(1)).shutdown(); } @Test void close() { - tracerFactory.close(); + tracerProvider.close(); Mockito.verify(spanProcessor, Mockito.times(1)).shutdown(); } @Test void forceFlush() { - tracerFactory.forceFlush(); + tracerProvider.forceFlush(); Mockito.verify(spanProcessor, Mockito.times(1)).forceFlush(); } @Test @SuppressLogger(SdkTracerProvider.class) void shutdownTwice_OnlyFlushSpanProcessorOnce() { - tracerFactory.shutdown(); + tracerProvider.shutdown(); Mockito.verify(spanProcessor, Mockito.times(1)).shutdown(); - tracerFactory.shutdown(); // the second call will be ignored + tracerProvider.shutdown(); // the second call will be ignored Mockito.verify(spanProcessor, Mockito.times(1)).shutdown(); } @Test void returnNoopSpanAfterShutdown() { - tracerFactory.shutdown(); - Span span = tracerFactory.get("noop").spanBuilder("span").startSpan(); + tracerProvider.shutdown(); + Span span = tracerProvider.get("noop").spanBuilder("span").startSpan(); assertThat(span.getSpanContext().isValid()).isFalse(); span.end(); } @Test void suppliesDefaultTracerForNullName() { - SdkTracer tracer = (SdkTracer) tracerFactory.get(null); + SdkTracer tracer = (SdkTracer) tracerProvider.get(null); assertThat(tracer.getInstrumentationScopeInfo().getName()) .isEqualTo(SdkTracerProvider.DEFAULT_TRACER_NAME); - tracer = (SdkTracer) tracerFactory.get(null, null); + tracer = (SdkTracer) tracerProvider.get(null, null); assertThat(tracer.getInstrumentationScopeInfo().getName()) .isEqualTo(SdkTracerProvider.DEFAULT_TRACER_NAME); } @Test void suppliesDefaultTracerForEmptyName() { - SdkTracer tracer = (SdkTracer) tracerFactory.get(""); + SdkTracer tracer = (SdkTracer) tracerProvider.get(""); assertThat(tracer.getInstrumentationScopeInfo().getName()) .isEqualTo(SdkTracerProvider.DEFAULT_TRACER_NAME); - tracer = (SdkTracer) tracerFactory.get("", ""); + tracer = (SdkTracer) tracerProvider.get("", ""); assertThat(tracer.getInstrumentationScopeInfo().getName()) .isEqualTo(SdkTracerProvider.DEFAULT_TRACER_NAME); } + + @Test + void exceptionAttributeResolver() { + SdkTracerProviderBuilder builder = SdkTracerProvider.builder().addSpanProcessor(spanProcessor); + ExceptionAttributeResolver exceptionAttributeResolver = + spy(DefaultExceptionAttributeResolver.getInstance()); + SdkTracerProviderUtil.setExceptionAttributeResolver(builder, exceptionAttributeResolver); + + Exception exception = new Exception("error"); + builder.build().get("tracer").spanBuilder("span").startSpan().recordException(exception).end(); + + verify(exceptionAttributeResolver).getExceptionType(exception); + verify(exceptionAttributeResolver).getExceptionMessage(exception); + verify(exceptionAttributeResolver).getExceptionStacktrace(exception); + } } From 1b0a28e8403fa60b827b234b57c66f3e2d789fde Mon Sep 17 00:00:00 2001 From: Jack Berg Date: Thu, 6 Mar 2025 13:32:39 -0600 Subject: [PATCH 2/4] Use System.lineSeparator() instead of \n to fix build --- .../io/opentelemetry/sdk/logs/ExtendedLoggerBuilderTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 92c36c27f06..698da1fc8df 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 @@ -44,7 +44,9 @@ void setException_DefaultResolver() { equalTo(EXCEPTION_MESSAGE, "error"), satisfies( EXCEPTION_STACKTRACE, - stacktrace -> stacktrace.startsWith("java.lang.Exception: error\n")))); + stacktrace -> + stacktrace.startsWith( + "java.lang.Exception: error" + System.lineSeparator())))); } @Test From a11ac5bbf81e31e5a6658b6d4397120ffcc09a8a Mon Sep 17 00:00:00 2001 From: Jack Berg Date: Thu, 10 Apr 2025 15:24:47 -0500 Subject: [PATCH 3/4] Remove custom exception resolver --- .../sdk/internal/AttributeUtil.java | 32 ++++++++ .../DefaultExceptionAttributeResolver.java | 49 ------------ .../internal/ExceptionAttributeResolver.java | 44 ----------- sdk/logs/build.gradle.kts | 1 + .../sdk/logs/LoggerSharedState.java | 10 +-- .../sdk/logs/SdkLogRecordBuilder.java | 23 +----- .../sdk/logs/SdkLoggerProvider.java | 7 +- .../sdk/logs/SdkLoggerProviderBuilder.java | 26 +------ .../logs/internal/SdkLoggerProviderUtil.java | 17 ----- .../sdk/logs/LoggerSharedStateTest.java | 7 +- .../sdk/logs/ExtendedLoggerBuilderTest.java | 48 +----------- .../io/opentelemetry/sdk/trace/SdkSpan.java | 24 +----- .../sdk/trace/SdkSpanBuilder.java | 1 - .../sdk/trace/SdkTracerProvider.java | 12 +-- .../sdk/trace/SdkTracerProviderBuilder.java | 22 +----- .../sdk/trace/TracerSharedState.java | 11 +-- .../trace/internal/SdkTracerProviderUtil.java | 17 ----- .../opentelemetry/sdk/trace/SdkSpanTest.java | 74 ++----------------- .../sdk/trace/SdkTracerProviderTest.java | 19 ----- 19 files changed, 57 insertions(+), 387 deletions(-) delete mode 100644 sdk/common/src/main/java/io/opentelemetry/sdk/internal/DefaultExceptionAttributeResolver.java delete mode 100644 sdk/common/src/main/java/io/opentelemetry/sdk/internal/ExceptionAttributeResolver.java diff --git a/sdk/common/src/main/java/io/opentelemetry/sdk/internal/AttributeUtil.java b/sdk/common/src/main/java/io/opentelemetry/sdk/internal/AttributeUtil.java index de7fac88eba..e0fc9b56b90 100644 --- a/sdk/common/src/main/java/io/opentelemetry/sdk/internal/AttributeUtil.java +++ b/sdk/common/src/main/java/io/opentelemetry/sdk/internal/AttributeUtil.java @@ -8,9 +8,12 @@ import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; +import java.io.PrintWriter; +import java.io.StringWriter; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.function.BiConsumer; import java.util.function.Predicate; /** @@ -19,6 +22,13 @@ */ public final class AttributeUtil { + private static final AttributeKey EXCEPTION_TYPE = + AttributeKey.stringKey("exception.type"); + private static final AttributeKey EXCEPTION_MESSAGE = + AttributeKey.stringKey("exception.message"); + private static final AttributeKey EXCEPTION_STACKTRACE = + AttributeKey.stringKey("exception.stacktrace"); + private AttributeUtil() {} /** @@ -95,4 +105,26 @@ public static Object applyAttributeLengthLimit(Object value, int lengthLimit) { } return value; } + + public static void addExceptionAttributes( + Throwable exception, BiConsumer, String> attributeConsumer) { + String exceptionType = exception.getClass().getCanonicalName(); + if (exceptionType != null) { + attributeConsumer.accept(EXCEPTION_TYPE, exceptionType); + } + + String exceptionMessage = exception.getMessage(); + if (exceptionMessage != null) { + attributeConsumer.accept(EXCEPTION_MESSAGE, exceptionMessage); + } + + StringWriter stringWriter = new StringWriter(); + try (PrintWriter printWriter = new PrintWriter(stringWriter)) { + exception.printStackTrace(printWriter); + } + String stackTrace = stringWriter.toString(); + if (stackTrace != null) { + attributeConsumer.accept(EXCEPTION_STACKTRACE, stackTrace); + } + } } diff --git a/sdk/common/src/main/java/io/opentelemetry/sdk/internal/DefaultExceptionAttributeResolver.java b/sdk/common/src/main/java/io/opentelemetry/sdk/internal/DefaultExceptionAttributeResolver.java deleted file mode 100644 index af5fb8f77f8..00000000000 --- a/sdk/common/src/main/java/io/opentelemetry/sdk/internal/DefaultExceptionAttributeResolver.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.sdk.internal; - -import java.io.PrintWriter; -import java.io.StringWriter; -import javax.annotation.Nullable; - -/** - * This class is internal and experimental. Its APIs are unstable and can change at any time. Its - * APIs (or a version of them) may be promoted to the public stable API in the future, but no - * guarantees are made. - */ -public final class DefaultExceptionAttributeResolver implements ExceptionAttributeResolver { - - private static final DefaultExceptionAttributeResolver INSTANCE = - new DefaultExceptionAttributeResolver(); - - private DefaultExceptionAttributeResolver() {} - - public static ExceptionAttributeResolver getInstance() { - return INSTANCE; - } - - @Override - @Nullable - public String getExceptionType(Throwable throwable) { - return throwable.getClass().getCanonicalName(); - } - - @Override - @Nullable - public String getExceptionMessage(Throwable throwable) { - return throwable.getMessage(); - } - - @Override - @Nullable - public String getExceptionStacktrace(Throwable throwable) { - StringWriter stringWriter = new StringWriter(); - try (PrintWriter printWriter = new PrintWriter(stringWriter)) { - throwable.printStackTrace(printWriter); - } - return stringWriter.toString(); - } -} diff --git a/sdk/common/src/main/java/io/opentelemetry/sdk/internal/ExceptionAttributeResolver.java b/sdk/common/src/main/java/io/opentelemetry/sdk/internal/ExceptionAttributeResolver.java deleted file mode 100644 index b5c26fc56a6..00000000000 --- a/sdk/common/src/main/java/io/opentelemetry/sdk/internal/ExceptionAttributeResolver.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.sdk.internal; - -import io.opentelemetry.api.common.AttributeKey; -import javax.annotation.Nullable; - -/** - * Implementations resolve {@code exception.*} attributes attached to span events, logs, etc. - * - *

This class is internal and experimental. Its APIs are unstable and can change at any time. Its - * APIs (or a version of them) may be promoted to the public stable API in the future, but no - * guarantees are made. - */ -public interface ExceptionAttributeResolver { - - AttributeKey EXCEPTION_TYPE = AttributeKey.stringKey("exception.type"); - AttributeKey EXCEPTION_MESSAGE = AttributeKey.stringKey("exception.message"); - AttributeKey EXCEPTION_STACKTRACE = AttributeKey.stringKey("exception.stacktrace"); - - /** - * Resolve the {@link #EXCEPTION_TYPE} attribute from the {@code throwable}, or {@code null} if no - * value should be set. - */ - @Nullable - String getExceptionType(Throwable throwable); - - /** - * Resolve the {@link #EXCEPTION_MESSAGE} attribute from the {@code throwable}, or {@code null} if - * no value should be set. - */ - @Nullable - String getExceptionMessage(Throwable throwable); - - /** - * Resolve the {@link #EXCEPTION_STACKTRACE} attribute from the {@code throwable}, or {@code null} - * if no value should be set. - */ - @Nullable - String getExceptionStacktrace(Throwable throwable); -} diff --git a/sdk/logs/build.gradle.kts b/sdk/logs/build.gradle.kts index c0c740d3743..b205b03e90e 100644 --- a/sdk/logs/build.gradle.kts +++ b/sdk/logs/build.gradle.kts @@ -28,6 +28,7 @@ testing { dependencies { implementation(project(":sdk:testing")) implementation(project(":api:incubator")) + implementation("io.opentelemetry.semconv:opentelemetry-semconv-incubating") implementation("com.google.guava:guava") } } diff --git a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/LoggerSharedState.java b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/LoggerSharedState.java index 5b40f897a32..768871e1e57 100644 --- a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/LoggerSharedState.java +++ b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/LoggerSharedState.java @@ -7,7 +7,6 @@ import io.opentelemetry.sdk.common.Clock; import io.opentelemetry.sdk.common.CompletableResultCode; -import io.opentelemetry.sdk.internal.ExceptionAttributeResolver; import io.opentelemetry.sdk.resources.Resource; import java.util.function.Supplier; import javax.annotation.Nullable; @@ -22,20 +21,17 @@ final class LoggerSharedState { private final Supplier logLimitsSupplier; private final LogRecordProcessor logRecordProcessor; private final Clock clock; - private final ExceptionAttributeResolver exceptionAttributeResolver; @Nullable private volatile CompletableResultCode shutdownResult = null; LoggerSharedState( Resource resource, Supplier logLimitsSupplier, LogRecordProcessor logRecordProcessor, - Clock clock, - ExceptionAttributeResolver exceptionAttributeResolver) { + Clock clock) { this.resource = resource; this.logLimitsSupplier = logLimitsSupplier; this.logRecordProcessor = logRecordProcessor; this.clock = clock; - this.exceptionAttributeResolver = exceptionAttributeResolver; } Resource getResource() { @@ -54,10 +50,6 @@ Clock getClock() { return clock; } - ExceptionAttributeResolver getExceptionAttributeResolver() { - return exceptionAttributeResolver; - } - boolean hasBeenShutdown() { return shutdownResult != null; } diff --git a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLogRecordBuilder.java b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLogRecordBuilder.java index 48af664b4f2..a73f4aa681b 100644 --- a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLogRecordBuilder.java +++ b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLogRecordBuilder.java @@ -5,10 +5,6 @@ package io.opentelemetry.sdk.logs; -import static io.opentelemetry.sdk.internal.ExceptionAttributeResolver.EXCEPTION_MESSAGE; -import static io.opentelemetry.sdk.internal.ExceptionAttributeResolver.EXCEPTION_STACKTRACE; -import static io.opentelemetry.sdk.internal.ExceptionAttributeResolver.EXCEPTION_TYPE; - import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Value; import io.opentelemetry.api.logs.LogRecordBuilder; @@ -16,8 +12,8 @@ import io.opentelemetry.api.trace.Span; import io.opentelemetry.context.Context; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; +import io.opentelemetry.sdk.internal.AttributeUtil; import io.opentelemetry.sdk.internal.AttributesMap; -import io.opentelemetry.sdk.internal.ExceptionAttributeResolver; import java.time.Instant; import java.util.concurrent.TimeUnit; import javax.annotation.Nullable; @@ -56,20 +52,9 @@ SdkLogRecordBuilder setException(Throwable throwable) { if (throwable == null) { return this; } - ExceptionAttributeResolver exceptionAttributeResolver = - loggerSharedState.getExceptionAttributeResolver(); - String type = exceptionAttributeResolver.getExceptionType(throwable); - if (type != null) { - setAttribute(EXCEPTION_TYPE, type); - } - String message = exceptionAttributeResolver.getExceptionMessage(throwable); - if (message != null) { - setAttribute(EXCEPTION_MESSAGE, message); - } - String stacktrace = exceptionAttributeResolver.getExceptionStacktrace(throwable); - if (stacktrace != null) { - setAttribute(EXCEPTION_STACKTRACE, stacktrace); - } + + AttributeUtil.addExceptionAttributes(throwable, this::setAttribute); + return this; } diff --git a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLoggerProvider.java b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLoggerProvider.java index b698318cf16..ea68a6c2a5c 100644 --- a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLoggerProvider.java +++ b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLoggerProvider.java @@ -13,7 +13,6 @@ import io.opentelemetry.sdk.common.CompletableResultCode; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; import io.opentelemetry.sdk.internal.ComponentRegistry; -import io.opentelemetry.sdk.internal.ExceptionAttributeResolver; import io.opentelemetry.sdk.internal.ScopeConfigurator; import io.opentelemetry.sdk.logs.internal.LoggerConfig; import io.opentelemetry.sdk.resources.Resource; @@ -54,12 +53,10 @@ public static SdkLoggerProviderBuilder builder() { Supplier logLimitsSupplier, List processors, Clock clock, - ScopeConfigurator loggerConfigurator, - ExceptionAttributeResolver exceptionAttributeResolver) { + ScopeConfigurator loggerConfigurator) { LogRecordProcessor logRecordProcessor = LogRecordProcessor.composite(processors); this.sharedState = - new LoggerSharedState( - resource, logLimitsSupplier, logRecordProcessor, clock, exceptionAttributeResolver); + new LoggerSharedState(resource, logLimitsSupplier, logRecordProcessor, clock); this.loggerComponentRegistry = new ComponentRegistry<>( instrumentationScopeInfo -> diff --git a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLoggerProviderBuilder.java b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLoggerProviderBuilder.java index 298a1511f37..94d990c8a4b 100644 --- a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLoggerProviderBuilder.java +++ b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLoggerProviderBuilder.java @@ -12,8 +12,6 @@ import io.opentelemetry.context.Context; import io.opentelemetry.sdk.common.Clock; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; -import io.opentelemetry.sdk.internal.DefaultExceptionAttributeResolver; -import io.opentelemetry.sdk.internal.ExceptionAttributeResolver; import io.opentelemetry.sdk.internal.ScopeConfigurator; import io.opentelemetry.sdk.internal.ScopeConfiguratorBuilder; import io.opentelemetry.sdk.logs.data.LogRecordData; @@ -39,8 +37,6 @@ public final class SdkLoggerProviderBuilder { private Clock clock = Clock.getDefault(); private ScopeConfiguratorBuilder loggerConfiguratorBuilder = LoggerConfig.configuratorBuilder(); - private ExceptionAttributeResolver exceptionAttributeResolver = - DefaultExceptionAttributeResolver.getInstance(); SdkLoggerProviderBuilder() {} @@ -153,21 +149,6 @@ SdkLoggerProviderBuilder addLoggerConfiguratorCondition( return this; } - /** - * Set the exception attribute resolver, which resolves {@code exception.*} attributes an - * exception is set on a log. - * - *

This method is experimental so not public. You may reflectively call it using {@link - * SdkLoggerProviderUtil#setExceptionAttributeResolver(SdkLoggerProviderBuilder, - * ExceptionAttributeResolver)}. - */ - SdkLoggerProviderBuilder setExceptionAttributeResolver( - ExceptionAttributeResolver exceptionAttributeResolver) { - requireNonNull(exceptionAttributeResolver, "exceptionAttributeResolver"); - this.exceptionAttributeResolver = exceptionAttributeResolver; - return this; - } - /** * Create a {@link SdkLoggerProvider} instance. * @@ -175,11 +156,6 @@ SdkLoggerProviderBuilder setExceptionAttributeResolver( */ public SdkLoggerProvider build() { return new SdkLoggerProvider( - resource, - logLimitsSupplier, - logRecordProcessors, - clock, - loggerConfiguratorBuilder.build(), - exceptionAttributeResolver); + resource, logLimitsSupplier, logRecordProcessors, clock, loggerConfiguratorBuilder.build()); } } diff --git a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/internal/SdkLoggerProviderUtil.java b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/internal/SdkLoggerProviderUtil.java index 979e62c5c06..eb4fbb4ec29 100644 --- a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/internal/SdkLoggerProviderUtil.java +++ b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/internal/SdkLoggerProviderUtil.java @@ -6,7 +6,6 @@ package io.opentelemetry.sdk.logs.internal; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; -import io.opentelemetry.sdk.internal.ExceptionAttributeResolver; import io.opentelemetry.sdk.internal.ScopeConfigurator; import io.opentelemetry.sdk.logs.SdkLoggerProviderBuilder; import java.lang.reflect.InvocationTargetException; @@ -56,20 +55,4 @@ public static void addLoggerConfiguratorCondition( "Error calling addLoggerConfiguratorCondition on SdkLoggerProviderBuilder", e); } } - - /** Reflectively set exception attribute resolver to the {@link SdkLoggerProviderBuilder}. */ - public static void setExceptionAttributeResolver( - SdkLoggerProviderBuilder sdkLoggerProviderBuilder, - ExceptionAttributeResolver exceptionAttributeResolver) { - try { - Method method = - SdkLoggerProviderBuilder.class.getDeclaredMethod( - "setExceptionAttributeResolver", ExceptionAttributeResolver.class); - method.setAccessible(true); - method.invoke(sdkLoggerProviderBuilder, exceptionAttributeResolver); - } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { - throw new IllegalStateException( - "Error calling setExceptionAttributeResolver on SdkLoggerProviderBuilder", e); - } - } } diff --git a/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/LoggerSharedStateTest.java b/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/LoggerSharedStateTest.java index 31b46c65206..29a3a846a56 100644 --- a/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/LoggerSharedStateTest.java +++ b/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/LoggerSharedStateTest.java @@ -12,7 +12,6 @@ import io.opentelemetry.sdk.common.Clock; import io.opentelemetry.sdk.common.CompletableResultCode; -import io.opentelemetry.sdk.internal.DefaultExceptionAttributeResolver; import io.opentelemetry.sdk.resources.Resource; import org.junit.jupiter.api.Test; @@ -25,11 +24,7 @@ void shutdown() { when(logRecordProcessor.shutdown()).thenReturn(code); LoggerSharedState state = new LoggerSharedState( - Resource.empty(), - LogLimits::getDefault, - logRecordProcessor, - Clock.getDefault(), - DefaultExceptionAttributeResolver.getInstance()); + Resource.empty(), LogLimits::getDefault, logRecordProcessor, Clock.getDefault()); state.shutdown(); state.shutdown(); verify(logRecordProcessor, times(1)).shutdown(); 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 698da1fc8df..3b166f8a163 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 @@ -5,20 +5,17 @@ package io.opentelemetry.sdk.logs; -import static io.opentelemetry.sdk.internal.ExceptionAttributeResolver.EXCEPTION_MESSAGE; -import static io.opentelemetry.sdk.internal.ExceptionAttributeResolver.EXCEPTION_STACKTRACE; -import static io.opentelemetry.sdk.internal.ExceptionAttributeResolver.EXCEPTION_TYPE; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; +import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_MESSAGE; +import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_STACKTRACE; +import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_TYPE; import io.opentelemetry.api.incubator.logs.ExtendedLogRecordBuilder; import io.opentelemetry.api.logs.Logger; -import io.opentelemetry.sdk.internal.ExceptionAttributeResolver; import io.opentelemetry.sdk.logs.export.SimpleLogRecordProcessor; -import io.opentelemetry.sdk.logs.internal.SdkLoggerProviderUtil; import io.opentelemetry.sdk.testing.exporter.InMemoryLogRecordExporter; -import javax.annotation.Nullable; import org.junit.jupiter.api.Test; class ExtendedLoggerBuilderTest { @@ -28,7 +25,7 @@ class ExtendedLoggerBuilderTest { SdkLoggerProvider.builder().addLogRecordProcessor(SimpleLogRecordProcessor.create(exporter)); @Test - void setException_DefaultResolver() { + void setException() { Logger logger = loggerProviderBuilder.build().get("logger"); ((ExtendedLogRecordBuilder) logger.logRecordBuilder()) @@ -48,41 +45,4 @@ void setException_DefaultResolver() { stacktrace.startsWith( "java.lang.Exception: error" + System.lineSeparator())))); } - - @Test - void setException_CustomResolver() { - SdkLoggerProviderUtil.setExceptionAttributeResolver( - loggerProviderBuilder, - new ExceptionAttributeResolver() { - @Override - public String getExceptionType(Throwable throwable) { - return "type"; - } - - @Nullable - @Override - public String getExceptionMessage(Throwable throwable) { - return null; - } - - @Override - public String getExceptionStacktrace(Throwable throwable) { - return "stacktrace"; - } - }); - - Logger logger = loggerProviderBuilder.build().get("logger"); - - ((ExtendedLogRecordBuilder) logger.logRecordBuilder()) - .setException(new Exception("error")) - .emit(); - - assertThat(exporter.getFinishedLogRecordItems()) - .satisfiesExactly( - logRecord -> - assertThat(logRecord) - .hasAttributesSatisfyingExactly( - equalTo(EXCEPTION_TYPE, "type"), - equalTo(EXCEPTION_STACKTRACE, "stacktrace"))); - } } diff --git a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkSpan.java b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkSpan.java index cf140d1fc96..30ee4bbdebf 100644 --- a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkSpan.java +++ b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkSpan.java @@ -5,10 +5,6 @@ package io.opentelemetry.sdk.trace; -import static io.opentelemetry.sdk.internal.ExceptionAttributeResolver.EXCEPTION_MESSAGE; -import static io.opentelemetry.sdk.internal.ExceptionAttributeResolver.EXCEPTION_STACKTRACE; -import static io.opentelemetry.sdk.internal.ExceptionAttributeResolver.EXCEPTION_TYPE; - import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.internal.GuardedBy; @@ -21,7 +17,6 @@ import io.opentelemetry.sdk.common.InstrumentationScopeInfo; import io.opentelemetry.sdk.internal.AttributeUtil; import io.opentelemetry.sdk.internal.AttributesMap; -import io.opentelemetry.sdk.internal.ExceptionAttributeResolver; import io.opentelemetry.sdk.internal.InstrumentationScopeUtil; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.trace.data.EventData; @@ -53,8 +48,6 @@ final class SdkSpan implements ReadWriteSpan { private final SpanContext parentSpanContext; // Handler called when the span starts and ends. private final SpanProcessor spanProcessor; - // Resolves exception.* when an recordException is called - private final ExceptionAttributeResolver exceptionAttributeResolver; // The kind of the span. private final SpanKind kind; // The clock used to get the time. @@ -130,7 +123,6 @@ private SdkSpan( SpanContext parentSpanContext, SpanLimits spanLimits, SpanProcessor spanProcessor, - ExceptionAttributeResolver exceptionAttributeResolver, AnchoredClock clock, Resource resource, @Nullable AttributesMap attributes, @@ -145,7 +137,6 @@ private SdkSpan( this.name = name; this.kind = kind; this.spanProcessor = spanProcessor; - this.exceptionAttributeResolver = exceptionAttributeResolver; this.resource = resource; this.hasEnded = EndState.NOT_ENDED; this.clock = clock; @@ -178,7 +169,6 @@ static SdkSpan startSpan( Context parentContext, SpanLimits spanLimits, SpanProcessor spanProcessor, - ExceptionAttributeResolver exceptionAttributeResolver, Clock tracerClock, Resource resource, @Nullable AttributesMap attributes, @@ -217,7 +207,6 @@ static SdkSpan startSpan( parentSpan.getSpanContext(), spanLimits, spanProcessor, - exceptionAttributeResolver, clock, resource, attributes, @@ -480,18 +469,7 @@ public ReadWriteSpan recordException(Throwable exception, Attributes additionalA AttributesMap.create( spanLimits.getMaxNumberOfAttributes(), spanLimits.getMaxAttributeValueLength()); - String type = exceptionAttributeResolver.getExceptionType(exception); - if (type != null) { - attributes.put(EXCEPTION_TYPE, type); - } - String message = exceptionAttributeResolver.getExceptionMessage(exception); - if (message != null) { - attributes.put(EXCEPTION_MESSAGE, message); - } - String stacktrace = exceptionAttributeResolver.getExceptionStacktrace(exception); - if (stacktrace != null) { - attributes.put(EXCEPTION_STACKTRACE, stacktrace); - } + AttributeUtil.addExceptionAttributes(exception, attributes::put); additionalAttributes.forEach(attributes::put); diff --git a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkSpanBuilder.java b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkSpanBuilder.java index c0f872265ec..b12107c7d43 100644 --- a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkSpanBuilder.java +++ b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkSpanBuilder.java @@ -226,7 +226,6 @@ public Span startSpan() { parentContext, spanLimits, tracerSharedState.getActiveSpanProcessor(), - tracerSharedState.getExceptionAttributesResolver(), tracerSharedState.getClock(), tracerSharedState.getResource(), recordedAttributes, diff --git a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkTracerProvider.java b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkTracerProvider.java index deee9024151..7bd69a24236 100644 --- a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkTracerProvider.java +++ b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkTracerProvider.java @@ -12,7 +12,6 @@ import io.opentelemetry.sdk.common.CompletableResultCode; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; import io.opentelemetry.sdk.internal.ComponentRegistry; -import io.opentelemetry.sdk.internal.ExceptionAttributeResolver; import io.opentelemetry.sdk.internal.ScopeConfigurator; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.trace.internal.SdkTracerProviderUtil; @@ -53,17 +52,10 @@ public static SdkTracerProviderBuilder builder() { Supplier spanLimitsSupplier, Sampler sampler, List spanProcessors, - ScopeConfigurator tracerConfigurator, - ExceptionAttributeResolver exceptionAttributeResolver) { + ScopeConfigurator tracerConfigurator) { this.sharedState = new TracerSharedState( - clock, - idsGenerator, - resource, - spanLimitsSupplier, - sampler, - spanProcessors, - exceptionAttributeResolver); + clock, idsGenerator, resource, spanLimitsSupplier, sampler, spanProcessors); this.tracerSdkComponentRegistry = new ComponentRegistry<>( instrumentationScopeInfo -> diff --git a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkTracerProviderBuilder.java b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkTracerProviderBuilder.java index f83b21fcbbf..f20374cb6fb 100644 --- a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkTracerProviderBuilder.java +++ b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkTracerProviderBuilder.java @@ -10,8 +10,6 @@ import io.opentelemetry.api.trace.Span; import io.opentelemetry.sdk.common.Clock; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; -import io.opentelemetry.sdk.internal.DefaultExceptionAttributeResolver; -import io.opentelemetry.sdk.internal.ExceptionAttributeResolver; import io.opentelemetry.sdk.internal.ScopeConfigurator; import io.opentelemetry.sdk.internal.ScopeConfiguratorBuilder; import io.opentelemetry.sdk.resources.Resource; @@ -37,8 +35,6 @@ public final class SdkTracerProviderBuilder { private Sampler sampler = DEFAULT_SAMPLER; private ScopeConfiguratorBuilder tracerConfiguratorBuilder = TracerConfig.configuratorBuilder(); - private ExceptionAttributeResolver exceptionAttributeResolver = - DefaultExceptionAttributeResolver.getInstance(); /** * Assign a {@link Clock}. {@link Clock} will be used each time a {@link Span} is started, ended @@ -200,21 +196,6 @@ SdkTracerProviderBuilder addTracerConfiguratorCondition( return this; } - /** - * Set the exception attribute resolver, which resolves {@code exception.*} attributes when {@link - * Span#recordException(Throwable)} - * - *

This method is experimental so not public. You may reflectively call it using {@link - * SdkTracerProviderUtil#setExceptionAttributeResolver(SdkTracerProviderBuilder, - * ExceptionAttributeResolver)}. - */ - SdkTracerProviderBuilder setExceptionAttributeResolver( - ExceptionAttributeResolver exceptionAttributeResolver) { - requireNonNull(exceptionAttributeResolver, "exceptionAttributeResolver"); - this.exceptionAttributeResolver = exceptionAttributeResolver; - return this; - } - /** * Create a new {@link SdkTracerProvider} instance with the configuration. * @@ -228,8 +209,7 @@ public SdkTracerProvider build() { spanLimitsSupplier, sampler, spanProcessors, - tracerConfiguratorBuilder.build(), - exceptionAttributeResolver); + tracerConfiguratorBuilder.build()); } SdkTracerProviderBuilder() {} diff --git a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/TracerSharedState.java b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/TracerSharedState.java index 74d43076b97..37bb6675c06 100644 --- a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/TracerSharedState.java +++ b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/TracerSharedState.java @@ -7,7 +7,6 @@ import io.opentelemetry.sdk.common.Clock; import io.opentelemetry.sdk.common.CompletableResultCode; -import io.opentelemetry.sdk.internal.ExceptionAttributeResolver; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.trace.samplers.Sampler; import java.util.List; @@ -27,7 +26,6 @@ final class TracerSharedState { private final Supplier spanLimitsSupplier; private final Sampler sampler; private final SpanProcessor activeSpanProcessor; - private final ExceptionAttributeResolver exceptionAttributeResolver; @Nullable private volatile CompletableResultCode shutdownResult = null; @@ -37,8 +35,7 @@ final class TracerSharedState { Resource resource, Supplier spanLimitsSupplier, Sampler sampler, - List spanProcessors, - ExceptionAttributeResolver exceptionAttributeResolver) { + List spanProcessors) { this.clock = clock; this.idGenerator = idGenerator; this.idGeneratorSafeToSkipIdValidation = idGenerator instanceof RandomIdGenerator; @@ -46,7 +43,6 @@ final class TracerSharedState { this.spanLimitsSupplier = spanLimitsSupplier; this.sampler = sampler; this.activeSpanProcessor = SpanProcessor.composite(spanProcessors); - this.exceptionAttributeResolver = exceptionAttributeResolver; } Clock getClock() { @@ -93,11 +89,6 @@ boolean hasBeenShutdown() { return shutdownResult != null; } - /** Return the {@link ExceptionAttributeResolver}. */ - ExceptionAttributeResolver getExceptionAttributesResolver() { - return exceptionAttributeResolver; - } - /** * Stops tracing, including shutting down processors and set to {@code true} {@link * #hasBeenShutdown()}. diff --git a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/internal/SdkTracerProviderUtil.java b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/internal/SdkTracerProviderUtil.java index f9ca8fafc56..753a312fa1f 100644 --- a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/internal/SdkTracerProviderUtil.java +++ b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/internal/SdkTracerProviderUtil.java @@ -6,7 +6,6 @@ package io.opentelemetry.sdk.trace.internal; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; -import io.opentelemetry.sdk.internal.ExceptionAttributeResolver; import io.opentelemetry.sdk.internal.ScopeConfigurator; import io.opentelemetry.sdk.trace.SdkTracerProvider; import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder; @@ -73,20 +72,4 @@ public static void addTracerConfiguratorCondition( "Error calling addTracerConfiguratorCondition on SdkTracerProviderBuilder", e); } } - - /** Reflectively set exception attribute resolver to the {@link SdkTracerProviderBuilder}. */ - public static void setExceptionAttributeResolver( - SdkTracerProviderBuilder sdkTracerProviderBuilder, - ExceptionAttributeResolver exceptionAttributeResolver) { - try { - Method method = - SdkTracerProviderBuilder.class.getDeclaredMethod( - "setExceptionAttributeResolver", ExceptionAttributeResolver.class); - method.setAccessible(true); - method.invoke(sdkTracerProviderBuilder, exceptionAttributeResolver); - } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { - throw new IllegalStateException( - "Error calling setExceptionAttributeResolver on SdkTracerProviderBuilder", e); - } - } } diff --git a/sdk/trace/src/test/java/io/opentelemetry/sdk/trace/SdkSpanTest.java b/sdk/trace/src/test/java/io/opentelemetry/sdk/trace/SdkSpanTest.java index bd7fc11ec06..1ec553f75cb 100644 --- a/sdk/trace/src/test/java/io/opentelemetry/sdk/trace/SdkSpanTest.java +++ b/sdk/trace/src/test/java/io/opentelemetry/sdk/trace/SdkSpanTest.java @@ -14,7 +14,6 @@ import static io.opentelemetry.api.common.AttributeKey.stringArrayKey; import static io.opentelemetry.api.common.AttributeKey.stringKey; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static java.util.stream.Collectors.joining; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; @@ -39,8 +38,6 @@ import io.opentelemetry.context.Context; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; import io.opentelemetry.sdk.internal.AttributesMap; -import io.opentelemetry.sdk.internal.DefaultExceptionAttributeResolver; -import io.opentelemetry.sdk.internal.ExceptionAttributeResolver; import io.opentelemetry.sdk.internal.InstrumentationScopeUtil; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.testing.time.TestClock; @@ -947,8 +944,7 @@ void addLink() { .build(), parentSpanId, null, - null, - DefaultExceptionAttributeResolver.getInstance()); + null); try { Span span1 = createTestSpan(SpanKind.INTERNAL); Span span2 = createTestSpan(SpanKind.INTERNAL); @@ -1038,7 +1034,6 @@ void addLink_FaultIn() { Context.root(), SpanLimits.getDefault(), spanProcessor, - DefaultExceptionAttributeResolver.getInstance(), testClock, resource, null, @@ -1299,47 +1294,6 @@ void recordException_SpanLimits() { assertThat(event.getTotalAttributeCount() - event.getAttributes().size()).isPositive(); } - @Test - void recordException_CustomResolver() { - ExceptionAttributeResolver exceptionAttributeResolver = - new ExceptionAttributeResolver() { - @Override - public String getExceptionType(Throwable throwable) { - return "type"; - } - - @Nullable - @Override - public String getExceptionMessage(Throwable throwable) { - return null; - } - - @Override - public String getExceptionStacktrace(Throwable throwable) { - return "stacktrace"; - } - }; - - SdkSpan span = - createTestSpan( - SpanKind.INTERNAL, - SpanLimits.getDefault(), - parentSpanId, - null, - Collections.singletonList(link), - exceptionAttributeResolver); - - span.recordException(new IllegalStateException("error")); - - List events = span.toSpanData().getEvents(); - assertThat(events.size()).isEqualTo(1); - EventData event = events.get(0); - assertThat(event) - .hasAttributesSatisfyingExactly( - equalTo(ExceptionAttributeResolver.EXCEPTION_TYPE, "type"), - equalTo(ExceptionAttributeResolver.EXCEPTION_STACKTRACE, "stacktrace")); - } - @Test void badArgsIgnored() { SdkSpan span = createTestRootSpan(); @@ -1391,7 +1345,6 @@ void onStartOnEndNotRequired() { Context.root(), spanLimits, spanProcessor, - DefaultExceptionAttributeResolver.getInstance(), testClock, resource, AttributesMap.create( @@ -1473,8 +1426,7 @@ private SdkSpan createTestSpanWithAttributes(Map attribute SpanLimits.getDefault(), null, attributesMap, - Collections.singletonList(link), - DefaultExceptionAttributeResolver.getInstance()); + Collections.singletonList(link)); } private SdkSpan createTestRootSpan() { @@ -1483,28 +1435,17 @@ private SdkSpan createTestRootSpan() { SpanLimits.getDefault(), SpanId.getInvalid(), null, - Collections.singletonList(link), - DefaultExceptionAttributeResolver.getInstance()); + Collections.singletonList(link)); } private SdkSpan createTestSpan(SpanKind kind) { return createTestSpan( - kind, - SpanLimits.getDefault(), - parentSpanId, - null, - Collections.singletonList(link), - DefaultExceptionAttributeResolver.getInstance()); + kind, SpanLimits.getDefault(), parentSpanId, null, Collections.singletonList(link)); } private SdkSpan createTestSpan(SpanLimits config) { return createTestSpan( - SpanKind.INTERNAL, - config, - parentSpanId, - null, - Collections.singletonList(link), - DefaultExceptionAttributeResolver.getInstance()); + SpanKind.INTERNAL, config, parentSpanId, null, Collections.singletonList(link)); } private SdkSpan createTestSpan( @@ -1512,8 +1453,7 @@ private SdkSpan createTestSpan( SpanLimits config, @Nullable String parentSpanId, @Nullable AttributesMap attributes, - @Nullable List links, - ExceptionAttributeResolver exceptionAttributeResolver) { + @Nullable List links) { List linksCopy = links == null ? new ArrayList<>() : new ArrayList<>(links); SdkSpan span = @@ -1530,7 +1470,6 @@ private SdkSpan createTestSpan( Context.root(), config, spanProcessor, - exceptionAttributeResolver, testClock, resource, attributes, @@ -1618,7 +1557,6 @@ void testAsSpanData() { Context.root(), spanLimits, spanProcessor, - DefaultExceptionAttributeResolver.getInstance(), clock, resource, attributesWithCapacity, diff --git a/sdk/trace/src/test/java/io/opentelemetry/sdk/trace/SdkTracerProviderTest.java b/sdk/trace/src/test/java/io/opentelemetry/sdk/trace/SdkTracerProviderTest.java index 191b6ad6fec..578fd9099ee 100644 --- a/sdk/trace/src/test/java/io/opentelemetry/sdk/trace/SdkTracerProviderTest.java +++ b/sdk/trace/src/test/java/io/opentelemetry/sdk/trace/SdkTracerProviderTest.java @@ -9,8 +9,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import io.opentelemetry.api.common.Attributes; @@ -20,8 +18,6 @@ import io.opentelemetry.sdk.common.Clock; import io.opentelemetry.sdk.common.CompletableResultCode; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; -import io.opentelemetry.sdk.internal.DefaultExceptionAttributeResolver; -import io.opentelemetry.sdk.internal.ExceptionAttributeResolver; import io.opentelemetry.sdk.internal.ScopeConfigurator; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.trace.internal.SdkTracerProviderUtil; @@ -286,19 +282,4 @@ void suppliesDefaultTracerForEmptyName() { assertThat(tracer.getInstrumentationScopeInfo().getName()) .isEqualTo(SdkTracerProvider.DEFAULT_TRACER_NAME); } - - @Test - void exceptionAttributeResolver() { - SdkTracerProviderBuilder builder = SdkTracerProvider.builder().addSpanProcessor(spanProcessor); - ExceptionAttributeResolver exceptionAttributeResolver = - spy(DefaultExceptionAttributeResolver.getInstance()); - SdkTracerProviderUtil.setExceptionAttributeResolver(builder, exceptionAttributeResolver); - - Exception exception = new Exception("error"); - builder.build().get("tracer").spanBuilder("span").startSpan().recordException(exception).end(); - - verify(exceptionAttributeResolver).getExceptionType(exception); - verify(exceptionAttributeResolver).getExceptionMessage(exception); - verify(exceptionAttributeResolver).getExceptionStacktrace(exception); - } } From 7f06c87df22c9be17bcfcdde834a043609e9bffe Mon Sep 17 00:00:00 2001 From: Jack Berg Date: Thu, 10 Apr 2025 15:48:46 -0500 Subject: [PATCH 4/4] PR feedback --- .../kotlin/otel.java-conventions.gradle.kts | 4 +- .../opentelemetry/sdk/trace/SdkSpanTest.java | 42 +++++++++---------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/buildSrc/src/main/kotlin/otel.java-conventions.gradle.kts b/buildSrc/src/main/kotlin/otel.java-conventions.gradle.kts index b5eb4b8ab03..d1957a45ebd 100644 --- a/buildSrc/src/main/kotlin/otel.java-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/otel.java-conventions.gradle.kts @@ -87,7 +87,9 @@ tasks { // https://groups.google.com/forum/#!topic/bazel-discuss/_R3A9TJSoPM "-Xlint:-processing", // We suppress the "options" warning because it prevents compilation on modern JDKs - "-Xlint:-options" + "-Xlint:-options", + // Fail build on any warning + "-Werror", ), ) } diff --git a/sdk/trace/src/test/java/io/opentelemetry/sdk/trace/SdkSpanTest.java b/sdk/trace/src/test/java/io/opentelemetry/sdk/trace/SdkSpanTest.java index 1ec553f75cb..625313dbcb5 100644 --- a/sdk/trace/src/test/java/io/opentelemetry/sdk/trace/SdkSpanTest.java +++ b/sdk/trace/src/test/java/io/opentelemetry/sdk/trace/SdkSpanTest.java @@ -15,7 +15,6 @@ import static io.opentelemetry.api.common.AttributeKey.stringKey; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static java.util.stream.Collectors.joining; -import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; @@ -68,7 +67,6 @@ import java.util.stream.IntStream; import java.util.stream.Stream; import javax.annotation.Nullable; -import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -443,7 +441,7 @@ void getAttribute() { void getAttributes() { SdkSpan span = createTestSpanWithAttributes(attributes); try { - Assertions.assertThat(span.getAttributes()) + assertThat(span.getAttributes()) .isEqualTo( Attributes.builder() .put("MyBooleanAttributeKey", false) @@ -459,7 +457,7 @@ void getAttributes() { void getAttributes_Empty() { SdkSpan span = createTestSpan(SpanKind.INTERNAL); try { - Assertions.assertThat(span.getAttributes()).isEqualTo(Attributes.empty()); + assertThat(span.getAttributes()).isEqualTo(Attributes.empty()); } finally { span.end(); } @@ -793,48 +791,48 @@ void addEvent() { } List events = span.toSpanData().getEvents(); assertThat(events).hasSize(6); - Assertions.assertThat(events.get(0)) + assertThat(events.get(0)) .satisfies( event -> { assertThat(event.getName()).isEqualTo("event1"); - Assertions.assertThat(event.getAttributes()).isEqualTo(Attributes.empty()); + assertThat(event.getAttributes()).isEqualTo(Attributes.empty()); assertThat(event.getEpochNanos()).isEqualTo(START_EPOCH_NANOS); }); - Assertions.assertThat(events.get(1)) + assertThat(events.get(1)) .satisfies( event -> { assertThat(event.getName()).isEqualTo("event2"); - Assertions.assertThat(event.getAttributes()) + assertThat(event.getAttributes()) .isEqualTo(Attributes.of(stringKey("e1key"), "e1Value")); assertThat(event.getEpochNanos()).isEqualTo(START_EPOCH_NANOS); }); - Assertions.assertThat(events.get(2)) + assertThat(events.get(2)) .satisfies( event -> { assertThat(event.getName()).isEqualTo("event3"); - Assertions.assertThat(event.getAttributes()).isEqualTo(Attributes.empty()); + assertThat(event.getAttributes()).isEqualTo(Attributes.empty()); assertThat(event.getEpochNanos()).isEqualTo(TimeUnit.SECONDS.toNanos(10)); }); - Assertions.assertThat(events.get(3)) + assertThat(events.get(3)) .satisfies( event -> { assertThat(event.getName()).isEqualTo("event4"); - Assertions.assertThat(event.getAttributes()).isEqualTo(Attributes.empty()); + assertThat(event.getAttributes()).isEqualTo(Attributes.empty()); assertThat(event.getEpochNanos()).isEqualTo(TimeUnit.SECONDS.toNanos(20)); }); - Assertions.assertThat(events.get(4)) + assertThat(events.get(4)) .satisfies( event -> { assertThat(event.getName()).isEqualTo("event5"); - Assertions.assertThat(event.getAttributes()) + assertThat(event.getAttributes()) .isEqualTo(Attributes.builder().put("foo", "bar").build()); assertThat(event.getEpochNanos()).isEqualTo(TimeUnit.MILLISECONDS.toNanos(30)); }); - Assertions.assertThat(events.get(5)) + assertThat(events.get(5)) .satisfies( event -> { assertThat(event.getName()).isEqualTo("event6"); - Assertions.assertThat(event.getAttributes()) + assertThat(event.getAttributes()) .isEqualTo(Attributes.builder().put("foo", "bar").build()); assertThat(event.getEpochNanos()).isEqualTo(TimeUnit.MILLISECONDS.toNanos(1000)); }); @@ -983,11 +981,11 @@ void addLink() { .satisfiesExactly( link -> { assertThat(link.getSpanContext()).isEqualTo(span1.getSpanContext()); - Assertions.assertThat(link.getAttributes()).isEqualTo(Attributes.empty()); + assertThat(link.getAttributes()).isEqualTo(Attributes.empty()); }, link -> { assertThat(link.getSpanContext()).isEqualTo(span2.getSpanContext()); - Assertions.assertThat(link.getAttributes()) + assertThat(link.getAttributes()) .isEqualTo( Attributes.builder() .put("key1", true) @@ -1139,7 +1137,7 @@ void droppingEvents() { EventData expectedEvent = EventData.create( START_EPOCH_NANOS + i * NANOS_PER_SECOND, "event2", Attributes.empty(), 0); - Assertions.assertThat(spanData.getEvents().get(i)).isEqualTo(expectedEvent); + assertThat(spanData.getEvents().get(i)).isEqualTo(expectedEvent); assertThat(spanData.getTotalRecordedEvents()).isEqualTo(2 * maxNumberOfEvents); } } finally { @@ -1151,7 +1149,7 @@ void droppingEvents() { EventData expectedEvent = EventData.create( START_EPOCH_NANOS + i * NANOS_PER_SECOND, "event2", Attributes.empty(), 0); - Assertions.assertThat(spanData.getEvents().get(i)).isEqualTo(expectedEvent); + assertThat(spanData.getEvents().get(i)).isEqualTo(expectedEvent); } } @@ -1183,7 +1181,7 @@ void recordException() { .isEqualTo(exception.getClass().getName()); assertThat(event.getAttributes().get(stringKey("exception.stacktrace"))).isEqualTo(stacktrace); assertThat(event.getAttributes().size()).isEqualTo(3); - Assertions.assertThat(event) + assertThat(event) .isInstanceOfSatisfying( ExceptionEventData.class, exceptionEvent -> { @@ -1271,7 +1269,7 @@ void recordException_additionalAttributes() { assertThat(event.getAttributes().get(stringKey("exception.stacktrace"))).isEqualTo(stacktrace); assertThat(event.getAttributes().size()).isEqualTo(4); - Assertions.assertThat(event) + assertThat(event) .isInstanceOfSatisfying( ExceptionEventData.class, exceptionEvent -> {