diff --git a/conventions/src/main/kotlin/io.opentelemetry.instrumentation.javaagent-testing.gradle.kts b/conventions/src/main/kotlin/io.opentelemetry.instrumentation.javaagent-testing.gradle.kts index 07165f789707..b00652f7d59c 100644 --- a/conventions/src/main/kotlin/io.opentelemetry.instrumentation.javaagent-testing.gradle.kts +++ b/conventions/src/main/kotlin/io.opentelemetry.instrumentation.javaagent-testing.gradle.kts @@ -93,7 +93,8 @@ class JavaagentTestArgumentsProvider( "-Dio.opentelemetry.javaagent.slf4j.simpleLogger.log.io.grpc.internal.ServerImplBuilder=INFO", "-Dio.opentelemetry.javaagent.slf4j.simpleLogger.log.io.grpc.internal.ManagedChannelImplBuilder=INFO", "-Dio.opentelemetry.javaagent.slf4j.simpleLogger.log.io.perfmark.PerfMark=INFO", - "-Dio.opentelemetry.javaagent.slf4j.simpleLogger.log.io.grpc.Context=INFO" + "-Dio.opentelemetry.javaagent.slf4j.simpleLogger.log.io.grpc.Context=INFO", + "-Dotel.java.experimental.span-attributes.copy-from-baggage.include=test-baggage-key-1,test-baggage-key-2" ) } @@ -108,12 +109,22 @@ afterEvaluate { // this dependency. dependsOn(agentForTesting.buildDependencies) - jvmArgumentProviders.add(JavaagentTestArgumentsProvider(agentShadowJar, shadowJar.archiveFile.get().asFile)) + jvmArgumentProviders.add( + JavaagentTestArgumentsProvider( + agentShadowJar, + shadowJar.archiveFile.get().asFile + ) + ) // We do fine-grained filtering of the classpath of this codebase's sources since Gradle's // configurations will include transitive dependencies as well, which tests do often need. classpath = classpath.filter { - if (file(layout.buildDirectory.dir("resources/main")).equals(it) || file(layout.buildDirectory.dir("classes/java/main")).equals(it)) { + if (file(layout.buildDirectory.dir("resources/main")).equals(it) || file( + layout.buildDirectory.dir( + "classes/java/main" + ) + ).equals(it) + ) { // The sources are packaged into the testing jar, so we need to exclude them from the test // classpath, which automatically inherits them, to ensure our shaded versions are used. return@filter false diff --git a/instrumentation/spring/spring-webmvc/spring-webmvc-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/JavaxHttpServletRequestGetter.java b/instrumentation/spring/spring-webmvc/spring-webmvc-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/JavaxHttpServletRequestGetter.java index 3fba07f6341e..955c074a1b58 100644 --- a/instrumentation/spring/spring-webmvc/spring-webmvc-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/JavaxHttpServletRequestGetter.java +++ b/instrumentation/spring/spring-webmvc/spring-webmvc-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/JavaxHttpServletRequestGetter.java @@ -5,11 +5,15 @@ package io.opentelemetry.instrumentation.spring.webmvc.v5_3; -import io.opentelemetry.context.propagation.TextMapGetter; +import static java.util.Collections.emptyIterator; + +import io.opentelemetry.context.propagation.internal.ExtendedTextMapGetter; import java.util.Collections; +import java.util.Enumeration; +import java.util.Iterator; import javax.servlet.http.HttpServletRequest; -enum JavaxHttpServletRequestGetter implements TextMapGetter { +enum JavaxHttpServletRequestGetter implements ExtendedTextMapGetter { INSTANCE; @Override @@ -21,4 +25,24 @@ public Iterable keys(HttpServletRequest carrier) { public String get(HttpServletRequest carrier, String key) { return carrier.getHeader(key); } + + @Override + public Iterator getAll(HttpServletRequest carrier, String key) { + Enumeration values = carrier.getHeaders(key); + if (values == null) { + return emptyIterator(); + } + + return new Iterator() { + @Override + public boolean hasNext() { + return values.hasMoreElements(); + } + + @Override + public String next() { + return values.nextElement(); + } + }; + } } diff --git a/instrumentation/spring/spring-webmvc/spring-webmvc-5.3/library/src/test/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/WebMvcHttpServerTest.java b/instrumentation/spring/spring-webmvc/spring-webmvc-5.3/library/src/test/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/WebMvcHttpServerTest.java index d0a69337c7de..f13b1f1a98f4 100644 --- a/instrumentation/spring/spring-webmvc/spring-webmvc-5.3/library/src/test/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/WebMvcHttpServerTest.java +++ b/instrumentation/spring/spring-webmvc/spring-webmvc-5.3/library/src/test/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/WebMvcHttpServerTest.java @@ -53,6 +53,8 @@ protected void configure(HttpServerTestOptions options) { } return expectedHttpRoute(endpoint, method); }); + + options.setTestExtractMultiBaggage(true); } @Test diff --git a/testing-common/build.gradle.kts b/testing-common/build.gradle.kts index 8d0088f73415..d7d6af74389a 100644 --- a/testing-common/build.gradle.kts +++ b/testing-common/build.gradle.kts @@ -9,10 +9,16 @@ group = "io.opentelemetry.javaagent" sourceSets { main { val armeriaShadedDeps = project(":testing:armeria-shaded-for-testing") - output.dir(armeriaShadedDeps.file("build/extracted/shadow"), "builtBy" to ":testing:armeria-shaded-for-testing:extractShadowJar") + output.dir( + armeriaShadedDeps.file("build/extracted/shadow"), + "builtBy" to ":testing:armeria-shaded-for-testing:extractShadowJar" + ) val protoShadedDeps = project(":testing:proto-shaded-for-testing") - output.dir(protoShadedDeps.file("build/extracted/shadow"), "builtBy" to ":testing:proto-shaded-for-testing:extractShadowJar") + output.dir( + protoShadedDeps.file("build/extracted/shadow"), + "builtBy" to ":testing:proto-shaded-for-testing:extractShadowJar" + ) } } @@ -62,6 +68,7 @@ dependencies { implementation("org.slf4j:jcl-over-slf4j") implementation("org.slf4j:jul-to-slf4j") implementation("io.opentelemetry:opentelemetry-exporter-logging") + implementation("io.opentelemetry.contrib:opentelemetry-baggage-processor") api(project(":instrumentation-api-incubator")) annotationProcessor("com.google.auto.service:auto-service") diff --git a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/LibraryTestRunner.java b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/LibraryTestRunner.java index 2437e2537d40..f7cb08c5484b 100644 --- a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/LibraryTestRunner.java +++ b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/LibraryTestRunner.java @@ -5,8 +5,11 @@ package io.opentelemetry.instrumentation.testing; +import static java.util.Arrays.asList; + import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator; import io.opentelemetry.api.logs.LoggerProvider; import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.api.trace.TracerBuilder; @@ -14,6 +17,8 @@ import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; import io.opentelemetry.context.Context; import io.opentelemetry.context.propagation.ContextPropagators; +import io.opentelemetry.context.propagation.TextMapPropagator; +import io.opentelemetry.contrib.baggage.processor.BaggageSpanProcessor; import io.opentelemetry.exporter.logging.LoggingSpanExporter; import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.common.CompletableResultCode; @@ -73,13 +78,22 @@ public final class LibraryTestRunner extends InstrumentationTestRunner { .addSpanProcessor(new FlushTrackingSpanProcessor()) .addSpanProcessor(SimpleSpanProcessor.create(LoggingSpanExporter.create())) .addSpanProcessor(SimpleSpanProcessor.create(testSpanExporter)) + .addSpanProcessor( + new BaggageSpanProcessor( + baggageKey -> + asList("test-baggage-key-1", "test-baggage-key-2") + .contains(baggageKey))) .build()) .setMeterProvider(SdkMeterProvider.builder().registerMetricReader(metricReader).build()) .setLoggerProvider( SdkLoggerProvider.builder() .addLogRecordProcessor(SimpleLogRecordProcessor.create(testLogRecordExporter)) .build()) - .setPropagators(ContextPropagators.create(W3CTraceContextPropagator.getInstance())) + .setPropagators( + ContextPropagators.create( + TextMapPropagator.composite( + W3CTraceContextPropagator.getInstance(), + W3CBaggagePropagator.getInstance()))) .buildAndRegisterGlobal(); openTelemetry = wrap(openTelemetrySdk); } diff --git a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/InstrumentationExtension.java b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/InstrumentationExtension.java index d66c9c86552d..d1d8735be37a 100644 --- a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/InstrumentationExtension.java +++ b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/InstrumentationExtension.java @@ -6,7 +6,6 @@ package io.opentelemetry.instrumentation.testing.junit; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; -import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; import io.opentelemetry.api.OpenTelemetry; diff --git a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/AbstractHttpServerTest.java b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/AbstractHttpServerTest.java index d8c6ce62aea5..93940b0551bb 100644 --- a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/AbstractHttpServerTest.java +++ b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/AbstractHttpServerTest.java @@ -543,6 +543,57 @@ void requestWithNonStandardHttpMethod() throws InterruptedException { } } + @Test + void extractSingleBaggage() { + String method = "GET"; + AggregatedHttpRequest request = + AggregatedHttpRequest.of( + request(SUCCESS, method).headers().toBuilder() + // adding baggage header in w3c baggage format + .set("baggage", "test-baggage-key-1=test-baggage-value-1") + .build()); + AggregatedHttpResponse response = client.execute(request).aggregate().join(); + + assertThat(response.status().code()).isEqualTo(SUCCESS.getStatus()); + assertThat(response.contentUtf8()).isEqualTo(SUCCESS.getBody()); + + testing.waitAndAssertTraces( + trace -> + trace.anySatisfy( + span -> + assertServerSpan(assertThat(span), method, SUCCESS, SUCCESS.status) + .hasAttribute( + AttributeKey.stringKey("test-baggage-key-1"), "test-baggage-value-1"))); + } + + @Test + void extractMultiBaggage() { + assumeTrue(options.testExtractMultiBaggage); + + String method = "GET"; + AggregatedHttpRequest request = + AggregatedHttpRequest.of( + request(SUCCESS, method).headers().toBuilder() + // adding baggage header in w3c baggage format + .add("baggage", "test-baggage-key-1=test-baggage-value-1") + .add("baggage", "test-baggage-key-2=test-baggage-value-2") + .build()); + AggregatedHttpResponse response = client.execute(request).aggregate().join(); + + assertThat(response.status().code()).isEqualTo(SUCCESS.getStatus()); + assertThat(response.contentUtf8()).isEqualTo(SUCCESS.getBody()); + + testing.waitAndAssertTraces( + trace -> + trace.anySatisfy( + span -> + assertServerSpan(assertThat(span), method, SUCCESS, SUCCESS.status) + .hasAttribute( + AttributeKey.stringKey("test-baggage-key-1"), "test-baggage-value-1") + .hasAttribute( + AttributeKey.stringKey("test-baggage-key-2"), "test-baggage-value-2"))); + } + private static Bootstrap buildBootstrap(EventLoopGroup eventLoopGroup) { Bootstrap bootstrap = new Bootstrap(); bootstrap diff --git a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/HttpServerTestOptions.java b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/HttpServerTestOptions.java index 13e79b8f31b7..8023050f57d3 100644 --- a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/HttpServerTestOptions.java +++ b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/HttpServerTestOptions.java @@ -64,6 +64,7 @@ public final class HttpServerTestOptions { boolean testNonStandardHttpMethod = true; boolean verifyServerSpanEndTime = true; boolean useHttp2 = false; + boolean testExtractMultiBaggage = false; HttpServerTestOptions() {} @@ -230,6 +231,12 @@ public HttpServerTestOptions useHttp2() { return setUseHttp2(true); } + @CanIgnoreReturnValue + public HttpServerTestOptions setTestExtractMultiBaggage(boolean testExtractMultiBaggage) { + this.testExtractMultiBaggage = testExtractMultiBaggage; + return this; + } + @FunctionalInterface public interface SpanNameMapper {