Skip to content

Commit 9f2ecde

Browse files
committed
Add ExtendedLogRecordBuilder#setException, make exception.* resolution configurable
1 parent 4d34b53 commit 9f2ecde

File tree

21 files changed

+511
-102
lines changed

21 files changed

+511
-102
lines changed

api/incubator/src/main/java/io/opentelemetry/api/incubator/logs/ExtendedDefaultLogger.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ public ExtendedLogRecordBuilder setEventName(String eventName) {
4040
return this;
4141
}
4242

43+
@Override
44+
public ExtendedLogRecordBuilder setException(Throwable throwable) {
45+
return this;
46+
}
47+
4348
@Override
4449
public LogRecordBuilder setTimestamp(long timestamp, TimeUnit unit) {
4550
return this;

api/incubator/src/main/java/io/opentelemetry/api/incubator/logs/ExtendedLogRecordBuilder.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,7 @@ public interface ExtendedLogRecordBuilder extends LogRecordBuilder {
1919
* record with a non-empty event name is an Event.
2020
*/
2121
ExtendedLogRecordBuilder setEventName(String eventName);
22+
23+
/** Set standard {@code exception.*} attributes based on the {@code throwable}. */
24+
ExtendedLogRecordBuilder setException(Throwable throwable);
2225
}

buildSrc/src/main/kotlin/otel.java-conventions.gradle.kts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,7 @@ tasks {
8787
// https://groups.google.com/forum/#!topic/bazel-discuss/_R3A9TJSoPM
8888
"-Xlint:-processing",
8989
// We suppress the "options" warning because it prevents compilation on modern JDKs
90-
"-Xlint:-options",
91-
// Fail build on any warning
92-
"-Werror",
90+
"-Xlint:-options"
9391
),
9492
)
9593
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.sdk.internal;
7+
8+
import java.io.PrintWriter;
9+
import java.io.StringWriter;
10+
import javax.annotation.Nullable;
11+
12+
/**
13+
* This class is internal and experimental. Its APIs are unstable and can change at any time. Its
14+
* APIs (or a version of them) may be promoted to the public stable API in the future, but no
15+
* guarantees are made.
16+
*/
17+
public final class DefaultExceptionAttributeResolver implements ExceptionAttributeResolver {
18+
19+
private static final DefaultExceptionAttributeResolver INSTANCE =
20+
new DefaultExceptionAttributeResolver();
21+
22+
private DefaultExceptionAttributeResolver() {}
23+
24+
public static ExceptionAttributeResolver getInstance() {
25+
return INSTANCE;
26+
}
27+
28+
@Override
29+
@Nullable
30+
public String getExceptionType(Throwable throwable) {
31+
return throwable.getClass().getCanonicalName();
32+
}
33+
34+
@Override
35+
@Nullable
36+
public String getExceptionMessage(Throwable throwable) {
37+
return throwable.getMessage();
38+
}
39+
40+
@Override
41+
@Nullable
42+
public String getExceptionStacktrace(Throwable throwable) {
43+
StringWriter stringWriter = new StringWriter();
44+
try (PrintWriter printWriter = new PrintWriter(stringWriter)) {
45+
throwable.printStackTrace(printWriter);
46+
}
47+
return stringWriter.toString();
48+
}
49+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.sdk.internal;
7+
8+
import io.opentelemetry.api.common.AttributeKey;
9+
import javax.annotation.Nullable;
10+
11+
/**
12+
* Implementations resolve {@code exception.*} attributes attached to span events, logs, etc.
13+
*
14+
* <p>This class is internal and experimental. Its APIs are unstable and can change at any time. Its
15+
* APIs (or a version of them) may be promoted to the public stable API in the future, but no
16+
* guarantees are made.
17+
*/
18+
public interface ExceptionAttributeResolver {
19+
20+
AttributeKey<String> EXCEPTION_TYPE = AttributeKey.stringKey("exception.type");
21+
AttributeKey<String> EXCEPTION_MESSAGE = AttributeKey.stringKey("exception.message");
22+
AttributeKey<String> EXCEPTION_STACKTRACE = AttributeKey.stringKey("exception.stacktrace");
23+
24+
/**
25+
* Resolve the {@link #EXCEPTION_TYPE} attribute from the {@code throwable}, or {@code null} if no
26+
* value should be set.
27+
*/
28+
@Nullable
29+
String getExceptionType(Throwable throwable);
30+
31+
/**
32+
* Resolve the {@link #EXCEPTION_MESSAGE} attribute from the {@code throwable}, or {@code null} if
33+
* no value should be set.
34+
*/
35+
@Nullable
36+
String getExceptionMessage(Throwable throwable);
37+
38+
/**
39+
* Resolve the {@link #EXCEPTION_STACKTRACE} attribute from the {@code throwable}, or {@code null}
40+
* if no value should be set.
41+
*/
42+
@Nullable
43+
String getExceptionStacktrace(Throwable throwable);
44+
}

sdk/logs/src/main/java/io/opentelemetry/sdk/logs/ExtendedSdkLogRecordBuilder.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ public ExtendedSdkLogRecordBuilder setEventName(String eventName) {
2929
return this;
3030
}
3131

32+
@Override
33+
public ExtendedSdkLogRecordBuilder setException(Throwable throwable) {
34+
super.setException(throwable);
35+
return this;
36+
}
37+
3238
@Override
3339
public ExtendedSdkLogRecordBuilder setTimestamp(long timestamp, TimeUnit unit) {
3440
super.setTimestamp(timestamp, unit);

sdk/logs/src/main/java/io/opentelemetry/sdk/logs/LoggerSharedState.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import io.opentelemetry.sdk.common.Clock;
99
import io.opentelemetry.sdk.common.CompletableResultCode;
10+
import io.opentelemetry.sdk.internal.ExceptionAttributeResolver;
1011
import io.opentelemetry.sdk.resources.Resource;
1112
import java.util.function.Supplier;
1213
import javax.annotation.Nullable;
@@ -21,17 +22,20 @@ final class LoggerSharedState {
2122
private final Supplier<LogLimits> logLimitsSupplier;
2223
private final LogRecordProcessor logRecordProcessor;
2324
private final Clock clock;
25+
private final ExceptionAttributeResolver exceptionAttributeResolver;
2426
@Nullable private volatile CompletableResultCode shutdownResult = null;
2527

2628
LoggerSharedState(
2729
Resource resource,
2830
Supplier<LogLimits> logLimitsSupplier,
2931
LogRecordProcessor logRecordProcessor,
30-
Clock clock) {
32+
Clock clock,
33+
ExceptionAttributeResolver exceptionAttributeResolver) {
3134
this.resource = resource;
3235
this.logLimitsSupplier = logLimitsSupplier;
3336
this.logRecordProcessor = logRecordProcessor;
3437
this.clock = clock;
38+
this.exceptionAttributeResolver = exceptionAttributeResolver;
3539
}
3640

3741
Resource getResource() {
@@ -50,6 +54,10 @@ Clock getClock() {
5054
return clock;
5155
}
5256

57+
ExceptionAttributeResolver getExceptionAttributeResolver() {
58+
return exceptionAttributeResolver;
59+
}
60+
5361
boolean hasBeenShutdown() {
5462
return shutdownResult != null;
5563
}

sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLogRecordBuilder.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55

66
package io.opentelemetry.sdk.logs;
77

8+
import static io.opentelemetry.sdk.internal.ExceptionAttributeResolver.EXCEPTION_MESSAGE;
9+
import static io.opentelemetry.sdk.internal.ExceptionAttributeResolver.EXCEPTION_STACKTRACE;
10+
import static io.opentelemetry.sdk.internal.ExceptionAttributeResolver.EXCEPTION_TYPE;
11+
812
import io.opentelemetry.api.common.AttributeKey;
913
import io.opentelemetry.api.common.Value;
1014
import io.opentelemetry.api.logs.LogRecordBuilder;
@@ -13,6 +17,7 @@
1317
import io.opentelemetry.context.Context;
1418
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
1519
import io.opentelemetry.sdk.internal.AttributesMap;
20+
import io.opentelemetry.sdk.internal.ExceptionAttributeResolver;
1621
import java.time.Instant;
1722
import java.util.concurrent.TimeUnit;
1823
import javax.annotation.Nullable;
@@ -46,6 +51,28 @@ SdkLogRecordBuilder setEventName(String eventName) {
4651
return this;
4752
}
4853

54+
// accessible via ExtendedSdkLogRecordBuilder
55+
SdkLogRecordBuilder setException(Throwable throwable) {
56+
if (throwable == null) {
57+
return this;
58+
}
59+
ExceptionAttributeResolver exceptionAttributeResolver =
60+
loggerSharedState.getExceptionAttributeResolver();
61+
String type = exceptionAttributeResolver.getExceptionType(throwable);
62+
if (type != null) {
63+
setAttribute(EXCEPTION_TYPE, type);
64+
}
65+
String message = exceptionAttributeResolver.getExceptionMessage(throwable);
66+
if (message != null) {
67+
setAttribute(EXCEPTION_MESSAGE, message);
68+
}
69+
String stacktrace = exceptionAttributeResolver.getExceptionStacktrace(throwable);
70+
if (stacktrace != null) {
71+
setAttribute(EXCEPTION_STACKTRACE, stacktrace);
72+
}
73+
return this;
74+
}
75+
4976
@Override
5077
public SdkLogRecordBuilder setTimestamp(long timestamp, TimeUnit unit) {
5178
this.timestampEpochNanos = unit.toNanos(timestamp);

sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLoggerProvider.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import io.opentelemetry.sdk.common.CompletableResultCode;
1414
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
1515
import io.opentelemetry.sdk.internal.ComponentRegistry;
16+
import io.opentelemetry.sdk.internal.ExceptionAttributeResolver;
1617
import io.opentelemetry.sdk.internal.ScopeConfigurator;
1718
import io.opentelemetry.sdk.logs.internal.LoggerConfig;
1819
import io.opentelemetry.sdk.resources.Resource;
@@ -53,10 +54,12 @@ public static SdkLoggerProviderBuilder builder() {
5354
Supplier<LogLimits> logLimitsSupplier,
5455
List<LogRecordProcessor> processors,
5556
Clock clock,
56-
ScopeConfigurator<LoggerConfig> loggerConfigurator) {
57+
ScopeConfigurator<LoggerConfig> loggerConfigurator,
58+
ExceptionAttributeResolver exceptionAttributeResolver) {
5759
LogRecordProcessor logRecordProcessor = LogRecordProcessor.composite(processors);
5860
this.sharedState =
59-
new LoggerSharedState(resource, logLimitsSupplier, logRecordProcessor, clock);
61+
new LoggerSharedState(
62+
resource, logLimitsSupplier, logRecordProcessor, clock, exceptionAttributeResolver);
6063
this.loggerComponentRegistry =
6164
new ComponentRegistry<>(
6265
instrumentationScopeInfo ->

sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLoggerProviderBuilder.java

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
import io.opentelemetry.context.Context;
1313
import io.opentelemetry.sdk.common.Clock;
1414
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
15+
import io.opentelemetry.sdk.internal.DefaultExceptionAttributeResolver;
16+
import io.opentelemetry.sdk.internal.ExceptionAttributeResolver;
1517
import io.opentelemetry.sdk.internal.ScopeConfigurator;
1618
import io.opentelemetry.sdk.internal.ScopeConfiguratorBuilder;
1719
import io.opentelemetry.sdk.logs.data.LogRecordData;
@@ -37,6 +39,8 @@ public final class SdkLoggerProviderBuilder {
3739
private Clock clock = Clock.getDefault();
3840
private ScopeConfiguratorBuilder<LoggerConfig> loggerConfiguratorBuilder =
3941
LoggerConfig.configuratorBuilder();
42+
private ExceptionAttributeResolver exceptionAttributeResolver =
43+
DefaultExceptionAttributeResolver.getInstance();
4044

4145
SdkLoggerProviderBuilder() {}
4246

@@ -149,13 +153,33 @@ SdkLoggerProviderBuilder addLoggerConfiguratorCondition(
149153
return this;
150154
}
151155

156+
/**
157+
* Set the exception attribute resolver, which resolves {@code exception.*} attributes an
158+
* exception is set on a log.
159+
*
160+
* <p>This method is experimental so not public. You may reflectively call it using {@link
161+
* SdkLoggerProviderUtil#setExceptionAttributeResolver(SdkLoggerProviderBuilder,
162+
* ExceptionAttributeResolver)}.
163+
*/
164+
SdkLoggerProviderBuilder setExceptionAttributeResolver(
165+
ExceptionAttributeResolver exceptionAttributeResolver) {
166+
requireNonNull(exceptionAttributeResolver, "exceptionAttributeResolver");
167+
this.exceptionAttributeResolver = exceptionAttributeResolver;
168+
return this;
169+
}
170+
152171
/**
153172
* Create a {@link SdkLoggerProvider} instance.
154173
*
155174
* @return an instance configured with the provided options
156175
*/
157176
public SdkLoggerProvider build() {
158177
return new SdkLoggerProvider(
159-
resource, logLimitsSupplier, logRecordProcessors, clock, loggerConfiguratorBuilder.build());
178+
resource,
179+
logLimitsSupplier,
180+
logRecordProcessors,
181+
clock,
182+
loggerConfiguratorBuilder.build(),
183+
exceptionAttributeResolver);
160184
}
161185
}

0 commit comments

Comments
 (0)