Skip to content

Commit 24bf7c1

Browse files
committed
Add exception event names to AWS SDK instrumenters
1 parent 5f2634e commit 24bf7c1

8 files changed

Lines changed: 139 additions & 69 deletions

File tree

instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/awssdk/v1_11/S3ClientTest.java

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

88
import static io.opentelemetry.api.common.AttributeKey.stringKey;
99
import static io.opentelemetry.api.trace.SpanKind.CLIENT;
10+
import static io.opentelemetry.instrumentation.api.internal.SemconvExceptionSignal.emitExceptionAsSpanEvents;
1011
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
1112
import static io.opentelemetry.semconv.ErrorAttributes.ERROR_TYPE;
1213
import static io.opentelemetry.semconv.HttpAttributes.HTTP_REQUEST_METHOD;
@@ -124,7 +125,7 @@ public void beforeRequest(Request<?> request) {
124125
span.hasName("S3.HeadBucket")
125126
.hasKind(CLIENT)
126127
.hasStatus(StatusData.error())
127-
.hasException(caught)
128+
.hasException(emitExceptionAsSpanEvents() ? caught : null)
128129
.hasNoParent()
129130
.hasAttributesSatisfyingExactly(
130131
equalTo(URL_FULL, "https://s3.amazonaws.com"),

instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test_before_1_11_106/java/io/opentelemetry/javaagent/instrumentation/awssdk/v1_11/Aws0ClientTest.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import static io.opentelemetry.api.common.AttributeKey.stringKey;
99
import static io.opentelemetry.api.trace.SpanKind.CLIENT;
1010
import static io.opentelemetry.api.trace.SpanKind.PRODUCER;
11+
import static io.opentelemetry.instrumentation.api.internal.SemconvExceptionSignal.emitExceptionAsSpanEvents;
1112
import static io.opentelemetry.instrumentation.test.utils.PortUtils.UNUSABLE_PORT;
1213
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
1314
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies;
@@ -257,7 +258,7 @@ void testSendS3RequestToClosedPort() {
257258
span.hasName("S3.GetObject")
258259
.hasKind(CLIENT)
259260
.hasStatus(StatusData.error())
260-
.hasException(caught)
261+
.hasException(emitExceptionAsSpanEvents() ? caught : null)
261262
.hasNoParent()
262263
.hasAttributesSatisfyingExactly(
263264
equalTo(URL_FULL, "http://localhost:" + UNUSABLE_PORT),
@@ -295,7 +296,7 @@ public void beforeRequest(Request<?> request) {
295296
span.hasName("S3.GetObject")
296297
.hasKind(CLIENT)
297298
.hasStatus(StatusData.error())
298-
.hasException(caught)
299+
.hasException(emitExceptionAsSpanEvents() ? caught : null)
299300
.hasNoParent()
300301
.hasAttributesSatisfyingExactly(
301302
equalTo(URL_FULL, "https://s3.amazonaws.com"),
@@ -333,7 +334,7 @@ void testTimeoutAndRetryErrorsAreNotCaptured() {
333334
span.hasName("S3.GetObject")
334335
.hasKind(CLIENT)
335336
.hasStatus(StatusData.error())
336-
.hasException(caught)
337+
.hasException(emitExceptionAsSpanEvents() ? caught : null)
337338
.hasNoParent()
338339
.hasAttributesSatisfyingExactly(
339340
equalTo(URL_FULL, server.httpUri().toString()),

instrumentation/aws-sdk/aws-sdk-1.11/library/build.gradle.kts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,13 @@ tasks {
5858
jvmArgs("-Dotel.semconv-stability.opt-in=database")
5959
}
6060

61+
val testExceptionSignalLogs by registering(Test::class) {
62+
testClassesDirs = sourceSets.test.get().output.classesDirs
63+
classpath = sourceSets.test.get().runtimeClasspath
64+
jvmArgs("-Dotel.semconv.exception.signal.opt-in=logs")
65+
}
66+
6167
check {
62-
dependsOn(testing.suites, testStableSemconv)
68+
dependsOn(testing.suites, testStableSemconv, testExceptionSignalLogs)
6369
}
6470
}

instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AwsSdkInstrumenterFactory.java

Lines changed: 23 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,27 @@
55

66
package io.opentelemetry.instrumentation.awssdk.v1_11;
77

8-
import static java.util.Collections.emptyList;
9-
import static java.util.Collections.singletonList;
10-
118
import com.amazonaws.Request;
129
import com.amazonaws.Response;
1310
import io.opentelemetry.api.OpenTelemetry;
1411
import io.opentelemetry.api.common.AttributesBuilder;
1512
import io.opentelemetry.api.trace.Span;
1613
import io.opentelemetry.context.Context;
1714
import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientMetrics;
15+
import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbExceptionEventExtractors;
1816
import io.opentelemetry.instrumentation.api.incubator.semconv.messaging.MessageOperation;
1917
import io.opentelemetry.instrumentation.api.incubator.semconv.messaging.MessagingAttributesExtractor;
2018
import io.opentelemetry.instrumentation.api.incubator.semconv.messaging.MessagingAttributesGetter;
19+
import io.opentelemetry.instrumentation.api.incubator.semconv.messaging.MessagingExceptionEventExtractors;
2120
import io.opentelemetry.instrumentation.api.incubator.semconv.messaging.MessagingSpanNameExtractor;
2221
import io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcClientAttributesExtractor;
22+
import io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcExceptionEventExtractors;
2323
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
2424
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
2525
import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder;
2626
import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor;
2727
import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor;
28+
import io.opentelemetry.instrumentation.api.internal.Experimental;
2829
import io.opentelemetry.instrumentation.api.semconv.http.HttpClientAttributesExtractor;
2930
import java.util.ArrayList;
3031
import java.util.Arrays;
@@ -88,7 +89,8 @@ Instrumenter<Request<?>, Response<?>> requestInstrumenter() {
8889
spanName,
8990
SpanKindExtractor.alwaysClient(),
9091
attributesExtractors(),
91-
emptyList(),
92+
builder ->
93+
Experimental.setExceptionEventExtractor(builder, RpcExceptionEventExtractors.client()),
9294
true);
9395
}
9496

@@ -116,7 +118,11 @@ Instrumenter<SqsReceiveRequest, Response<?>> consumerReceiveInstrumenter() {
116118
MessagingSpanNameExtractor.create(getter, operation),
117119
SpanKindExtractor.alwaysConsumer(),
118120
toSqsRequestExtractors(attributesExtractors()),
119-
singletonList(messagingAttributeExtractor),
121+
builder -> {
122+
builder.addAttributesExtractor(messagingAttributeExtractor);
123+
Experimental.setExceptionEventExtractor(
124+
builder, MessagingExceptionEventExtractors.client());
125+
},
120126
messagingReceiveInstrumentationEnabled);
121127
}
122128

@@ -142,6 +148,7 @@ Instrumenter<SqsProcessRequest, Response<?>> consumerProcessInstrumenter() {
142148
spanLinks.addLink(Span.fromContext(extracted).getSpanContext());
143149
});
144150
}
151+
Experimental.setExceptionEventExtractor(builder, MessagingExceptionEventExtractors.process());
145152
return builder.buildInstrumenter(SpanKindExtractor.alwaysConsumer());
146153
}
147154

@@ -184,7 +191,11 @@ Instrumenter<Request<?>, Response<?>> producerInstrumenter() {
184191
MessagingSpanNameExtractor.create(getter, operation),
185192
SpanKindExtractor.alwaysProducer(),
186193
attributesExtractors(),
187-
singletonList(messagingAttributeExtractor),
194+
builder -> {
195+
builder.addAttributesExtractor(messagingAttributeExtractor);
196+
Experimental.setExceptionEventExtractor(
197+
builder, MessagingExceptionEventExtractors.client());
198+
},
188199
true);
189200
}
190201

@@ -194,29 +205,15 @@ Instrumenter<Request<?>, Response<?>> dynamoDbInstrumenter() {
194205
spanName,
195206
SpanKindExtractor.alwaysClient(),
196207
attributesExtractors(),
197-
builder ->
198-
builder
199-
.addAttributesExtractor(new DynamoDbAttributesExtractor())
200-
.addOperationMetrics(DbClientMetrics.get()),
208+
builder -> {
209+
builder
210+
.addAttributesExtractor(new DynamoDbAttributesExtractor())
211+
.addOperationMetrics(DbClientMetrics.get());
212+
Experimental.setExceptionEventExtractor(builder, DbExceptionEventExtractors.client());
213+
},
201214
true);
202215
}
203216

