Skip to content

Commit 92b4883

Browse files
committed
Add unit and integration tests for FaaS exception event extractor
1 parent 9428042 commit 92b4883

6 files changed

Lines changed: 156 additions & 30 deletions

File tree

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.api.incubator.semconv.faas.internal;
7+
8+
import static io.opentelemetry.instrumentation.api.internal.SemconvExceptionSignal.emitExceptionAsLogs;
9+
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
10+
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
11+
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies;
12+
import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_MESSAGE;
13+
import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_STACKTRACE;
14+
import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_TYPE;
15+
16+
import io.opentelemetry.api.logs.Severity;
17+
import io.opentelemetry.context.Context;
18+
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
19+
import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder;
20+
import io.opentelemetry.sdk.logs.data.LogRecordData;
21+
import io.opentelemetry.sdk.testing.junit5.OpenTelemetryExtension;
22+
import java.util.List;
23+
import org.assertj.core.api.AbstractAssert;
24+
import org.junit.jupiter.api.Test;
25+
import org.junit.jupiter.api.extension.RegisterExtension;
26+
27+
class FaasExceptionEventExtractorsTest {
28+
29+
@RegisterExtension
30+
static final OpenTelemetryExtension otelTesting = OpenTelemetryExtension.create();
31+
32+
@Test
33+
void faasInvocationExceptionLog() {
34+
InstrumenterBuilder<String, String> builder =
35+
Instrumenter.builder(otelTesting.getOpenTelemetry(), "test", unused -> "span");
36+
FaasExceptionEventExtractors.setFaasInvocationExceptionEventExtractor(builder);
37+
Instrumenter<String, String> instrumenter = builder.buildInstrumenter();
38+
39+
Context context = instrumenter.start(Context.root(), "request");
40+
IllegalStateException error = new IllegalStateException("test");
41+
instrumenter.end(context, "request", "response", error);
42+
43+
List<LogRecordData> logs = otelTesting.getLogRecords();
44+
if (emitExceptionAsLogs()) {
45+
assertThat(logs).hasSize(1);
46+
assertThat(logs.get(0))
47+
.hasSeverity(Severity.ERROR)
48+
.hasEventName("faas.invocation.exception")
49+
.hasAttributesSatisfyingExactly(
50+
equalTo(EXCEPTION_TYPE, "java.lang.IllegalStateException"),
51+
equalTo(EXCEPTION_MESSAGE, "test"),
52+
satisfies(EXCEPTION_STACKTRACE, AbstractAssert::isNotNull));
53+
} else {
54+
assertThat(logs).isEmpty();
55+
}
56+
}
57+
}

instrumentation/aws-lambda/aws-lambda-core-1.0/javaagent/build.gradle.kts

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,28 @@ dependencies {
2020
testImplementation(project(":instrumentation:aws-lambda:aws-lambda-core-1.0:testing"))
2121
}
2222

