Skip to content

Commit e5ccd7d

Browse files
committed
Add opt-in support for emitting exceptions as log signals
1 parent 308da8a commit e5ccd7d

18 files changed

Lines changed: 564 additions & 15 deletions

File tree

instrumentation-api-incubator/build.gradle.kts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,21 @@ tasks {
102102
inputs.dir(jflexOutputDir)
103103
}
104104

105+
val testExceptionSignalLogs by registering(Test::class) {
106+
testClassesDirs = sourceSets.test.get().output.classesDirs
107+
classpath = sourceSets.test.get().runtimeClasspath
108+
jvmArgs("-Dotel.semconv.exception.signal.opt-in=logs")
109+
inputs.dir(jflexOutputDir)
110+
}
111+
112+
val testExceptionSignalLogsDup by registering(Test::class) {
113+
testClassesDirs = sourceSets.test.get().output.classesDirs
114+
classpath = sourceSets.test.get().runtimeClasspath
115+
jvmArgs("-Dotel.semconv.exception.signal.opt-in=logs/dup")
116+
inputs.dir(jflexOutputDir)
117+
}
118+
105119
check {
106-
dependsOn(testStableSemconv, testBothSemconv)
120+
dependsOn(testStableSemconv, testBothSemconv, testExceptionSignalLogs, testExceptionSignalLogsDup)
107121
}
108122
}

instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/builder/internal/DefaultHttpClientInstrumenterBuilder.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import io.opentelemetry.instrumentation.api.incubator.config.internal.CommonConfig;
1515
import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpClientExperimentalMetrics;
1616
import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpClientServicePeerAttributesExtractor;
17+
import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpExceptionEventExtractors;
1718
import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpExperimentalAttributesExtractor;
1819
import io.opentelemetry.instrumentation.api.incubator.semconv.http.internal.HttpClientUrlTemplateUtil;
1920
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
@@ -223,6 +224,7 @@ public Instrumenter<REQUEST, RESPONSE> build() {
223224
.addAttributesExtractors(additionalExtractors)
224225
.addOperationMetrics(HttpClientMetrics.get())
225226
.setSchemaUrl(SchemaUrls.V1_37_0);
227+
Experimental.setExceptionEventExtractor(builder, HttpExceptionEventExtractors.client());
226228
if (emitExperimentalHttpClientTelemetry) {
227229
builder
228230
.addAttributesExtractor(HttpExperimentalAttributesExtractor.create(attributesGetter))

instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/builder/internal/DefaultHttpServerInstrumenterBuilder.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import io.opentelemetry.api.OpenTelemetry;
1212
import io.opentelemetry.context.propagation.TextMapGetter;
1313
import io.opentelemetry.instrumentation.api.incubator.config.internal.CommonConfig;
14+
import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpExceptionEventExtractors;
1415
import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpExperimentalAttributesExtractor;
1516
import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpServerExperimentalMetrics;
1617
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
@@ -19,6 +20,7 @@
1920
import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor;
2021
import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor;
2122
import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusExtractor;
23+
import io.opentelemetry.instrumentation.api.internal.Experimental;
2224
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerAttributesExtractor;
2325
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerAttributesExtractorBuilder;
2426
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerAttributesGetter;
@@ -215,6 +217,7 @@ public InstrumenterBuilder<REQUEST, RESPONSE> instrumenterBuilder() {
215217
.addContextCustomizer(httpServerRouteBuilder.build())
216218
.addOperationMetrics(HttpServerMetrics.get())
217219
.setSchemaUrl(SchemaUrls.V1_37_0);
220+
Experimental.setExceptionEventExtractor(builder, HttpExceptionEventExtractors.server());
218221
if (emitExperimentalHttpServerTelemetry) {
219222
builder
220223
.addAttributesExtractor(HttpExperimentalAttributesExtractor.create(attributesGetter))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.api.incubator.instrumenter;
7+
8+
import io.opentelemetry.api.logs.LogRecordBuilder;
9+
import io.opentelemetry.api.logs.Severity;
10+
import io.opentelemetry.api.trace.Span;
11+
import io.opentelemetry.context.Context;
12+
import io.opentelemetry.instrumentation.api.instrumenter.LocalRootSpan;
13+
14+
/**
15+
* {@link ExceptionEventExtractor} that sets the given event name, using {@link Severity#ERROR} when
16+
* the span is a local root, and {@link Severity#DEBUG} otherwise.
17+
*/
18+
final class DefaultExceptionEventExtractor<REQUEST> implements ExceptionEventExtractor<REQUEST> {
19+
20+
private final String eventName;
21+
22+
static <REQUEST> ExceptionEventExtractor<REQUEST> create(String eventName) {
23+
return new DefaultExceptionEventExtractor<>(eventName);
24+
}
25+
26+
private DefaultExceptionEventExtractor(String eventName) {
27+
this.eventName = eventName;
28+
}
29+
30+
@Override
31+
public void extract(LogRecordBuilder logRecordBuilder, Context context, REQUEST request) {
32+
logRecordBuilder.setEventName(eventName);
33+
Span currentSpan = Span.fromContext(context);
34+
Span localRootSpan = LocalRootSpan.fromContextOrNull(context);
35+
if (currentSpan.equals(localRootSpan)) {
36+
logRecordBuilder.setSeverity(Severity.ERROR);
37+
} else {
38+
logRecordBuilder.setSeverity(Severity.DEBUG);
39+
}
40+
}
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.api.incubator.instrumenter;
7+
8+
import io.opentelemetry.api.logs.LogRecordBuilder;
9+
import io.opentelemetry.api.logs.Severity;
10+
import io.opentelemetry.context.Context;
11+
import io.opentelemetry.instrumentation.api.internal.InternalExceptionEventExtractor;
12+
13+
/**
14+
* Extractor that populates the exception event {@link LogRecordBuilder} for a request. This allows
15+
* instrumentations to set the event name, severity, and any additional attributes on the exception
16+
* log event.
17+
*
18+
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
19+
* at any time.
20+
*/
21+
@FunctionalInterface
22+
public interface ExceptionEventExtractor<REQUEST> extends InternalExceptionEventExtractor<REQUEST> {
23+
24+
/**
25+
* Returns an {@link ExceptionEventExtractor} that always sets the given event name and severity.
26+
*/
27+
static <REQUEST> ExceptionEventExtractor<REQUEST> create(String eventName, Severity severity) {
28+
return (logRecordBuilder, context, request) -> {
29+
logRecordBuilder.setEventName(eventName);
30+
logRecordBuilder.setSeverity(severity);
31+
};
32+
}
33+
34+
/**
35+
* Returns an {@link ExceptionEventExtractor} that sets the given event name, with {@link
36+
* Severity#ERROR} for local root spans and {@link Severity#DEBUG} otherwise.
37+
*/
38+
static <REQUEST> ExceptionEventExtractor<REQUEST> create(String eventName) {
39+
return DefaultExceptionEventExtractor.create(eventName);
40+
}
41+
42+
/**
43+
* Populates the exception event {@link LogRecordBuilder} with the event name, severity, and any
44+
* additional attributes for the given context and request.
45+
*/
46+
@Override
47+
void extract(LogRecordBuilder logRecordBuilder, Context context, REQUEST request);
48+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.api.incubator.semconv.db;
7+
8+
import io.opentelemetry.api.logs.Severity;
9+
import io.opentelemetry.instrumentation.api.incubator.instrumenter.ExceptionEventExtractor;
10+
11+
/**
12+
* {@link ExceptionEventExtractor} constants for DB client instrumentations.
13+
*
14+
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
15+
* at any time.
16+
*/
17+
public final class DbExceptionEventExtractors {
18+
19+
/** Exception event extractor for DB client spans. */
20+
public static <REQUEST> ExceptionEventExtractor<REQUEST> client() {
21+
return ExceptionEventExtractor.create("db.client.operation.exception", Severity.WARN);
22+
}
23+
24+
private DbExceptionEventExtractors() {}
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.api.incubator.semconv.genai;
7+
8+
import io.opentelemetry.api.logs.Severity;
9+
import io.opentelemetry.instrumentation.api.incubator.instrumenter.ExceptionEventExtractor;
10+
11+
/**
12+
* {@link ExceptionEventExtractor} constants for GenAI client instrumentations.
13+
*
14+
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
15+
* at any time.
16+
*/
17+
public final class GenAiExceptionEventExtractors {
18+
19+
/** Exception event extractor for GenAI client spans. */
20+
public static <REQUEST> ExceptionEventExtractor<REQUEST> client() {
21+
return ExceptionEventExtractor.create("gen_ai.client.operation.exception", Severity.WARN);
22+
}
23+
24+
private GenAiExceptionEventExtractors() {}
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.api.incubator.semconv.http;
7+
8+
import io.opentelemetry.api.logs.Severity;
9+
import io.opentelemetry.instrumentation.api.incubator.instrumenter.ExceptionEventExtractor;
10+
11+
/**
12+
* {@link ExceptionEventExtractor} constants for HTTP client and server instrumentations.
13+
*
14+
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
15+
* at any time.
16+
*/
17+
public final class HttpExceptionEventExtractors {
18+
19+
/** Exception event extractor for HTTP client spans. */
20+
public static <REQUEST> ExceptionEventExtractor<REQUEST> client() {
21+
return ExceptionEventExtractor.create("http.client.request.exception", Severity.WARN);
22+
}
23+
24+
/** Exception event extractor for HTTP server spans. */
25+
public static <REQUEST> ExceptionEventExtractor<REQUEST> server() {
26+
return ExceptionEventExtractor.create("http.server.request.exception", Severity.ERROR);
27+
}
28+
29+
private HttpExceptionEventExtractors() {}
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.api.incubator.semconv.messaging;
7+
8+
import io.opentelemetry.api.logs.Severity;
9+
import io.opentelemetry.instrumentation.api.incubator.instrumenter.ExceptionEventExtractor;
10+
11+
/**
12+
* {@link ExceptionEventExtractor} constants for messaging instrumentations.
13+
*
14+
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
15+
* at any time.
16+
*/
17+
public final class MessagingExceptionEventExtractors {
18+
19+
/** Exception event extractor for messaging client operation spans. */
20+
public static <REQUEST> ExceptionEventExtractor<REQUEST> client() {
21+
return ExceptionEventExtractor.create("messaging.client.operation.exception", Severity.WARN);
22+
}
23+
24+
/** Exception event extractor for messaging process spans. */
25+
public static <REQUEST> ExceptionEventExtractor<REQUEST> process() {
26+
return ExceptionEventExtractor.create("messaging.process.exception", Severity.ERROR);
27+
}
28+
29+
private MessagingExceptionEventExtractors() {}
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.api.incubator.semconv.rpc;
7+
8+
import io.opentelemetry.api.logs.Severity;
9+
import io.opentelemetry.instrumentation.api.incubator.instrumenter.ExceptionEventExtractor;
10+
11+
/**
12+
* {@link ExceptionEventExtractor} constants for RPC client and server instrumentations.
13+
*
14+
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
15+
* at any time.
16+
*/
17+
public final class RpcExceptionEventExtractors {
18+
19+
/** Exception event extractor for RPC client spans. */
20+
public static <REQUEST> ExceptionEventExtractor<REQUEST> client() {
21+
return ExceptionEventExtractor.create("rpc.client.call.exception", Severity.WARN);
22+
}
23+
24+
/** Exception event extractor for RPC server spans. */
25+
public static <REQUEST> ExceptionEventExtractor<REQUEST> server() {
26+
return ExceptionEventExtractor.create("rpc.server.call.exception", Severity.ERROR);
27+
}
28+
29+
private RpcExceptionEventExtractors() {}
30+
}

0 commit comments

Comments
 (0)