204-
private static <REQUEST, RESPONSE> Instrumenter<REQUEST, RESPONSE> createInstrumenter(
205-
OpenTelemetry openTelemetry,
206-
SpanNameExtractor<REQUEST> spanNameExtractor,
207-
SpanKindExtractor<REQUEST> spanKindExtractor,
208-
List<? extends AttributesExtractor<? super REQUEST, ? super RESPONSE>> attributeExtractors,
209-
List<AttributesExtractor<REQUEST, RESPONSE>> additionalAttributeExtractors,
210-
boolean enabled) {
211-
return createInstrumenter(
212-
openTelemetry,
213-
spanNameExtractor,
214-
spanKindExtractor,
215-
attributeExtractors,
216-
builder -> builder.addAttributesExtractors(additionalAttributeExtractors),
217-
enabled);
218-
}
219-
220217
private static <REQUEST, RESPONSE> Instrumenter<REQUEST, RESPONSE> createInstrumenter(
221218
OpenTelemetry openTelemetry,
222219
SpanNameExtractor<REQUEST> spanNameExtractor,

instrumentation/aws-sdk/aws-sdk-1.11/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AbstractS3ClientTest.java

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,15 @@
77

88
import static io.opentelemetry.api.common.AttributeKey.stringKey;
99
import static io.opentelemetry.api.trace.SpanKind.CLIENT;
10+
import static io.opentelemetry.instrumentation.api.internal.SemconvExceptionSignal.emitExceptionAsLogs;
11+
import static io.opentelemetry.instrumentation.api.internal.SemconvExceptionSignal.emitExceptionAsSpanEvents;
1012
import static io.opentelemetry.instrumentation.test.utils.PortUtils.UNUSABLE_PORT;
1113
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
14+
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies;
1215
import static io.opentelemetry.semconv.ErrorAttributes.ERROR_TYPE;
16+
import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_MESSAGE;
17+
import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_STACKTRACE;
18+
import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_TYPE;
1319
import static io.opentelemetry.semconv.HttpAttributes.HTTP_REQUEST_METHOD;
1420
import static io.opentelemetry.semconv.ServerAttributes.SERVER_ADDRESS;
1521
import static io.opentelemetry.semconv.ServerAttributes.SERVER_PORT;
@@ -29,7 +35,9 @@
2935
import com.amazonaws.retry.PredefinedRetryPolicies;
3036
import com.amazonaws.services.s3.AmazonS3;
3137
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
38+
import io.opentelemetry.api.logs.Severity;
3239
import io.opentelemetry.api.trace.Span;
40+
import io.opentelemetry.api.trace.SpanContext;
3341
import io.opentelemetry.sdk.testing.assertj.AttributeAssertion;
3442
import io.opentelemetry.sdk.trace.data.StatusData;
3543
import io.opentelemetry.testing.internal.armeria.common.HttpResponse;
@@ -120,7 +128,7 @@ public void testSendRequestToClosedPort() {
120128
span.hasName("S3.GetObject")
121129
.hasKind(CLIENT)
122130
.hasStatus(StatusData.error())
123-
.hasException(caught)
131+
.hasException(emitExceptionAsSpanEvents() ? caught : null)
124132
.hasNoParent()
125133
.hasAttributesSatisfyingExactly(
126134
equalTo(URL_FULL, "http://127.0.0.1:" + UNUSABLE_PORT),
@@ -133,6 +141,20 @@ public void testSendRequestToClosedPort() {
133141
equalTo(stringKey("aws.agent"), "java-aws-sdk"),
134142
equalTo(AWS_S3_BUCKET, "someBucket"),
135143
equalTo(ERROR_TYPE, SdkClientException.class.getName()))));
144+
145+
if (emitExceptionAsLogs()) {
146+
SpanContext spanCtx = testing().spans().get(0).getSpanContext();
147+
testing()
148+
.waitAndAssertLogRecords(
149+
log ->
150+
log.hasSpanContext(spanCtx)
151+
.hasSeverity(Severity.WARN)
152+
.hasEventName("rpc.client.call.exception")
153+
.hasAttributesSatisfyingExactly(
154+
equalTo(EXCEPTION_TYPE, caught.getClass().getName()),
155+
equalTo(EXCEPTION_MESSAGE, caught.getMessage()),
156+
satisfies(EXCEPTION_STACKTRACE, val -> val.isNotNull())));
157+
}
136158
}
137159