23-
tasks.test {
24-
// required on jdk17
25-
jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED")
26-
jvmArgs("--add-opens=java.base/java.util=ALL-UNNAMED")
27-
jvmArgs("-XX:+IgnoreUnrecognizedVMOptions")
23+
tasks {
24+
withType<Test>().configureEach {
25+
// required on jdk17
26+
jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED")
27+
jvmArgs("--add-opens=java.base/java.util=ALL-UNNAMED")
28+
jvmArgs("-XX:+IgnoreUnrecognizedVMOptions")
2829

29-
systemProperty("collectMetadata", otelProps.collectMetadata)
30+
systemProperty("collectMetadata", otelProps.collectMetadata)
31+
}
32+
33+
val testExceptionSignalLogs by registering(Test::class) {
34+
testClassesDirs = sourceSets.test.get().output.classesDirs
35+
classpath = sourceSets.test.get().runtimeClasspath
36+
37+
filter {
38+
includeTestsMatching("AwsLambdaTest")
39+
}
40+
jvmArgs("-Dotel.semconv.exception.signal.preview=logs")
41+
systemProperty("metadataConfig", "otel.semconv.exception.signal.preview=logs")
42+
}
43+
44+
check {
45+
dependsOn(testExceptionSignalLogs)
46+
}
3047
}

instrumentation/aws-lambda/aws-lambda-core-1.0/library/build.gradle.kts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,19 @@ tasks.withType<Test>().configureEach {
3434
jvmArgs("--add-opens=java.base/java.util=ALL-UNNAMED")
3535
jvmArgs("-XX:+IgnoreUnrecognizedVMOptions")
3636
}
37+
38+
tasks {
39+
val testExceptionSignalLogs by registering(Test::class) {
40+
testClassesDirs = sourceSets.test.get().output.classesDirs
41+
classpath = sourceSets.test.get().runtimeClasspath
42+
43+
filter {
44+
includeTestsMatching("AwsLambdaTest")
45+
}
46+
jvmArgs("-Dotel.semconv.exception.signal.preview=logs")
47+
}
48+
49+
check {
50+
dependsOn(testExceptionSignalLogs)
51+
}
52+
}

instrumentation/aws-lambda/aws-lambda-core-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/internal/AwsLambdaFunctionInstrumenterFactory.java

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import io.opentelemetry.api.OpenTelemetry;
1111
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
12+
import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder;
1213
import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor;
1314
import io.opentelemetry.instrumentation.awslambdacore.v1_0.AwsLambdaRequest;
1415

@@ -19,15 +20,16 @@
1920
public final class AwsLambdaFunctionInstrumenterFactory {
2021

2122
public static AwsLambdaFunctionInstrumenter createInstrumenter(OpenTelemetry openTelemetry) {
23+
InstrumenterBuilder<AwsLambdaRequest, Object> builder =
24+
Instrumenter.<AwsLambdaRequest, Object>builder(
25+
openTelemetry,
26+
"io.opentelemetry.aws-lambda-core-1.0",
27+
AwsLambdaFunctionInstrumenterFactory::spanName)
28+
.addAttributesExtractor(new AwsLambdaFunctionAttributesExtractor());
29+
setFaasInvocationExceptionEventExtractor(builder);
30+
2231
return new AwsLambdaFunctionInstrumenter(
23-
openTelemetry,
24-
setFaasInvocationExceptionEventExtractor(
25-
Instrumenter.builder(
26-
openTelemetry,
27-
"io.opentelemetry.aws-lambda-core-1.0",
28-
AwsLambdaFunctionInstrumenterFactory::spanName)
29-
.addAttributesExtractor(new AwsLambdaFunctionAttributesExtractor()))
30-
.buildInstrumenter(SpanKindExtractor.alwaysServer()));
32+
openTelemetry, builder.buildInstrumenter(SpanKindExtractor.alwaysServer()));
3133
}
3234

3335
private static String spanName(AwsLambdaRequest input) {

instrumentation/aws-lambda/aws-lambda-core-1.0/testing/src/main/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/AbstractAwsLambdaTest.java

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,28 @@
55

66
package io.opentelemetry.instrumentation.awslambdacore.v1_0;
77

8+
import static io.opentelemetry.instrumentation.api.internal.SemconvExceptionSignal.emitExceptionAsLogs;
9+
import static io.opentelemetry.instrumentation.api.internal.SemconvExceptionSignal.emitExceptionAsSpanEvents;
810
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
911
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
12+
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies;
13+
import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_MESSAGE;
14+
import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_STACKTRACE;
15+
import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_TYPE;
1016
import static io.opentelemetry.semconv.incubating.FaasIncubatingAttributes.FAAS_INVOCATION_ID;
17+
import static java.util.stream.Collectors.toList;
1118
import static org.assertj.core.api.Assertions.catchThrowable;
1219
import static org.mockito.Mockito.when;
1320

1421
import com.amazonaws.services.lambda.runtime.Context;
1522
import com.amazonaws.services.lambda.runtime.RequestHandler;
23+
import io.opentelemetry.api.logs.Severity;
1624
import io.opentelemetry.api.trace.SpanKind;
1725
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
26+
import io.opentelemetry.sdk.logs.data.LogRecordData;
1827
import io.opentelemetry.sdk.trace.data.StatusData;
28+
import java.util.List;
29+
import org.awaitility.Awaitility;
1930
import org.junit.jupiter.api.AfterEach;
2031
import org.junit.jupiter.api.BeforeEach;
2132
import org.junit.jupiter.api.Test;
@@ -80,13 +91,36 @@ void handlerTracedWithException() {
8091
.waitAndAssertTraces(
8192
trace ->
8293
trace.hasSpansSatisfyingExactly(
83-
span ->
84-
span.hasName("my_function")
85-
.hasKind(SpanKind.SERVER)
86-
.hasStatus(StatusData.error())
87-
.hasException(thrown)
88-
.hasAttributesSatisfyingExactly(
89-
equalTo(FAAS_INVOCATION_ID, "1-22-333"))));
94+
span -> {
95+
span.hasName("my_function")
96+
.hasKind(SpanKind.SERVER)
97+
.hasStatus(StatusData.error())
98+
.hasAttributesSatisfyingExactly(equalTo(FAAS_INVOCATION_ID, "1-22-333"));
99+
span.hasException(emitExceptionAsSpanEvents() ? thrown : null);
100+
}));
101+
102+
if (emitExceptionAsLogs()) {
103+
assertInvocationExceptionLog();
104+
}
105+
}
106+
107+
private void assertInvocationExceptionLog() {
108+
Awaitility.await()
109+
.untilAsserted(
110+
() -> {
111+
List<LogRecordData> logs =
112+
testing().logRecords().stream()
113+
.filter(log -> "faas.invocation.exception".equals(log.getEventName()))
114+
.collect(toList());
115+
assertThat(logs).hasSize(1);
116+
assertThat(logs.get(0))
117+
.hasSeverity(Severity.ERROR)
118+
.hasEventName("faas.invocation.exception")
119+
.hasAttributesSatisfyingExactly(
120+
satisfies(EXCEPTION_TYPE, val -> val.isNotNull()),
121+
satisfies(EXCEPTION_MESSAGE, val -> val.isNotNull()),
122+
satisfies(EXCEPTION_STACKTRACE, val -> val.isNotNull()));
123+
});
90124
}
91125