138160
@Test
@@ -165,8 +187,10 @@ void testTimeoutAndRetryErrorsNotCaptured() {
165187
.hasStatus(StatusData.error())
166188
.hasNoParent()
167189
.hasException(
168-
new SdkClientException(
169-
"Unable to execute HTTP request: Request did not complete before the request timeout configuration."))
190+
emitExceptionAsSpanEvents()
191+
? new SdkClientException(
192+
"Unable to execute HTTP request: Request did not complete before the request timeout configuration.")
193+
: null)
170194
.hasAttributesSatisfyingExactly(
171195
equalTo(URL_FULL, server.httpUri().toString()),
172196
equalTo(HTTP_REQUEST_METHOD, "GET"),
@@ -178,5 +202,21 @@ void testTimeoutAndRetryErrorsNotCaptured() {
178202
equalTo(stringKey("aws.agent"), "java-aws-sdk"),
179203
equalTo(AWS_S3_BUCKET, "someBucket"),
180204
equalTo(ERROR_TYPE, SdkClientException.class.getName()))));
205+
206+
if (emitExceptionAsLogs()) {
207+
SpanContext spanCtx = testing().spans().get(0).getSpanContext();
208+
testing()
209+
.waitAndAssertLogRecords(
210+
log ->
211+
log.hasSpanContext(spanCtx)
212+
.hasSeverity(Severity.WARN)
213+
.hasEventName("rpc.client.call.exception")
214+
.hasAttributesSatisfyingExactly(
215+
equalTo(EXCEPTION_TYPE, SdkClientException.class.getName()),
216+
equalTo(
217+
EXCEPTION_MESSAGE,
218+
"Unable to execute HTTP request: Request did not complete before the request timeout configuration."),
219+
satisfies(EXCEPTION_STACKTRACE, val -> val.isNotNull())));
220+
}
181221
}
182222
}

instrumentation/aws-sdk/aws-sdk-2.2/library/build.gradle.kts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,16 @@ tasks {
8080
val testStableSemconv by registering(Test::class) {
8181
testClassesDirs = sourceSets.test.get().output.classesDirs
8282
classpath = sourceSets.test.get().runtimeClasspath
83-
8483
jvmArgs("-Dotel.semconv-stability.opt-in=database")
8584
}
8685

86+
val testExceptionSignalLogs by registering(Test::class) {
87+
testClassesDirs = sourceSets.test.get().output.classesDirs
88+
classpath = sourceSets.test.get().runtimeClasspath
89+
jvmArgs("-Dotel.semconv.exception.signal.opt-in=logs")
90+
}
91+
8792
check {
88-
dependsOn(testing.suites, testStableSemconv)
93+
dependsOn(testing.suites, testStableSemconv, testExceptionSignalLogs)
8994
}
9095
}

0 commit comments

Comments
 (0)