92126
/**

instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/AwsLambdaEventsInstrumenterFactory.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
1111
import io.opentelemetry.api.OpenTelemetry;
1212
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
13+
import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder;
1314
import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor;
1415
import io.opentelemetry.instrumentation.awslambdacore.v1_0.AwsLambdaRequest;
1516
import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.AwsLambdaFunctionAttributesExtractor;
@@ -24,16 +25,15 @@ public final class AwsLambdaEventsInstrumenterFactory {
2425

2526
public static AwsLambdaFunctionInstrumenter createInstrumenter(
2627
OpenTelemetry openTelemetry, String instrumentationName, Set<String> knownMethods) {
28+
InstrumenterBuilder<AwsLambdaRequest, Object> builder =
29+
Instrumenter.<AwsLambdaRequest, Object>builder(
30+
openTelemetry, instrumentationName, AwsLambdaEventsInstrumenterFactory::spanName)
31+
.addAttributesExtractor(new AwsLambdaFunctionAttributesExtractor())
32+
.addAttributesExtractor(new ApiGatewayProxyAttributesExtractor(knownMethods));
33+
setFaasInvocationExceptionEventExtractor(builder);
34+
2735
return new AwsLambdaFunctionInstrumenter(
28-
openTelemetry,
29-
setFaasInvocationExceptionEventExtractor(
30-
Instrumenter.builder(
31-
openTelemetry,
32-
instrumentationName,
33-
AwsLambdaEventsInstrumenterFactory::spanName)
34-
.addAttributesExtractor(new AwsLambdaFunctionAttributesExtractor())
35-
.addAttributesExtractor(new ApiGatewayProxyAttributesExtractor(knownMethods)))
36-
.buildInstrumenter(SpanKindExtractor.alwaysServer()));
36+
openTelemetry, builder.buildInstrumenter(SpanKindExtractor.alwaysServer()));
3737
}
3838

3939
private static String spanName(AwsLambdaRequest input) {

0 commit comments

Comments
 (0)