From b25360392b06b1034c81dc940fcafd999abc4144 Mon Sep 17 00:00:00 2001 From: Shashank Patidar <74622220+shashank11p@users.noreply.github.com> Date: Tue, 26 Mar 2024 16:48:09 +0530 Subject: [PATCH] Upgrade otel to 1.33.0 (#402) * 1st attempt otel upgrade * temp trying * test using javaagent * fix client tests * otel 1.33.0, and fix netty and blocking extension * fix netty 4.1 * fix servlet-3.0 * fix servlet-3.0, rw * fix spark * fix spring-webflux * fix struts and undertow * fix vertx * fix grpc * fix tests * cleanup * spotless apply * fix grpc 1.30.0 * fix micronaut * fix smoke tests * fix smoke tests * fix netty client * fix smoke tests * fix smoke tests * debug * increase timeout for flake test * increase timeout for flaky test, try to fix vertx test * disable flaky test --- .gitmodules | 6 +- build.gradle.kts | 15 +- buildSrc/build.gradle.kts | 2 +- .../gradle/AutoInstrumentationPlugin.java | 14 - .../build.gradle.kts | 5 +- ...eAsyncClientInstrumentationModuleTest.java | 53 +-- .../apache-httpclient-4.0/build.gradle.kts | 5 +- instrumentation/build.gradle.kts | 108 +++++ instrumentation/grpc-1.6/build.gradle.kts | 45 ++- .../grpc/v1_6/GrpcInstrumentationTest.java | 125 ++---- .../resources/ht-config-all-disabled.yaml | 17 - instrumentation/java-streams/build.gradle.kts | 3 +- .../ContextAccessorInstrumentationModule.java | 109 ----- .../InputStreamInstrumentationModuleTest.java | 161 -------- ...OutputStreamInstrumentationModuleTest.java | 95 ----- .../src/test/java/org/ContextAccessor.java | 33 -- ...sion.instrumentation.InstrumentationModule | 1 - .../jaxrs-client-2.0/build.gradle.kts | 8 +- .../micronaut-1.0/build.gradle.kts | 42 +- .../MicronautClientInstrumentationTest.java | 63 +-- .../MicronautInstrumentationTest.java | 103 +++-- .../micronaut-3.0/build.gradle.kts | 5 +- .../MicronautClientInstrumentationTest.java | 55 ++- .../v3/MicronautInstrumentationTest.java | 103 +++-- .../netty/netty-4.0/build.gradle.kts | 6 +- .../HttpClientResponseTracingHandler.java | 4 +- .../HttpServerResponseTracingHandler.java | 4 +- ...tractNetty40ServerInstrumentationTest.java | 171 ++++---- .../netty/netty-4.1/build.gradle.kts | 6 +- .../NettyChannelPipelineInstrumentation.java | 23 +- .../HttpClientResponseTracingHandler.java | 4 +- .../HttpServerBlockingRequestHandler.java | 13 +- .../HttpServerRequestTracingHandler.java | 9 +- .../HttpServerResponseTracingHandler.java | 22 +- ...tractNetty41ServerInstrumentationTest.java | 171 ++++---- ...y41HttpServerCodecInstrumentationTest.java | 4 +- .../okhttp/okhttp-3.0/build.gradle.kts | 3 +- .../OkHttp3BodyInstrumentationModule.java | 2 +- .../servlet/servlet-3.0/build.gradle.kts | 8 +- .../Servlet30InstrumentationTest.java | 146 +++---- ...eamContextAccessInstrumentationModule.java | 93 ----- ...ServletInputStreamInstrumentationTest.java | 97 ----- ...eamContextAccessInstrumentationModule.java | 92 ----- ...ervletOutputStreamInstrumentationTest.java | 93 ----- ...sion.instrumentation.InstrumentationModule | 2 - .../servlet/servlet-rw/build.gradle.kts | 4 +- .../reader/BufferedReaderInstrumentation.java | 137 ++++--- ...derContextAccessInstrumentationModule.java | 91 ----- .../BufferedReaderInstrumentationTest.java | 146 ------- ...terContextAccessInstrumentationModule.java | 92 ----- .../PrintWriterInstrumentationTest.java | 125 ------ .../v3_0/rw/ServletRWInstrumentationTest.java | 326 +++++++++++++++ .../servlet/v3_0/rw/TestServlets.java | 198 ++++++++++ ...ufferedReaderPrintWriterContextAccess.java | 33 -- .../DelegatingBufferedReader.java} | 15 +- .../test/java/rw/DelegatingPrintWriter.java | 27 +- .../src/test/java/rw/WrappingFilter.java | 86 ++++ ...sion.instrumentation.InstrumentationModule | 2 - instrumentation/spark-2.3/build.gradle.kts | 6 +- .../SparkJavaInstrumentationTest.java | 57 +-- .../spring-webflux-5.0/build.gradle.kts | 5 +- .../webflux/SpringWebfluxServerTest.java | 113 +++--- instrumentation/struts-2.3/build.gradle.kts | 9 +- .../struts/StrutsInstrumentationTest.java | 68 ++-- .../undertow/undertow-1.4/build.gradle.kts | 7 +- .../v1_4/UndertowInstrumentationTest.java | 64 +-- .../vertx/vertx-web-3.0/build.gradle.kts | 5 +- .../vertx/ResponseBodyWrappingHandler.java | 31 +- .../vertx/VertxClientInstrumentationTest.java | 40 +- .../vertx/VertxServerInstrumentationTest.java | 86 ++-- otel-extensions/build.gradle.kts | 7 +- .../extensions/HypertracePropertySource.java | 85 ++++ .../HypertraceResourceProvider.java | 2 +- .../processor/AddTagsSpanProcessor.java | 2 +- settings.gradle.kts | 1 + smoke-tests/build.gradle.kts | 4 +- .../agent/smoketest/AppServerTest.groovy | 168 -------- .../agent/smoketest/PlaySmokeTest.groovy | 41 -- .../agent/smoketest/SmokeTest.groovy | 12 +- .../agent/smoketest/TraceInspector.java | 2 +- .../agent/smoketest/AbstractSmokeTest.java | 12 +- ...pringBootDisabledBodyCaptureSmokeTest.java | 10 +- .../agent/smoketest/SpringBootSmokeTest.java | 17 +- smoke-tests/src/test/resources/otel.yaml | 3 +- testing-bootstrap/build.gradle.kts | 13 - testing-common/build.gradle.kts | 75 ++-- .../agent/testing/AbstractHttpClientTest.java | 193 +++++---- .../testing/AbstractInstrumenterTest.java | 73 +--- .../hypertrace/agent/testing/OkHttpUtils.java | 0 .../agent/testing/TestHttpServer.java | 0 .../agent/testing/TestOtlpReceiver.java | 299 ++++++++++++++ testing-common/src/main/proto | 1 + .../agent/testing/FakeTransformer.java | 135 ------- .../agent/testing/InMemoryExporter.java | 374 ------------------ .../TestSdkTracerProviderConfigurer.java | 32 -- tests-extension/build.gradle.kts | 68 ++++ .../javaagent}/mockfilter/MockFilter.java | 2 +- .../mockfilter/MockFilterProvider.java | 9 +- 98 files changed, 2385 insertions(+), 3177 deletions(-) delete mode 100644 instrumentation/grpc-1.6/src/test/resources/ht-config-all-disabled.yaml delete mode 100644 instrumentation/java-streams/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/java/ContextAccessorInstrumentationModule.java delete mode 100644 instrumentation/java-streams/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/java/inputstream/InputStreamInstrumentationModuleTest.java delete mode 100644 instrumentation/java-streams/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/java/outputstream/OutputStreamInstrumentationModuleTest.java delete mode 100644 instrumentation/java-streams/src/test/java/org/ContextAccessor.java delete mode 100644 instrumentation/java-streams/src/test/resources/META-INF/services/io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule delete mode 100644 instrumentation/servlet/servlet-3.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/request/ServletInputStreamContextAccessInstrumentationModule.java delete mode 100644 instrumentation/servlet/servlet-3.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/request/ServletInputStreamInstrumentationTest.java delete mode 100644 instrumentation/servlet/servlet-3.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/response/ServletOutputStreamContextAccessInstrumentationModule.java delete mode 100644 instrumentation/servlet/servlet-3.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/response/ServletOutputStreamInstrumentationTest.java delete mode 100644 instrumentation/servlet/servlet-3.0/src/test/resources/META-INF/services/io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule delete mode 100644 instrumentation/servlet/servlet-rw/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/rw/reader/BufferedReaderContextAccessInstrumentationModule.java delete mode 100644 instrumentation/servlet/servlet-rw/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/rw/reader/BufferedReaderInstrumentationTest.java delete mode 100644 instrumentation/servlet/servlet-rw/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/rw/writer/PrintWriterContextAccessInstrumentationModule.java delete mode 100644 instrumentation/servlet/servlet-rw/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/rw/writer/PrintWriterInstrumentationTest.java create mode 100644 instrumentation/servlet/servlet-rw/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/rw/ServletRWInstrumentationTest.java create mode 100644 instrumentation/servlet/servlet-rw/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/rw/TestServlets.java delete mode 100644 instrumentation/servlet/servlet-rw/src/test/java/org/BufferedReaderPrintWriterContextAccess.java rename instrumentation/servlet/servlet-rw/src/test/java/{org/TestBufferedReader.java => rw/DelegatingBufferedReader.java} (70%) rename testing-common/src/testFixtures/java/org/hypertrace/agent/testing/TestAgentStarter.java => instrumentation/servlet/servlet-rw/src/test/java/rw/DelegatingPrintWriter.java (59%) create mode 100644 instrumentation/servlet/servlet-rw/src/test/java/rw/WrappingFilter.java delete mode 100644 instrumentation/servlet/servlet-rw/src/test/resources/META-INF/services/io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule create mode 100644 otel-extensions/src/main/java/org/hypertrace/agent/otel/extensions/HypertracePropertySource.java delete mode 100644 smoke-tests/src/test/groovy/org/hypertrace/agent/smoketest/PlaySmokeTest.groovy rename testing-common/src/{testFixtures => main}/java/org/hypertrace/agent/testing/AbstractHttpClientTest.java (57%) rename testing-common/src/{testFixtures => main}/java/org/hypertrace/agent/testing/AbstractInstrumenterTest.java (52%) rename testing-common/src/{testFixtures => main}/java/org/hypertrace/agent/testing/OkHttpUtils.java (100%) rename testing-common/src/{testFixtures => main}/java/org/hypertrace/agent/testing/TestHttpServer.java (100%) create mode 100644 testing-common/src/main/java/org/hypertrace/agent/testing/TestOtlpReceiver.java create mode 160000 testing-common/src/main/proto delete mode 100644 testing-common/src/testFixtures/java/org/hypertrace/agent/testing/FakeTransformer.java delete mode 100644 testing-common/src/testFixtures/java/org/hypertrace/agent/testing/InMemoryExporter.java delete mode 100644 testing-common/src/testFixtures/java/org/hypertrace/agent/testing/TestSdkTracerProviderConfigurer.java create mode 100644 tests-extension/build.gradle.kts rename {testing-common/src/testFixtures/java/org/hypertrace/agent/testing => tests-extension/src/main/java/org/hypertrace/javaagent}/mockfilter/MockFilter.java (97%) rename {testing-common/src/testFixtures/java/org/hypertrace/agent/testing => tests-extension/src/main/java/org/hypertrace/javaagent}/mockfilter/MockFilterProvider.java (81%) diff --git a/.gitmodules b/.gitmodules index bd38ff0a5..b69562bd2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,7 @@ -[submodule "javaagent-core/src/main/proto"] +[submodule "otel-extensions/src/main/proto"] path = otel-extensions/src/main/proto url = https://github.com/hypertrace/agent-config.git + +[submodule "testing-common/src/main/proto"] + path = testing-common/src/main/proto + url = https://github.com/open-telemetry/opentelemetry-proto.git diff --git a/build.gradle.kts b/build.gradle.kts index 8b5878178..ddc0ca1f0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -54,14 +54,15 @@ subprojects { extra.set("versions", mapOf( // when updating these values, some values must also be updated in buildSrc as this map // cannot be accessed there - "opentelemetry" to "1.24.0", - "opentelemetry_semconv" to "1.24.0-alpha", - "opentelemetry_proto" to "0.11.0-alpha", - "opentelemetry_java_agent" to "1.24.0-alpha", - "opentelemetry_java_agent_all" to "1.24.0", - "opentelemetry_java_agent-tooling" to "1.24.0-alpha", + "opentelemetry" to "1.33.0", + "opentelemetry_semconv" to "1.21.0-alpha", + "opentelemetry_api_semconv" to "1.33.0-alpha", + "opentelemetry_proto" to "0.20.0-alpha", + "opentelemetry_java_agent" to "1.33.0-alpha", + "opentelemetry_java_agent_all" to "1.33.0", + "opentelemetry_java_agent-tooling" to "1.33.0-alpha", - "opentelemetry_gradle_plugin" to "1.24.0-alpha", + "opentelemetry_gradle_plugin" to "1.33.0-alpha", "byte_buddy" to "1.12.10", "slf4j" to "2.0.7" )) diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 684caa445..c074a0fe6 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -33,7 +33,7 @@ tasks { dependencies { compileOnly(gradleApi()) implementation(localGroovy()) - val otelInstrumentationVersion = "1.24.0-alpha" + val otelInstrumentationVersion = "1.33.0-alpha" implementation("io.opentelemetry.javaagent:opentelemetry-muzzle:$otelInstrumentationVersion") implementation("io.opentelemetry.instrumentation.muzzle-generation:io.opentelemetry.instrumentation.muzzle-generation.gradle.plugin:$otelInstrumentationVersion") implementation("io.opentelemetry.instrumentation.muzzle-check:io.opentelemetry.instrumentation.muzzle-check.gradle.plugin:$otelInstrumentationVersion") diff --git a/buildSrc/src/main/java/io/opentelemetry/instrumentation/gradle/AutoInstrumentationPlugin.java b/buildSrc/src/main/java/io/opentelemetry/instrumentation/gradle/AutoInstrumentationPlugin.java index ec9c8defd..672254962 100644 --- a/buildSrc/src/main/java/io/opentelemetry/instrumentation/gradle/AutoInstrumentationPlugin.java +++ b/buildSrc/src/main/java/io/opentelemetry/instrumentation/gradle/AutoInstrumentationPlugin.java @@ -33,20 +33,6 @@ public void apply(Project project) { project.getPlugins().apply(JavaLibraryPlugin.class); createLibraryConfiguration(project); addDependencies(project); - project - .getTasks() - .withType( - Test.class, - task -> { - task.dependsOn(":testing-bootstrap:shadowJar"); - File testingBootstrapJar = - new File( - project.project(":testing-bootstrap").getBuildDir(), - "libs/testing-bootstrap.jar"); - // Make sure tests get rerun if the contents of the testing-bootstrap.jar change - task.getInputs().property("testing-bootstrap-jar", testingBootstrapJar); - task.getJvmArgumentProviders().add(new InstrumentationTestArgs(testingBootstrapJar)); - }); } private void addDependencies(Project project) { diff --git a/instrumentation/apache-httpasyncclient-4.1/build.gradle.kts b/instrumentation/apache-httpasyncclient-4.1/build.gradle.kts index 8b186e64b..f52b85ea8 100644 --- a/instrumentation/apache-httpasyncclient-4.1/build.gradle.kts +++ b/instrumentation/apache-httpasyncclient-4.1/build.gradle.kts @@ -32,9 +32,6 @@ dependencies { api(project(":instrumentation:apache-httpclient-4.0")) implementation("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-apache-httpasyncclient-4.1:${versions["opentelemetry_java_agent"]}") - testImplementation("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-apache-httpclient-4.0:${versions["opentelemetry_java_agent"]}") - testImplementation("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-semconv:${versions["opentelemetry_semconv"]}") library("org.apache.httpcomponents:httpasyncclient:4.1") - testImplementation(testFixtures(project(":testing-common"))) + testImplementation(project(":testing-common")) } - diff --git a/instrumentation/apache-httpasyncclient-4.1/src/test/java/io/opentelemetry/instrumentation/hypertrace/apachehttpasyncclient/ApacheAsyncClientInstrumentationModuleTest.java b/instrumentation/apache-httpasyncclient-4.1/src/test/java/io/opentelemetry/instrumentation/hypertrace/apachehttpasyncclient/ApacheAsyncClientInstrumentationModuleTest.java index 280426443..8a24b3131 100644 --- a/instrumentation/apache-httpasyncclient-4.1/src/test/java/io/opentelemetry/instrumentation/hypertrace/apachehttpasyncclient/ApacheAsyncClientInstrumentationModuleTest.java +++ b/instrumentation/apache-httpasyncclient-4.1/src/test/java/io/opentelemetry/instrumentation/hypertrace/apachehttpasyncclient/ApacheAsyncClientInstrumentationModuleTest.java @@ -16,7 +16,7 @@ package io.opentelemetry.instrumentation.hypertrace.apachehttpasyncclient; -import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.proto.trace.v1.Span; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -41,7 +41,6 @@ import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; import org.apache.http.impl.nio.client.HttpAsyncClients; import org.apache.http.message.BasicHeader; -import org.hypertrace.agent.core.instrumentation.HypertraceSemanticAttributes; import org.hypertrace.agent.testing.AbstractInstrumenterTest; import org.hypertrace.agent.testing.TestHttpServer; import org.hypertrace.agent.testing.TestHttpServer.GetJsonHandler; @@ -84,27 +83,33 @@ public void getJson() Assertions.assertEquals(GetJsonHandler.RESPONSE_BODY, responseBody); TEST_WRITER.waitForTraces(1); - // TODO : It needs some time to create second span for responseBody - TEST_WRITER.waitForSpans(2); - List> traces = TEST_WRITER.getTraces(); + // exclude server spans + List> traces = + TEST_WRITER.waitForSpans( + 2, span -> span.getKind().equals(Span.SpanKind.SPAN_KIND_SERVER), 123); Assertions.assertEquals(1, traces.size()); Assertions.assertEquals(2, traces.get(0).size()); - SpanData clientSpan = traces.get(0).get(0); + Span clientSpan = traces.get(0).get(1); + Span responseBodySpan = traces.get(0).get(0); + if (traces.get(0).get(0).getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT)) { + clientSpan = traces.get(0).get(0); + responseBodySpan = traces.get(0).get(1); + } Assertions.assertEquals( "test-value", - clientSpan - .getAttributes() - .get(HypertraceSemanticAttributes.httpResponseHeader("test-response-header"))); + TEST_WRITER + .getAttributesMap(clientSpan) + .get("http.response.header.test-response-header") + .getStringValue()); Assertions.assertEquals( "bar", - clientSpan.getAttributes().get(HypertraceSemanticAttributes.httpRequestHeader("foo"))); - Assertions.assertNull( - clientSpan.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); - SpanData responseBodySpan = traces.get(0).get(1); + TEST_WRITER.getAttributesMap(clientSpan).get("http.request.header.foo").getStringValue()); + Assertions.assertNull(TEST_WRITER.getAttributesMap(clientSpan).get("http.request.body")); + Assertions.assertEquals( GetJsonHandler.RESPONSE_BODY, - responseBodySpan.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER.getAttributesMap(responseBodySpan).get("http.response.body").getStringValue()); } @Test @@ -136,22 +141,24 @@ public void postJsonEntity(HttpEntity entity) Assertions.assertEquals(204, response.getStatusLine().getStatusCode()); TEST_WRITER.waitForTraces(1); - List> traces = TEST_WRITER.getTraces(); + // exclude server spans + List> traces = + TEST_WRITER.waitForSpans(1, span -> span.getKind().equals(Span.SpanKind.SPAN_KIND_SERVER)); Assertions.assertEquals(1, traces.size()); Assertions.assertEquals(1, traces.get(0).size()); - SpanData clientSpan = traces.get(0).get(0); + Span clientSpan = traces.get(0).get(0); String requestBody = readInputStream(entity.getContent()); Assertions.assertEquals( "test-value", - clientSpan - .getAttributes() - .get(HypertraceSemanticAttributes.httpResponseHeader("test-response-header"))); + TEST_WRITER + .getAttributesMap(clientSpan) + .get("http.response.header.test-response-header") + .getStringValue()); Assertions.assertEquals( requestBody, - clientSpan.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); - Assertions.assertNull( - clientSpan.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER.getAttributesMap(clientSpan).get("http.request.body").getStringValue()); + Assertions.assertNull(TEST_WRITER.getAttributesMap(clientSpan).get("http.response.body")); } private static String readInputStream(InputStream inputStream) throws IOException { @@ -168,7 +175,7 @@ private static String readInputStream(InputStream inputStream) throws IOExceptio return textBuilder.toString(); } - class NonRepeatableStringEntity extends StringEntity { + static class NonRepeatableStringEntity extends StringEntity { public NonRepeatableStringEntity(String s) throws UnsupportedEncodingException { super(s); diff --git a/instrumentation/apache-httpclient-4.0/build.gradle.kts b/instrumentation/apache-httpclient-4.0/build.gradle.kts index d2045c18a..9c4503a4e 100644 --- a/instrumentation/apache-httpclient-4.0/build.gradle.kts +++ b/instrumentation/apache-httpclient-4.0/build.gradle.kts @@ -41,9 +41,6 @@ val versions: Map by extra dependencies { api(project(":instrumentation:java-streams")) - testImplementation("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-apache-httpclient-4.0:${versions["opentelemetry_java_agent"]}") library("org.apache.httpcomponents:httpclient:4.0") - - testImplementation(testFixtures(project(":testing-common"))) - testImplementation("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-semconv:${versions["opentelemetry_semconv"]}") + testImplementation(project(":testing-common")) } diff --git a/instrumentation/build.gradle.kts b/instrumentation/build.gradle.kts index cd687806f..014016548 100644 --- a/instrumentation/build.gradle.kts +++ b/instrumentation/build.gradle.kts @@ -62,3 +62,111 @@ tasks { relocate("io.opentelemetry.extension.aws", "io.opentelemetry.javaagent.shaded.io.opentelemetry.extension.aws") } } + +subprojects { + class JavaagentTestArgumentsProvider( + @InputFile + @PathSensitive(org.gradle.api.tasks.PathSensitivity.RELATIVE) + val agentShadowJar: File, + + @InputFile + @PathSensitive(org.gradle.api.tasks.PathSensitivity.RELATIVE) + val shadowJar: File, + + @InputFile + @PathSensitive(org.gradle.api.tasks.PathSensitivity.RELATIVE) + val extensionJar: File, + ) : CommandLineArgumentProvider { + override fun asArguments(): Iterable = listOf( + "-Dotel.javaagent.debug=true", + "-javaagent:${agentShadowJar.absolutePath}", + "-Dht.javaagent.filter.jar.paths=${extensionJar.absolutePath}", + "-Dotel.exporter.otlp.protocol=http/protobuf", + "-Dotel.exporter.otlp.traces.endpoint=http://localhost:4318/v1/traces", + "-Dotel.metrics.exporter=none", + // make the path to the javaagent available to tests + "-Dotel.javaagent.testing.javaagent-jar-path=${agentShadowJar.absolutePath}", + "-Dotel.javaagent.experimental.initializer.jar=${shadowJar.absolutePath}", + + // prevent sporadic gradle deadlocks, see SafeLogger for more details + "-Dotel.javaagent.testing.transform-safe-logging.enabled=true", + // Reduce noise in assertion messages since we don't need to verify this in most tests. We check + // in smoke tests instead. + "-Dotel.javaagent.add-thread-details=false", + // suppress repeated logging of "No metric data to export - skipping export." + // since PeriodicMetricReader is configured with a short interval + "-Dio.opentelemetry.javaagent.slf4j.simpleLogger.log.io.opentelemetry.sdk.metrics.export.PeriodicMetricReader=INFO", + // suppress a couple of verbose ClassNotFoundException stack traces logged at debug level + "-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" + ) + } + + tasks.withType().configureEach { + val instShadowTask: Jar = project(":instrumentation").tasks.named("shadowJar").get() + inputs.files(layout.files(instShadowTask)) + val instShadowJar = instShadowTask.archiveFile.get().asFile + + val shadowTask: Jar = project(":javaagent").tasks.named("shadowJar").get() + inputs.files(layout.files(shadowTask)) + val agentShadowJar = shadowTask.archiveFile.get().asFile + + val extensionBuild: Jar = project(":tests-extension").tasks.named("shadowJar").get() + inputs.files(layout.files(extensionBuild)) + val extensionJar = extensionBuild.archiveFile.get().asFile + + dependsOn(":instrumentation:shadowJar") + + dependsOn(":javaagent:shadowJar") + + dependsOn(":tests-extension:shadowJar") + + jvmArgumentProviders.add(JavaagentTestArgumentsProvider(agentShadowJar, instShadowJar, extensionJar)) + + // 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 + ) + ) { + // 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 + } + + val lib = it.absoluteFile + if (lib.name.startsWith("opentelemetry-javaagent-")) { + // These dependencies 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 + } + if (lib.name.startsWith("javaagent-core")) { + // These dependencies 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 + } + if (lib.name.startsWith("filter-api")) { + // These dependencies 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 + } + if (lib.name.startsWith("opentelemetry-") && lib.name.contains("-autoconfigure-")) { + // These dependencies should not be on the test classpath, because they will auto-instrument + // the library and the tests could pass even if the javaagent instrumentation fails to apply + return@filter false + } + return@filter true + } + } + + configurations.configureEach { + if (name.endsWith("testruntimeclasspath", ignoreCase = true)) { + // Added by agent, don't let Gradle bring it in when running tests. + exclude("io.opentelemetry.javaagent", "opentelemetry-javaagent-bootstrap") + } + } + +} diff --git a/instrumentation/grpc-1.6/build.gradle.kts b/instrumentation/grpc-1.6/build.gradle.kts index 9a37bec5d..87742e9f8 100644 --- a/instrumentation/grpc-1.6/build.gradle.kts +++ b/instrumentation/grpc-1.6/build.gradle.kts @@ -35,8 +35,6 @@ idea { } } -val testGrpcVersion = "1.30.0" - protobuf { protoc { // The artifact spec for the Protobuf Compiler @@ -61,7 +59,6 @@ val grpcVersion = "1.6.0" dependencies { api("io.opentelemetry.instrumentation:opentelemetry-grpc-1.6:${versions["opentelemetry_java_agent"]}") - testImplementation("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-grpc-1.6:${versions["opentelemetry_java_agent"]}") implementation(project(":instrumentation:grpc-common")) implementation(project(":shaded-protobuf-java-util", "shadow")) @@ -74,7 +71,7 @@ dependencies { implementation("javax.annotation:javax.annotation-api:1.3.2") - testImplementation(testFixtures(project(":testing-common"))) + testImplementation(project(":testing-common")) testImplementation(files(project(":instrumentation:grpc-shaded-netty-1.9").artifacts)) @@ -128,20 +125,52 @@ for (version in listOf("1.30.0")) { extendsFrom(configurations.runtimeClasspath.get()) } dependencies { - versionedConfiguration(testFixtures(project(":testing-common"))) - versionedConfiguration("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-grpc-1.6:${versions["opentelemetry_java_agent"]}") - versionedConfiguration("io.opentelemetry.instrumentation:opentelemetry-grpc-1.6:${versions["opentelemetry_java_agent"]}") + versionedConfiguration(project(":testing-common")) versionedConfiguration(project(":instrumentation:grpc-shaded-netty-1.9")) versionedConfiguration(platform("io.grpc:grpc-bom:$version")) versionedConfiguration("io.grpc:grpc-core") versionedConfiguration("io.grpc:grpc-protobuf") versionedConfiguration("io.grpc:grpc-stub") versionedConfiguration("io.grpc:grpc-netty") - } val versionedTest = task("test_${version}") { group = "verification" classpath = versionedConfiguration + sourceSets.main.get().output + sourceSets.test.get().output + sourceSets.named(computeSourceSetNameForVersion(version)).get().output + // 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 + ) + ) { + // 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 + } + + val lib = it.absoluteFile + if (lib.name.startsWith("opentelemetry-javaagent-")) { + // These dependencies 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 + } + if (lib.name.startsWith("javaagent-core")) { + // These dependencies 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 + } + if (lib.name.startsWith("filter-api")) { + // These dependencies 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 + } + if (lib.name.startsWith("opentelemetry-") && lib.name.contains("-autoconfigure-")) { + // These dependencies should not be on the test classpath, because they will auto-instrument + // the library and the tests could pass even if the javaagent instrumentation fails to apply + return@filter false + } + return@filter true + } useJUnitPlatform() } tasks.check { dependsOn(versionedTest) } diff --git a/instrumentation/grpc-1.6/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/grpc/v1_6/GrpcInstrumentationTest.java b/instrumentation/grpc-1.6/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/grpc/v1_6/GrpcInstrumentationTest.java index 770b1e273..347c09a98 100644 --- a/instrumentation/grpc-1.6/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/grpc/v1_6/GrpcInstrumentationTest.java +++ b/instrumentation/grpc-1.6/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/grpc/v1_6/GrpcInstrumentationTest.java @@ -29,16 +29,10 @@ import io.grpc.Status; import io.grpc.StatusRuntimeException; import io.grpc.stub.MetadataUtils; -import io.opentelemetry.javaagent.instrumentation.hypertrace.grpc.GrpcSemanticAttributes; -import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.proto.trace.v1.Span; import java.io.IOException; -import java.net.URL; import java.util.List; import java.util.concurrent.TimeoutException; -import org.hypertrace.agent.core.config.InstrumentationConfig; -import org.hypertrace.agent.core.instrumentation.HypertraceSemanticAttributes; -import org.hypertrace.agent.otel.extensions.config.EnvironmentConfig; -import org.hypertrace.agent.otel.extensions.config.HypertraceConfig; import org.hypertrace.agent.testing.AbstractInstrumenterTest; import org.hypertrace.example.GreeterGrpc; import org.hypertrace.example.GreeterGrpc.GreeterBlockingStub; @@ -46,7 +40,6 @@ import org.hypertrace.example.Helloworld.Request; import org.hypertrace.example.Helloworld.Response; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; @@ -113,11 +106,6 @@ public static void close() { SERVER.shutdownNow(); } - @AfterEach - public void afterEach() { - HypertraceConfig.reset(); - } - @Test @Order(2) public void blockingStub() throws IOException, TimeoutException, InterruptedException { @@ -131,16 +119,18 @@ public void blockingStub() throws IOException, TimeoutException, InterruptedExce String requestJson = JsonFormat.printer().print(REQUEST); String responseJson = JsonFormat.printer().print(response); - - TEST_WRITER.waitForSpans(2); - List> traces = TEST_WRITER.getTraces(); + List> traces = TEST_WRITER.waitForSpans(2); Assertions.assertEquals(1, traces.size()); - List spans = traces.get(0); + List spans = traces.get(0); Assertions.assertEquals(2, spans.size()); - SpanData clientSpan = spans.get(0); + Span clientSpan = spans.get(0); + Span serverSpan = spans.get(1); + if (spans.get(0).getKind().equals(Span.SpanKind.SPAN_KIND_SERVER)) { + clientSpan = spans.get(1); + serverSpan = spans.get(0); + } assertBodiesAndHeaders(clientSpan, requestJson, responseJson); - SpanData serverSpan = spans.get(1); assertBodiesAndHeaders(serverSpan, requestJson, responseJson); assertHttp2HeadersForSayHelloMethod(serverSpan); @@ -162,98 +152,57 @@ public void serverRequestBlocking() throws TimeoutException, InterruptedExceptio Assertions.assertEquals(Status.PERMISSION_DENIED.getCode(), ex.getStatus().getCode()); } - TEST_WRITER.waitForSpans(2); - List> traces = TEST_WRITER.getTraces(); + List> traces = TEST_WRITER.waitForSpans(2); Assertions.assertEquals(1, traces.size()); - List spans = traces.get(0); + List spans = traces.get(0); Assertions.assertEquals(2, spans.size()); - SpanData serverSpan = spans.get(1); - Assertions.assertNull( - serverSpan.getAttributes().get(HypertraceSemanticAttributes.RPC_REQUEST_BODY)); - Assertions.assertNull( - serverSpan.getAttributes().get(HypertraceSemanticAttributes.RPC_RESPONSE_BODY)); + Span serverSpan = spans.get(1); + if (spans.get(0).getKind().equals(Span.SpanKind.SPAN_KIND_SERVER)) { + serverSpan = spans.get(0); + } + Assertions.assertNull(TEST_WRITER.getAttributesMap(serverSpan).get("rpc.request.body")); + Assertions.assertNull(TEST_WRITER.getAttributesMap(serverSpan).get("rpc.response.body")); Assertions.assertEquals( "true", - serverSpan - .getAttributes() - .get(HypertraceSemanticAttributes.rpcRequestMetadata("mockblock"))); + TEST_WRITER + .getAttributesMap(serverSpan) + .get("rpc.request.metadata.mockblock") + .getStringValue()); assertHttp2HeadersForSayHelloMethod(serverSpan); } - @Test - @Order(3) - public void disabledInstrumentation_dynamicConfig() - throws TimeoutException, InterruptedException { - URL configUrl = getClass().getClassLoader().getResource("ht-config-all-disabled.yaml"); - System.setProperty(EnvironmentConfig.CONFIG_FILE_PROPERTY, configUrl.getPath()); - HypertraceConfig.reset(); - InstrumentationConfig.ConfigProvider.reset(); - - GreeterBlockingStub blockingStub = GreeterGrpc.newBlockingStub(CHANNEL); - Response response = blockingStub.sayHello(REQUEST); - - TEST_WRITER.waitForSpans(2); - List> traces = TEST_WRITER.getTraces(); - Assertions.assertEquals(1, traces.size()); - List spans = traces.get(0); - Assertions.assertEquals(2, spans.size()); - - SpanData clientSpan = spans.get(0); - Assertions.assertNull( - clientSpan.getAttributes().get(HypertraceSemanticAttributes.RPC_REQUEST_BODY)); - Assertions.assertNull( - clientSpan.getAttributes().get(HypertraceSemanticAttributes.RPC_RESPONSE_BODY)); - SpanData serverSpan = spans.get(1); - Assertions.assertNull( - serverSpan.getAttributes().get(HypertraceSemanticAttributes.RPC_REQUEST_BODY)); - Assertions.assertNull( - serverSpan.getAttributes().get(HypertraceSemanticAttributes.RPC_RESPONSE_BODY)); - } - - private void assertBodiesAndHeaders(SpanData span, String requestJson, String responseJson) { + private void assertBodiesAndHeaders(Span span, String requestJson, String responseJson) { Assertions.assertEquals( - requestJson, span.getAttributes().get(HypertraceSemanticAttributes.RPC_REQUEST_BODY)); + requestJson, TEST_WRITER.getAttributesMap(span).get("rpc.request.body").getStringValue()); Assertions.assertEquals( - responseJson, span.getAttributes().get(HypertraceSemanticAttributes.RPC_RESPONSE_BODY)); + responseJson, TEST_WRITER.getAttributesMap(span).get("rpc.response.body").getStringValue()); Assertions.assertEquals( "clientheader", - span.getAttributes() - .get( - HypertraceSemanticAttributes.rpcRequestMetadata( - CLIENT_STRING_METADATA_KEY.name()))); + TEST_WRITER + .getAttributesMap(span) + .get("rpc.request.metadata." + CLIENT_STRING_METADATA_KEY.name()) + .getStringValue()); Assertions.assertEquals( "serverheader", - span.getAttributes() - .get( - HypertraceSemanticAttributes.rpcResponseMetadata( - SERVER_STRING_METADATA_KEY.name()))); + TEST_WRITER + .getAttributesMap(span) + .get("rpc.response.metadata." + SERVER_STRING_METADATA_KEY.name()) + .getStringValue()); } - private void assertHttp2HeadersForSayHelloMethod(SpanData span) { + private void assertHttp2HeadersForSayHelloMethod(Span span) { Assertions.assertEquals( "http", - span.getAttributes() - .get( - HypertraceSemanticAttributes.rpcRequestMetadata( - ":" + GrpcSemanticAttributes.SCHEME))); + TEST_WRITER.getAttributesMap(span).get("rpc.request.metadata.:scheme").getStringValue()); Assertions.assertEquals( "POST", - span.getAttributes() - .get( - HypertraceSemanticAttributes.rpcRequestMetadata( - ":" + GrpcSemanticAttributes.METHOD))); + TEST_WRITER.getAttributesMap(span).get("rpc.request.metadata.:method").getStringValue()); Assertions.assertEquals( String.format("localhost:%d", SERVER.getPort()), - span.getAttributes() - .get( - HypertraceSemanticAttributes.rpcRequestMetadata( - ":" + GrpcSemanticAttributes.AUTHORITY))); + TEST_WRITER.getAttributesMap(span).get("rpc.request.metadata.:authority").getStringValue()); Assertions.assertEquals( "/org.hypertrace.example.Greeter/SayHello", - span.getAttributes() - .get( - HypertraceSemanticAttributes.rpcRequestMetadata( - ":" + GrpcSemanticAttributes.PATH))); + TEST_WRITER.getAttributesMap(span).get("rpc.request.metadata.:path").getStringValue()); } } diff --git a/instrumentation/grpc-1.6/src/test/resources/ht-config-all-disabled.yaml b/instrumentation/grpc-1.6/src/test/resources/ht-config-all-disabled.yaml deleted file mode 100644 index 4f8fb49a5..000000000 --- a/instrumentation/grpc-1.6/src/test/resources/ht-config-all-disabled.yaml +++ /dev/null @@ -1,17 +0,0 @@ -serviceName: app_under_test -reporting: - address: http://localhost:9411 - secure: true -dataCapture: - httpHeaders: - request: false - response: false - httpBody: - request: false - response: false - rpcMetadata: - request: false - response: false - rpcBody: - request: false - response: false diff --git a/instrumentation/java-streams/build.gradle.kts b/instrumentation/java-streams/build.gradle.kts index c1a05f636..b16973589 100644 --- a/instrumentation/java-streams/build.gradle.kts +++ b/instrumentation/java-streams/build.gradle.kts @@ -25,6 +25,5 @@ val versions: Map by extra dependencies { implementation("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api:${versions["opentelemetry"]}") - testImplementation(testFixtures(project(":testing-common"))) - testImplementation("io.opentelemetry.javaagent:opentelemetry-muzzle:${versions["opentelemetry_java_agent"]}") + testImplementation(project(":testing-common")) } diff --git a/instrumentation/java-streams/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/java/ContextAccessorInstrumentationModule.java b/instrumentation/java-streams/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/java/ContextAccessorInstrumentationModule.java deleted file mode 100644 index 00f4570b5..000000000 --- a/instrumentation/java-streams/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/java/ContextAccessorInstrumentationModule.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright The Hypertrace Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.opentelemetry.javaagent.instrumentation.hypertrace.java; - -import static net.bytebuddy.matcher.ElementMatchers.isPublic; -import static net.bytebuddy.matcher.ElementMatchers.named; -import static net.bytebuddy.matcher.ElementMatchers.takesArguments; - -import io.opentelemetry.instrumentation.api.util.VirtualField; -import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; -import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; -import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; -import io.opentelemetry.javaagent.tooling.muzzle.InstrumentationModuleMuzzle; -import io.opentelemetry.javaagent.tooling.muzzle.VirtualFieldMappingsBuilder; -import io.opentelemetry.javaagent.tooling.muzzle.references.ClassRef; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import net.bytebuddy.asm.Advice; -import net.bytebuddy.description.type.TypeDescription; -import net.bytebuddy.matcher.ElementMatcher; -import org.hypertrace.agent.core.instrumentation.SpanAndBuffer; -import org.hypertrace.agent.core.instrumentation.buffer.BoundedByteArrayOutputStream; - -// SPI explicitly added in META-INF/services/... -public class ContextAccessorInstrumentationModule extends InstrumentationModule - implements InstrumentationModuleMuzzle { - - @Override - public Map getMuzzleReferences() { - return Collections.emptyMap(); - } - - @Override - public void registerMuzzleVirtualFields(VirtualFieldMappingsBuilder builder) { - builder - .register("java.io.InputStream", SpanAndBuffer.class.getName()) - .register("java.io.OutputStream", BoundedByteArrayOutputStream.class.getName()); - } - - @Override - public List getMuzzleHelperClassNames() { - return Collections.emptyList(); - } - - public ContextAccessorInstrumentationModule() { - super("test-context-accessor", "ht"); - } - - @Override - public List typeInstrumentations() { - return Collections.singletonList(new ContextAccessorInstrumentation()); - } - - class ContextAccessorInstrumentation implements TypeInstrumentation { - - @Override - public ElementMatcher typeMatcher() { - return named("org.ContextAccessor"); - } - - @Override - public void transform(TypeTransformer transformer) { - transformer.applyAdviceToMethod( - named("addToInputStreamContext").and(takesArguments(2)).and(isPublic()), - ContextAccessorInstrumentationModule.class.getName() + "$AddToInputStreamContextAdvice"); - transformer.applyAdviceToMethod( - named("addToOutputStreamContext").and(takesArguments(2)).and(isPublic()), - ContextAccessorInstrumentationModule.class.getName() + "$AddToOutputStreamContextAdvice"); - } - } - - static class AddToInputStreamContextAdvice { - - @Advice.OnMethodEnter(suppress = Throwable.class) - public static void enter( - @Advice.Argument(0) InputStream inputStream, - @Advice.Argument(1) SpanAndBuffer spanAndBuffer) { - VirtualField.find(InputStream.class, SpanAndBuffer.class).set(inputStream, spanAndBuffer); - } - } - - static class AddToOutputStreamContextAdvice { - - @Advice.OnMethodEnter(suppress = Throwable.class) - public static void enter( - @Advice.Argument(0) OutputStream outputStream, - @Advice.Argument(1) BoundedByteArrayOutputStream buffer) { - VirtualField.find(OutputStream.class, BoundedByteArrayOutputStream.class) - .set(outputStream, buffer); - } - } -} diff --git a/instrumentation/java-streams/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/java/inputstream/InputStreamInstrumentationModuleTest.java b/instrumentation/java-streams/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/java/inputstream/InputStreamInstrumentationModuleTest.java deleted file mode 100644 index c86e60865..000000000 --- a/instrumentation/java-streams/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/java/inputstream/InputStreamInstrumentationModuleTest.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright The Hypertrace Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.opentelemetry.javaagent.instrumentation.hypertrace.java.inputstream; - -import io.opentelemetry.api.common.AttributeKey; -import io.opentelemetry.api.trace.Span; -import io.opentelemetry.sdk.trace.data.SpanData; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import java.util.List; -import org.ContextAccessor; -import org.hypertrace.agent.core.instrumentation.SpanAndBuffer; -import org.hypertrace.agent.core.instrumentation.buffer.BoundedBuffersFactory; -import org.hypertrace.agent.core.instrumentation.buffer.BoundedByteArrayOutputStream; -import org.hypertrace.agent.testing.AbstractInstrumenterTest; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -public class InputStreamInstrumentationModuleTest extends AbstractInstrumenterTest { - - private static final AttributeKey ATTRIBUTE_KEY = - AttributeKey.stringKey("captured-stream"); - private static final String STR = "hello"; - - @Test - public void read() { - InputStream inputStream = new ByteArrayInputStream(STR.getBytes()); - read( - inputStream, - () -> { - while (true) { - try { - if (!(inputStream.read() != -1)) break; - } catch (IOException e) { - e.printStackTrace(); - } - ; - } - }, - STR); - } - - @Test - public void readBytes() { - InputStream inputStream = new ByteArrayInputStream(STR.getBytes()); - read( - inputStream, - () -> { - while (true) { - try { - if (!(inputStream.read(new byte[10]) != -1)) break; - } catch (IOException e) { - e.printStackTrace(); - } - ; - } - }, - STR); - } - - @Test - public void readBytesOffset() { - InputStream inputStream = new ByteArrayInputStream(STR.getBytes()); - read( - inputStream, - () -> { - while (true) { - try { - if (!(inputStream.read(new byte[10], 0, 10) != -1)) break; - } catch (IOException e) { - e.printStackTrace(); - } - ; - } - }, - STR); - } - - private void read(InputStream inputStream, Runnable read, String expected) { - Span span = TEST_TRACER.spanBuilder("test-span").startSpan(); - - BoundedByteArrayOutputStream buffer = - BoundedBuffersFactory.createStream(StandardCharsets.ISO_8859_1); - ContextAccessor.addToInputStreamContext( - inputStream, new SpanAndBuffer(span, buffer, ATTRIBUTE_KEY, StandardCharsets.ISO_8859_1)); - - read.run(); - span.end(); - - List> traces = TEST_WRITER.getTraces(); - Assertions.assertEquals(1, traces.size()); - - List trace = traces.get(0); - Assertions.assertEquals(1, trace.size()); - SpanData spanData = trace.get(0); - Assertions.assertEquals(expected, spanData.getAttributes().get(ATTRIBUTE_KEY)); - } - - @Test - public void readAfterSpanEnd() { - - InputStream inputStream = new ByteArrayInputStream(STR.getBytes()); - - Span span = - TEST_TRACER - .spanBuilder("test-span") - .setAttribute("http.request.header.content-type", "application/json") - .setAttribute("http.response.header.content-type", "application/xml") - .startSpan(); - - Runnable read = - () -> { - while (true) { - try { - if (inputStream.read(new byte[10], 0, 10) == -1) break; - span.end(); - } catch (IOException e) { - e.printStackTrace(); - } - ; - } - }; - - BoundedByteArrayOutputStream buffer = - BoundedBuffersFactory.createStream(StandardCharsets.ISO_8859_1); - ContextAccessor.addToInputStreamContext( - inputStream, new SpanAndBuffer(span, buffer, ATTRIBUTE_KEY, StandardCharsets.ISO_8859_1)); - - read.run(); - - List> traces = TEST_WRITER.getTraces(); - Assertions.assertEquals(1, traces.size()); - - List trace = traces.get(0); - Assertions.assertEquals(2, trace.size()); - SpanData spanData = trace.get(1); - Assertions.assertEquals(STR, spanData.getAttributes().get(ATTRIBUTE_KEY)); - Assertions.assertEquals( - "application/json", - spanData.getAttributes().get(AttributeKey.stringKey("http.request.header.content-type"))); - Assertions.assertEquals( - "application/xml", - spanData.getAttributes().get(AttributeKey.stringKey("http.response.header.content-type"))); - } -} diff --git a/instrumentation/java-streams/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/java/outputstream/OutputStreamInstrumentationModuleTest.java b/instrumentation/java-streams/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/java/outputstream/OutputStreamInstrumentationModuleTest.java deleted file mode 100644 index c74778303..000000000 --- a/instrumentation/java-streams/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/java/outputstream/OutputStreamInstrumentationModuleTest.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright The Hypertrace Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.opentelemetry.javaagent.instrumentation.hypertrace.java.outputstream; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import org.ContextAccessor; -import org.hypertrace.agent.core.instrumentation.buffer.BoundedBuffersFactory; -import org.hypertrace.agent.core.instrumentation.buffer.BoundedByteArrayOutputStream; -import org.hypertrace.agent.testing.AbstractInstrumenterTest; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -class OutputStreamInstrumentationModuleTest extends AbstractInstrumenterTest { - private static final String STR1 = "hellofoobarrandom"; - private static final String STR2 = "someother"; - - private static final int MAX_SIZE = Integer.MAX_VALUE; - private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8; - - @Test - public void write() { - OutputStream outputStream = new ByteArrayOutputStream(); - write( - outputStream, - () -> { - for (byte ch : STR1.getBytes()) { - try { - outputStream.write(ch); - } catch (IOException e) { - e.printStackTrace(); - } - } - }, - STR1); - } - - @Test - public void writeBytes() { - OutputStream outputStream = new ByteArrayOutputStream(); - write( - outputStream, - () -> { - try { - outputStream.write(STR1.getBytes()); - outputStream.write(STR2.getBytes()); - } catch (IOException e) { - e.printStackTrace(); - } - }, - STR1 + STR2); - } - - @Test - public void writeBytesOffset() { - OutputStream outputStream = new ByteArrayOutputStream(); - write( - outputStream, - () -> { - try { - outputStream.write(STR1.getBytes(), 0, STR1.length() / 2); - outputStream.write(STR1.getBytes(), STR1.length() / 2, STR1.length() / 2 + 1); - outputStream.write(STR2.getBytes()); - } catch (IOException e) { - e.printStackTrace(); - } - }, - STR1 + STR2); - } - - private void write(OutputStream outputStream, Runnable read, String expected) { - BoundedByteArrayOutputStream buffer = - BoundedBuffersFactory.createStream(MAX_SIZE, DEFAULT_CHARSET); - ContextAccessor.addToOutputStreamContext(outputStream, buffer); - read.run(); - Assertions.assertEquals(expected, buffer.toString()); - } -} diff --git a/instrumentation/java-streams/src/test/java/org/ContextAccessor.java b/instrumentation/java-streams/src/test/java/org/ContextAccessor.java deleted file mode 100644 index aadbc571d..000000000 --- a/instrumentation/java-streams/src/test/java/org/ContextAccessor.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright The Hypertrace Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org; - -import java.io.InputStream; -import java.io.OutputStream; -import org.hypertrace.agent.core.instrumentation.SpanAndBuffer; -import org.hypertrace.agent.core.instrumentation.buffer.BoundedByteArrayOutputStream; - -public class ContextAccessor { - - private ContextAccessor() {} - - public static void addToInputStreamContext( - InputStream inputStream, SpanAndBuffer spanAndBuffer) {} - - public static void addToOutputStreamContext( - OutputStream outputStream, BoundedByteArrayOutputStream buffer) {} -} diff --git a/instrumentation/java-streams/src/test/resources/META-INF/services/io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule b/instrumentation/java-streams/src/test/resources/META-INF/services/io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule deleted file mode 100644 index fbbff48ef..000000000 --- a/instrumentation/java-streams/src/test/resources/META-INF/services/io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule +++ /dev/null @@ -1 +0,0 @@ -io.opentelemetry.javaagent.instrumentation.hypertrace.java.ContextAccessorInstrumentationModule diff --git a/instrumentation/jaxrs-client-2.0/build.gradle.kts b/instrumentation/jaxrs-client-2.0/build.gradle.kts index a0ffcda8d..2d504d2b2 100644 --- a/instrumentation/jaxrs-client-2.0/build.gradle.kts +++ b/instrumentation/jaxrs-client-2.0/build.gradle.kts @@ -33,18 +33,14 @@ val versions: Map by extra dependencies { api(project(":instrumentation:java-streams")) - api("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-http-url-connection:1.24.0-alpha") + api("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-http-url-connection:${versions["opentelemetry_java_agent"]}") compileOnly("javax.ws.rs:javax.ws.rs-api:2.0.1") - testImplementation(testFixtures(project(":testing-common"))) + testImplementation(project(":testing-common")) testImplementation("org.glassfish.jersey.core:jersey-client:2.27") testImplementation("org.glassfish.jersey.inject:jersey-hk2:2.27") - testImplementation("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-semconv:${versions["opentelemetry_semconv"]}") - testImplementation(project(":instrumentation:apache-httpclient-4.0")) - testImplementation(files(project(":instrumentation:apache-httpclient-4.0").dependencyProject.sourceSets.main.map { it.output })) - testImplementation("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-apache-httpclient-4.0:${versions["opentelemetry_java_agent"]}") testImplementation("org.jboss.resteasy:resteasy-client:3.0.5.Final") // ^ This version has timeouts https://issues.redhat.com/browse/RESTEASY-975 testImplementation("org.apache.cxf:cxf-rt-rs-client:3.1.0") diff --git a/instrumentation/micronaut-1.0/build.gradle.kts b/instrumentation/micronaut-1.0/build.gradle.kts index 266933290..a20204716 100644 --- a/instrumentation/micronaut-1.0/build.gradle.kts +++ b/instrumentation/micronaut-1.0/build.gradle.kts @@ -11,16 +11,13 @@ val micronautTestVersion = "1.0.0" dependencies { implementation(project(":instrumentation:netty:netty-4.1")) - testImplementation("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-netty-4.1:${versions["opentelemetry_java_agent"]}") - testImplementation(testFixtures(project(":testing-common"))) + testImplementation(project(":testing-common")) testImplementation("io.micronaut.test:micronaut-test-junit5:${micronautTestVersion}") testImplementation("io.micronaut:micronaut-http-server-netty:${micronautVersion}") testImplementation("io.micronaut:micronaut-runtime:${micronautVersion}") testImplementation("io.micronaut:micronaut-inject:${micronautVersion}") testImplementation("io.micronaut:micronaut-http-client:${micronautVersion}") testAnnotationProcessor("io.micronaut:micronaut-inject-java:${micronautVersion}") - testImplementation("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-semconv:${versions["opentelemetry_semconv"]}") - testImplementation("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-netty-4.1:${versions["opentelemetry_java_agent"]}") } val micronaut2Version = "2.2.3" @@ -30,7 +27,7 @@ for (version in listOf(micronaut2Version)) { extendsFrom(configurations.runtimeClasspath.get()) } dependencies { - versionedConfiguration(testFixtures(project(":testing-common"))) + versionedConfiguration(project(":testing-common")) versionedConfiguration("io.micronaut.test:micronaut-test-junit5:${micronautTestVersion}") versionedConfiguration("io.micronaut:micronaut-http-server-netty:${version}") versionedConfiguration("io.micronaut:micronaut-http-client:${version}") @@ -42,6 +39,41 @@ for (version in listOf(micronaut2Version)) { val versionedTest = task("test_${version}") { group = "verification" classpath = versionedConfiguration + sourceSets.test.get().output + // 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 + ) + ) { + // 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 + } + + val lib = it.absoluteFile + if (lib.name.startsWith("opentelemetry-javaagent-")) { + // These dependencies 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 + } + if (lib.name.startsWith("javaagent-core")) { + // These dependencies 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 + } + if (lib.name.startsWith("filter-api")) { + // These dependencies 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 + } + if (lib.name.startsWith("opentelemetry-") && lib.name.contains("-autoconfigure-")) { + // These dependencies should not be on the test classpath, because they will auto-instrument + // the library and the tests could pass even if the javaagent instrumentation fails to apply + return@filter false + } + return@filter true + } useJUnitPlatform() } tasks.check { dependsOn(versionedTest) } diff --git a/instrumentation/micronaut-1.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/micronaut/MicronautClientInstrumentationTest.java b/instrumentation/micronaut-1.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/micronaut/MicronautClientInstrumentationTest.java index 89ba1ada1..a5c698734 100644 --- a/instrumentation/micronaut-1.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/micronaut/MicronautClientInstrumentationTest.java +++ b/instrumentation/micronaut-1.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/micronaut/MicronautClientInstrumentationTest.java @@ -21,11 +21,10 @@ import io.micronaut.http.client.HttpClient; import io.micronaut.http.client.annotation.Client; import io.micronaut.test.annotation.MicronautTest; -import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.proto.trace.v1.Span; import java.util.List; import java.util.concurrent.TimeoutException; import javax.inject.Inject; -import org.hypertrace.agent.core.instrumentation.HypertraceSemanticAttributes; import org.hypertrace.agent.testing.AbstractInstrumenterTest; import org.hypertrace.agent.testing.TestHttpServer; import org.hypertrace.agent.testing.TestHttpServer.GetJsonHandler; @@ -69,27 +68,35 @@ public void getJson() throws InterruptedException, TimeoutException { Assertions.assertEquals(GetJsonHandler.RESPONSE_BODY, retrieve); TEST_WRITER.waitForTraces(1); - List> traces = TEST_WRITER.getTraces(); + List> traces = + TEST_WRITER.waitForSpans( + 1, + span -> + !span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT) + || span.getAttributesList().stream() + .noneMatch( + keyValue -> + keyValue.getKey().equals("http.url") + && keyValue.getValue().getStringValue().contains("/get_json"))); Assertions.assertEquals(1, traces.size()); Assertions.assertEquals(1, traces.get(0).size()); - SpanData clientSpan = traces.get(0).get(0); + Span clientSpan = traces.get(0).get(0); Assertions.assertEquals( REQUEST_HEADER_VALUE, - clientSpan - .getAttributes() - .get(HypertraceSemanticAttributes.httpRequestHeader(REQUEST_HEADER_NAME))); + TEST_WRITER + .getAttributesMap(clientSpan) + .get("http.request.header." + REQUEST_HEADER_NAME) + .getStringValue()); Assertions.assertEquals( TestHttpServer.RESPONSE_HEADER_VALUE, - clientSpan - .getAttributes() - .get( - HypertraceSemanticAttributes.httpResponseHeader( - TestHttpServer.RESPONSE_HEADER_NAME))); - Assertions.assertNull( - clientSpan.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); + TEST_WRITER + .getAttributesMap(clientSpan) + .get("http.response.header." + TestHttpServer.RESPONSE_HEADER_NAME) + .getStringValue()); + Assertions.assertNull(TEST_WRITER.getAttributesMap(clientSpan).get("http.request.body")); Assertions.assertEquals( GetJsonHandler.RESPONSE_BODY, - clientSpan.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER.getAttributesMap(clientSpan).get("http.response.body").getStringValue()); } @Test @@ -106,26 +113,26 @@ public void post() throws InterruptedException, TimeoutException { Assertions.assertEquals(204, response.getStatus().getCode()); TEST_WRITER.waitForTraces(1); - List> traces = TEST_WRITER.getTraces(); + List> traces = + TEST_WRITER.waitForSpans(1, span -> span.getKind().equals(Span.SpanKind.SPAN_KIND_SERVER)); Assertions.assertEquals(1, traces.size()); Assertions.assertEquals(1, traces.get(0).size()); - SpanData clientSpan = traces.get(0).get(0); + Span clientSpan = traces.get(0).get(0); Assertions.assertEquals( REQUEST_HEADER_VALUE, - clientSpan - .getAttributes() - .get(HypertraceSemanticAttributes.httpRequestHeader(REQUEST_HEADER_NAME))); + TEST_WRITER + .getAttributesMap(clientSpan) + .get("http.request.header." + REQUEST_HEADER_NAME) + .getStringValue()); Assertions.assertEquals( TestHttpServer.RESPONSE_HEADER_VALUE, - clientSpan - .getAttributes() - .get( - HypertraceSemanticAttributes.httpResponseHeader( - TestHttpServer.RESPONSE_HEADER_NAME))); + TEST_WRITER + .getAttributesMap(clientSpan) + .get("http.response.header." + TestHttpServer.RESPONSE_HEADER_NAME) + .getStringValue()); Assertions.assertEquals( REQUEST_BODY, - clientSpan.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); - Assertions.assertNull( - clientSpan.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER.getAttributesMap(clientSpan).get("http.request.body").getStringValue()); + Assertions.assertNull(TEST_WRITER.getAttributesMap(clientSpan).get("http.response.body")); } } diff --git a/instrumentation/micronaut-1.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/micronaut/MicronautInstrumentationTest.java b/instrumentation/micronaut-1.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/micronaut/MicronautInstrumentationTest.java index 05759de8f..3288765a3 100644 --- a/instrumentation/micronaut-1.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/micronaut/MicronautInstrumentationTest.java +++ b/instrumentation/micronaut-1.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/micronaut/MicronautInstrumentationTest.java @@ -18,7 +18,7 @@ import io.micronaut.runtime.server.EmbeddedServer; import io.micronaut.test.annotation.MicronautTest; -import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.proto.trace.v1.Span; import java.io.IOException; import java.util.List; import java.util.concurrent.TimeoutException; @@ -27,7 +27,6 @@ import okhttp3.RequestBody; import okhttp3.Response; import okio.Buffer; -import org.hypertrace.agent.core.instrumentation.HypertraceSemanticAttributes; import org.hypertrace.agent.testing.AbstractInstrumenterTest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -53,29 +52,28 @@ public void get() throws IOException, TimeoutException, InterruptedException { Assertions.assertEquals(204, response.code()); } - List> traces = TEST_WRITER.getTraces(); + List> traces = + TEST_WRITER.waitForSpans(1, span -> span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT)); TEST_WRITER.waitForTraces(1); Assertions.assertEquals(1, traces.size()); - List trace = traces.get(0); + List trace = traces.get(0); Assertions.assertEquals(1, trace.size()); - SpanData spanData = trace.get(0); + Span span = trace.get(0); Assertions.assertEquals( REQUEST_HEADER_VALUE, - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpRequestHeader(REQUEST_HEADER_NAME))); + TEST_WRITER + .getAttributesMap(span) + .get("http.request.header." + REQUEST_HEADER_NAME) + .getStringValue()); Assertions.assertEquals( TestController.RESPONSE_HEADER_VALUE, - spanData - .getAttributes() - .get( - HypertraceSemanticAttributes.httpResponseHeader( - TestController.RESPONSE_HEADER_NAME))); - Assertions.assertNull( - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); - Assertions.assertNull( - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER + .getAttributesMap(span) + .get("http.response.header." + TestController.RESPONSE_HEADER_NAME) + .getStringValue()); + Assertions.assertNull(TEST_WRITER.getAttributesMap(span).get("http.request.body")); + Assertions.assertNull(TEST_WRITER.getAttributesMap(span).get("http.response.body")); } @Test @@ -98,31 +96,32 @@ public void postJson() throws IOException, TimeoutException, InterruptedExceptio Assertions.assertEquals(TestController.RESPONSE_BODY, response.body().string()); } - List> traces = TEST_WRITER.getTraces(); + List> traces = + TEST_WRITER.waitForSpans(1, span -> span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT)); TEST_WRITER.waitForTraces(1); Assertions.assertEquals(1, traces.size()); - List trace = traces.get(0); + List trace = traces.get(0); Assertions.assertEquals(1, trace.size()); - SpanData spanData = trace.get(0); + Span span = trace.get(0); Assertions.assertEquals( REQUEST_HEADER_VALUE, - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpRequestHeader(REQUEST_HEADER_NAME))); + TEST_WRITER + .getAttributesMap(span) + .get("http.request.header." + REQUEST_HEADER_NAME) + .getStringValue()); Assertions.assertEquals( TestController.RESPONSE_HEADER_VALUE, - spanData - .getAttributes() - .get( - HypertraceSemanticAttributes.httpResponseHeader( - TestController.RESPONSE_HEADER_NAME))); + TEST_WRITER + .getAttributesMap(span) + .get("http.response.header." + TestController.RESPONSE_HEADER_NAME) + .getStringValue()); Assertions.assertEquals( requestBodyStr, - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); + TEST_WRITER.getAttributesMap(span).get("http.request.body").getStringValue()); Assertions.assertEquals( TestController.RESPONSE_BODY, - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER.getAttributesMap(span).get("http.response.body").getStringValue()); } @Test @@ -144,23 +143,24 @@ public void stream() throws IOException, TimeoutException, InterruptedException Assertions.assertEquals(responseBody.toString(), response.body().string()); } - List> traces = TEST_WRITER.getTraces(); + List> traces = + TEST_WRITER.waitForSpans(1, span -> span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT)); TEST_WRITER.waitForTraces(1); Assertions.assertEquals(1, traces.size()); - List trace = traces.get(0); + List trace = traces.get(0); Assertions.assertEquals(1, trace.size()); - SpanData spanData = trace.get(0); + Span span = trace.get(0); Assertions.assertEquals( REQUEST_HEADER_VALUE, - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpRequestHeader(REQUEST_HEADER_NAME))); - Assertions.assertNull( - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); + TEST_WRITER + .getAttributesMap(span) + .get("http.request.header." + REQUEST_HEADER_NAME) + .getStringValue()); + Assertions.assertNull(TEST_WRITER.getAttributesMap(span).get("http.request.body")); Assertions.assertEquals( responseBody.toString(), - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER.getAttributesMap(span).get("http.response.body").getStringValue()); } @Test @@ -178,25 +178,24 @@ public void blocking() throws IOException, TimeoutException, InterruptedExceptio Assertions.assertEquals("Hypertrace Blocked Request", response.body().string()); } - List> traces = TEST_WRITER.getTraces(); + List> traces = + TEST_WRITER.waitForSpans(1, span -> span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT)); TEST_WRITER.waitForTraces(1); Assertions.assertEquals(1, traces.size()); - List trace = traces.get(0); + List trace = traces.get(0); Assertions.assertEquals(1, trace.size()); - SpanData spanData = trace.get(0); + Span span = trace.get(0); Assertions.assertEquals( REQUEST_HEADER_VALUE, - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpRequestHeader(REQUEST_HEADER_NAME))); - Assertions.assertNull( - spanData - .getAttributes() - .get( - HypertraceSemanticAttributes.httpResponseHeader( - TestController.RESPONSE_HEADER_NAME))); + TEST_WRITER + .getAttributesMap(span) + .get("http.request.header." + REQUEST_HEADER_NAME) + .getStringValue()); Assertions.assertNull( - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER + .getAttributesMap(span) + .get("http.response.header." + TestController.RESPONSE_HEADER_NAME)); + Assertions.assertNull(TEST_WRITER.getAttributesMap(span).get("http.response.body")); } } diff --git a/instrumentation/micronaut-3.0/build.gradle.kts b/instrumentation/micronaut-3.0/build.gradle.kts index de78133ea..17b4ca464 100644 --- a/instrumentation/micronaut-3.0/build.gradle.kts +++ b/instrumentation/micronaut-3.0/build.gradle.kts @@ -11,8 +11,7 @@ val micronautTestVersion = "3.0.5" dependencies { implementation(project(":instrumentation:netty:netty-4.1")) - testImplementation("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-netty-4.1:${versions["opentelemetry_java_agent"]}") - testImplementation(testFixtures(project(":testing-common"))) + testImplementation(project(":testing-common")) testImplementation("io.micronaut.test:micronaut-test-junit5:${micronautTestVersion}") testImplementation("io.micronaut:micronaut-http-server-netty:${micronautVersion}") testImplementation("io.micronaut:micronaut-runtime:${micronautVersion}") @@ -22,6 +21,4 @@ dependencies { testImplementation("io.micronaut.rxjava2:micronaut-rxjava2:1.1.0") testImplementation("io.micronaut.rxjava2:micronaut-rxjava2-http-server-netty:1.1.0") testImplementation("io.micronaut.rxjava2:micronaut-rxjava2-http-client:1.1.0") - testImplementation("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-semconv:${versions["opentelemetry_semconv"]}") - testImplementation("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-netty-4.1:${versions["opentelemetry_java_agent"]}") } diff --git a/instrumentation/micronaut-3.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/micronaut/v3/MicronautClientInstrumentationTest.java b/instrumentation/micronaut-3.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/micronaut/v3/MicronautClientInstrumentationTest.java index b47f2e266..3c29dd98f 100644 --- a/instrumentation/micronaut-3.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/micronaut/v3/MicronautClientInstrumentationTest.java +++ b/instrumentation/micronaut-3.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/micronaut/v3/MicronautClientInstrumentationTest.java @@ -21,11 +21,10 @@ import io.micronaut.http.client.HttpClient; import io.micronaut.http.client.annotation.Client; import io.micronaut.test.extensions.junit5.annotation.MicronautTest; -import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.proto.trace.v1.Span; import jakarta.inject.Inject; import java.util.List; import java.util.concurrent.TimeoutException; -import org.hypertrace.agent.core.instrumentation.HypertraceSemanticAttributes; import org.hypertrace.agent.testing.AbstractInstrumenterTest; import org.hypertrace.agent.testing.TestHttpServer; import org.hypertrace.agent.testing.TestHttpServer.GetJsonHandler; @@ -69,27 +68,27 @@ public void getJson() throws InterruptedException, TimeoutException { Assertions.assertEquals(GetJsonHandler.RESPONSE_BODY, retrieve); TEST_WRITER.waitForTraces(1); - List> traces = TEST_WRITER.getTraces(); + List> traces = + TEST_WRITER.waitForSpans(1, span -> span.getKind().equals(Span.SpanKind.SPAN_KIND_SERVER)); Assertions.assertEquals(1, traces.size()); Assertions.assertEquals(1, traces.get(0).size()); - SpanData clientSpan = traces.get(0).get(0); + Span clientSpan = traces.get(0).get(0); Assertions.assertEquals( REQUEST_HEADER_VALUE, - clientSpan - .getAttributes() - .get(HypertraceSemanticAttributes.httpRequestHeader(REQUEST_HEADER_NAME))); + TEST_WRITER + .getAttributesMap(clientSpan) + .get("http.request.header." + REQUEST_HEADER_NAME) + .getStringValue()); Assertions.assertEquals( TestHttpServer.RESPONSE_HEADER_VALUE, - clientSpan - .getAttributes() - .get( - HypertraceSemanticAttributes.httpResponseHeader( - TestHttpServer.RESPONSE_HEADER_NAME))); - Assertions.assertNull( - clientSpan.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); + TEST_WRITER + .getAttributesMap(clientSpan) + .get("http.response.header." + TestHttpServer.RESPONSE_HEADER_NAME) + .getStringValue()); + Assertions.assertNull(TEST_WRITER.getAttributesMap(clientSpan).get("http.request.body")); Assertions.assertEquals( GetJsonHandler.RESPONSE_BODY, - clientSpan.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER.getAttributesMap(clientSpan).get("http.response.body").getStringValue()); } @Test @@ -106,26 +105,26 @@ public void post() throws InterruptedException, TimeoutException { Assertions.assertEquals(204, response.getStatus().getCode()); TEST_WRITER.waitForTraces(1); - List> traces = TEST_WRITER.getTraces(); + List> traces = + TEST_WRITER.waitForSpans(1, span -> span.getKind().equals(Span.SpanKind.SPAN_KIND_SERVER)); Assertions.assertEquals(1, traces.size()); Assertions.assertEquals(1, traces.get(0).size()); - SpanData clientSpan = traces.get(0).get(0); + Span clientSpan = traces.get(0).get(0); Assertions.assertEquals( REQUEST_HEADER_VALUE, - clientSpan - .getAttributes() - .get(HypertraceSemanticAttributes.httpRequestHeader(REQUEST_HEADER_NAME))); + TEST_WRITER + .getAttributesMap(clientSpan) + .get("http.request.header." + REQUEST_HEADER_NAME) + .getStringValue()); Assertions.assertEquals( TestHttpServer.RESPONSE_HEADER_VALUE, - clientSpan - .getAttributes() - .get( - HypertraceSemanticAttributes.httpResponseHeader( - TestHttpServer.RESPONSE_HEADER_NAME))); + TEST_WRITER + .getAttributesMap(clientSpan) + .get("http.response.header." + TestHttpServer.RESPONSE_HEADER_NAME) + .getStringValue()); Assertions.assertEquals( REQUEST_BODY, - clientSpan.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); - Assertions.assertNull( - clientSpan.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER.getAttributesMap(clientSpan).get("http.request.body").getStringValue()); + Assertions.assertNull(TEST_WRITER.getAttributesMap(clientSpan).get("http.response.body")); } } diff --git a/instrumentation/micronaut-3.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/micronaut/v3/MicronautInstrumentationTest.java b/instrumentation/micronaut-3.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/micronaut/v3/MicronautInstrumentationTest.java index e2416f722..b04843f5e 100644 --- a/instrumentation/micronaut-3.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/micronaut/v3/MicronautInstrumentationTest.java +++ b/instrumentation/micronaut-3.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/micronaut/v3/MicronautInstrumentationTest.java @@ -18,7 +18,7 @@ import io.micronaut.runtime.server.EmbeddedServer; import io.micronaut.test.extensions.junit5.annotation.MicronautTest; -import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.proto.trace.v1.Span; import jakarta.inject.Inject; import java.io.IOException; import java.util.List; @@ -27,7 +27,6 @@ import okhttp3.RequestBody; import okhttp3.Response; import okio.Buffer; -import org.hypertrace.agent.core.instrumentation.HypertraceSemanticAttributes; import org.hypertrace.agent.testing.AbstractInstrumenterTest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -53,29 +52,28 @@ public void get() throws IOException, TimeoutException, InterruptedException { Assertions.assertEquals(204, response.code()); } - List> traces = TEST_WRITER.getTraces(); + List> traces = + TEST_WRITER.waitForSpans(1, span -> span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT)); TEST_WRITER.waitForTraces(1); Assertions.assertEquals(1, traces.size()); - List trace = traces.get(0); + List trace = traces.get(0); Assertions.assertEquals(1, trace.size()); - SpanData spanData = trace.get(0); + Span span = trace.get(0); Assertions.assertEquals( REQUEST_HEADER_VALUE, - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpRequestHeader(REQUEST_HEADER_NAME))); + TEST_WRITER + .getAttributesMap(span) + .get("http.request.header." + REQUEST_HEADER_NAME) + .getStringValue()); Assertions.assertEquals( TestController.RESPONSE_HEADER_VALUE, - spanData - .getAttributes() - .get( - HypertraceSemanticAttributes.httpResponseHeader( - TestController.RESPONSE_HEADER_NAME))); - Assertions.assertNull( - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); - Assertions.assertNull( - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER + .getAttributesMap(span) + .get("http.response.header." + TestController.RESPONSE_HEADER_NAME) + .getStringValue()); + Assertions.assertNull(TEST_WRITER.getAttributesMap(span).get("http.request.body")); + Assertions.assertNull(TEST_WRITER.getAttributesMap(span).get("http.response.body")); } @Test @@ -98,31 +96,32 @@ public void postJson() throws IOException, TimeoutException, InterruptedExceptio Assertions.assertEquals(TestController.RESPONSE_BODY, response.body().string()); } - List> traces = TEST_WRITER.getTraces(); + List> traces = + TEST_WRITER.waitForSpans(1, span -> span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT)); TEST_WRITER.waitForTraces(1); Assertions.assertEquals(1, traces.size()); - List trace = traces.get(0); + List trace = traces.get(0); Assertions.assertEquals(1, trace.size()); - SpanData spanData = trace.get(0); + Span span = trace.get(0); Assertions.assertEquals( REQUEST_HEADER_VALUE, - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpRequestHeader(REQUEST_HEADER_NAME))); + TEST_WRITER + .getAttributesMap(span) + .get("http.request.header." + REQUEST_HEADER_NAME) + .getStringValue()); Assertions.assertEquals( TestController.RESPONSE_HEADER_VALUE, - spanData - .getAttributes() - .get( - HypertraceSemanticAttributes.httpResponseHeader( - TestController.RESPONSE_HEADER_NAME))); + TEST_WRITER + .getAttributesMap(span) + .get("http.response.header." + TestController.RESPONSE_HEADER_NAME) + .getStringValue()); Assertions.assertEquals( requestBodyStr, - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); + TEST_WRITER.getAttributesMap(span).get("http.request.body").getStringValue()); Assertions.assertEquals( TestController.RESPONSE_BODY, - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER.getAttributesMap(span).get("http.response.body").getStringValue()); } @Test @@ -144,23 +143,24 @@ public void stream() throws IOException, TimeoutException, InterruptedException Assertions.assertEquals(responseBody.toString(), response.body().string()); } - List> traces = TEST_WRITER.getTraces(); + List> traces = + TEST_WRITER.waitForSpans(1, span -> span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT)); TEST_WRITER.waitForTraces(1); Assertions.assertEquals(1, traces.size()); - List trace = traces.get(0); + List trace = traces.get(0); Assertions.assertEquals(1, trace.size()); - SpanData spanData = trace.get(0); + Span span = trace.get(0); Assertions.assertEquals( REQUEST_HEADER_VALUE, - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpRequestHeader(REQUEST_HEADER_NAME))); - Assertions.assertNull( - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); + TEST_WRITER + .getAttributesMap(span) + .get("http.request.header." + REQUEST_HEADER_NAME) + .getStringValue()); + Assertions.assertNull(TEST_WRITER.getAttributesMap(span).get("http.request.body")); Assertions.assertEquals( responseBody.toString(), - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER.getAttributesMap(span).get("http.response.body").getStringValue()); } @Test @@ -178,25 +178,24 @@ public void blocking() throws IOException, TimeoutException, InterruptedExceptio Assertions.assertEquals("Hypertrace Blocked Request", response.body().string()); } - List> traces = TEST_WRITER.getTraces(); + List> traces = + TEST_WRITER.waitForSpans(1, span -> span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT)); TEST_WRITER.waitForTraces(1); Assertions.assertEquals(1, traces.size()); - List trace = traces.get(0); + List trace = traces.get(0); Assertions.assertEquals(1, trace.size()); - SpanData spanData = trace.get(0); + Span span = trace.get(0); Assertions.assertEquals( REQUEST_HEADER_VALUE, - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpRequestHeader(REQUEST_HEADER_NAME))); - Assertions.assertNull( - spanData - .getAttributes() - .get( - HypertraceSemanticAttributes.httpResponseHeader( - TestController.RESPONSE_HEADER_NAME))); + TEST_WRITER + .getAttributesMap(span) + .get("http.request.header." + REQUEST_HEADER_NAME) + .getStringValue()); Assertions.assertNull( - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER + .getAttributesMap(span) + .get("http.response.header." + TestController.RESPONSE_HEADER_NAME)); + Assertions.assertNull(TEST_WRITER.getAttributesMap(span).get("http.response.body")); } } diff --git a/instrumentation/netty/netty-4.0/build.gradle.kts b/instrumentation/netty/netty-4.0/build.gradle.kts index f62ddb27d..b8cab061e 100644 --- a/instrumentation/netty/netty-4.0/build.gradle.kts +++ b/instrumentation/netty/netty-4.0/build.gradle.kts @@ -49,8 +49,8 @@ dependencies { implementation("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-netty-4.0:${versions["opentelemetry_java_agent"]}") implementation("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-netty-4-common:${versions["opentelemetry_java_agent"]}") implementation("io.opentelemetry.instrumentation:opentelemetry-netty-4-common:${versions["opentelemetry_java_agent"]}") - implementation("io.opentelemetry:opentelemetry-semconv:${versions["opentelemetry_semconv"]}") - implementation("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-semconv:${versions["opentelemetry_semconv"]}") + implementation("io.opentelemetry.semconv:opentelemetry-semconv:${versions["opentelemetry_semconv"]}") + implementation("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-semconv:${versions["opentelemetry_api_semconv"]}") compileOnly("io.netty:netty-codec-http:${nettyVersion}") { version { strictly(nettyVersion) @@ -88,7 +88,7 @@ dependencies { } } - testImplementation(testFixtures(project(":testing-common"))) + testImplementation(project(":testing-common")) testImplementation("org.asynchttpclient:async-http-client:2.0.9") } diff --git a/instrumentation/netty/netty-4.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_0/client/HttpClientResponseTracingHandler.java b/instrumentation/netty/netty-4.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_0/client/HttpClientResponseTracingHandler.java index 354d9f649..438c1a46a 100644 --- a/instrumentation/netty/netty-4.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_0/client/HttpClientResponseTracingHandler.java +++ b/instrumentation/netty/netty-4.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_0/client/HttpClientResponseTracingHandler.java @@ -32,7 +32,7 @@ import io.opentelemetry.javaagent.instrumentation.hypertrace.netty.v4_0.AttributeKeys; import io.opentelemetry.javaagent.instrumentation.hypertrace.netty.v4_0.DataCaptureUtils; import io.opentelemetry.javaagent.instrumentation.netty.v4_0.client.NettyClientSingletons; -import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import io.opentelemetry.semconv.SemanticAttributes; import java.nio.charset.Charset; import java.util.Map; import org.hypertrace.agent.core.config.InstrumentationConfig; @@ -101,7 +101,7 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) { if (msg instanceof HttpResponse) { HttpResponse httpResponse = (HttpResponse) msg; int code = httpResponse.getStatus().code(); - span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, code); + span.setAttribute(SemanticAttributes.HTTP_RESPONSE_STATUS_CODE, code); span.setStatus(code >= 100 && code < 400 ? StatusCode.UNSET : StatusCode.ERROR); } if (msg instanceof LastHttpContent) { diff --git a/instrumentation/netty/netty-4.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_0/server/HttpServerResponseTracingHandler.java b/instrumentation/netty/netty-4.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_0/server/HttpServerResponseTracingHandler.java index 331979f28..0a83e153d 100644 --- a/instrumentation/netty/netty-4.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_0/server/HttpServerResponseTracingHandler.java +++ b/instrumentation/netty/netty-4.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_0/server/HttpServerResponseTracingHandler.java @@ -32,7 +32,7 @@ import io.opentelemetry.javaagent.instrumentation.hypertrace.netty.v4_0.AttributeKeys; import io.opentelemetry.javaagent.instrumentation.hypertrace.netty.v4_0.DataCaptureUtils; import io.opentelemetry.javaagent.instrumentation.netty.v4_0.server.NettyServerSingletons; -import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import io.opentelemetry.semconv.SemanticAttributes; import java.nio.charset.Charset; import java.util.Map; import org.hypertrace.agent.core.config.InstrumentationConfig; @@ -100,7 +100,7 @@ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise prm) { if (msg instanceof HttpResponse) { HttpResponse httpResponse = (HttpResponse) msg; int code = httpResponse.getStatus().code(); - span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, code); + span.setAttribute(SemanticAttributes.HTTP_RESPONSE_STATUS_CODE, code); span.setStatus(code >= 100 && code < 500 ? StatusCode.UNSET : StatusCode.ERROR); } if (msg instanceof LastHttpContent) { diff --git a/instrumentation/netty/netty-4.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_0/server/AbstractNetty40ServerInstrumentationTest.java b/instrumentation/netty/netty-4.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_0/server/AbstractNetty40ServerInstrumentationTest.java index dcc33e401..edfb04e59 100644 --- a/instrumentation/netty/netty-4.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_0/server/AbstractNetty40ServerInstrumentationTest.java +++ b/instrumentation/netty/netty-4.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_0/server/AbstractNetty40ServerInstrumentationTest.java @@ -20,7 +20,7 @@ import static io.opentelemetry.javaagent.instrumentation.hypertrace.netty.v4_0.server.NettyTestServer.RESPONSE_HEADER_NAME; import static io.opentelemetry.javaagent.instrumentation.hypertrace.netty.v4_0.server.NettyTestServer.RESPONSE_HEADER_VALUE; -import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.proto.trace.v1.Span; import java.io.IOException; import java.util.List; import java.util.concurrent.ExecutionException; @@ -29,13 +29,14 @@ import okhttp3.RequestBody; import okhttp3.Response; import okio.Buffer; -import org.hypertrace.agent.core.instrumentation.HypertraceSemanticAttributes; import org.hypertrace.agent.testing.AbstractInstrumenterTest; -import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +@TestInstance(TestInstance.Lifecycle.PER_CLASS) public abstract class AbstractNetty40ServerInstrumentationTest extends AbstractInstrumenterTest { public static final String REQUEST_HEADER_NAME = "reqheader"; @@ -44,13 +45,13 @@ public abstract class AbstractNetty40ServerInstrumentationTest extends AbstractI private static int port; private static NettyTestServer nettyTestServer; - @BeforeEach + @BeforeAll private void startServer() throws IOException, InterruptedException { nettyTestServer = createNetty(); port = nettyTestServer.create(); } - @AfterEach + @AfterAll private void stopServer() throws ExecutionException, InterruptedException { nettyTestServer.stopServer(); } @@ -70,27 +71,28 @@ public void get() throws IOException, TimeoutException, InterruptedException { Assertions.assertEquals(204, response.code()); } - List> traces = TEST_WRITER.getTraces(); TEST_WRITER.waitForTraces(1); + List> traces = + TEST_WRITER.waitForSpans(1, span -> span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT)); Assertions.assertEquals(1, traces.size()); - List trace = traces.get(0); + List trace = traces.get(0); Assertions.assertEquals(1, trace.size()); - SpanData spanData = trace.get(0); + Span span = trace.get(0); Assertions.assertEquals( REQUEST_HEADER_VALUE, - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpRequestHeader(REQUEST_HEADER_NAME))); + TEST_WRITER + .getAttributesMap(span) + .get("http.request.header." + REQUEST_HEADER_NAME) + .getStringValue()); Assertions.assertEquals( RESPONSE_HEADER_VALUE, - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpResponseHeader(RESPONSE_HEADER_NAME))); - Assertions.assertNull( - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); - Assertions.assertNull( - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER + .getAttributesMap(span) + .get("http.response.header." + RESPONSE_HEADER_NAME) + .getStringValue()); + Assertions.assertNull(TEST_WRITER.getAttributesMap(span).get("http.request.body")); + Assertions.assertNull(TEST_WRITER.getAttributesMap(span).get("http.response.body")); } @Test @@ -109,31 +111,34 @@ public void postJson() throws IOException, TimeoutException, InterruptedExceptio Assertions.assertEquals(RESPONSE_BODY, response.body().string()); } - List> traces = TEST_WRITER.getTraces(); TEST_WRITER.waitForTraces(1); + List> traces = + TEST_WRITER.waitForSpans(1, span -> span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT)); Assertions.assertEquals(1, traces.size()); - List trace = traces.get(0); + List trace = traces.get(0); Assertions.assertEquals(1, trace.size()); - SpanData spanData = trace.get(0); + Span span = trace.get(0); Assertions.assertEquals( REQUEST_HEADER_VALUE, - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpRequestHeader(REQUEST_HEADER_NAME))); + TEST_WRITER + .getAttributesMap(span) + .get("http.request.header." + REQUEST_HEADER_NAME) + .getStringValue()); Assertions.assertEquals( RESPONSE_HEADER_VALUE, - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpResponseHeader(RESPONSE_HEADER_NAME))); + TEST_WRITER + .getAttributesMap(span) + .get("http.response.header." + RESPONSE_HEADER_NAME) + .getStringValue()); Buffer requestBodyBuffer = new Buffer(); requestBody.writeTo(requestBodyBuffer); Assertions.assertEquals( new String(requestBodyBuffer.readByteArray()), - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); + TEST_WRITER.getAttributesMap(span).get("http.request.body").getStringValue()); Assertions.assertEquals( RESPONSE_BODY, - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER.getAttributesMap(span).get("http.response.body").getStringValue()); } @Test @@ -151,24 +156,23 @@ public void blocking() throws IOException, TimeoutException, InterruptedExceptio Assertions.assertEquals("Hypertrace Blocked Request", response.body().string()); } - List> traces = TEST_WRITER.getTraces(); TEST_WRITER.waitForTraces(1); + List> traces = + TEST_WRITER.waitForSpans(1, span -> span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT)); Assertions.assertEquals(1, traces.size()); - List trace = traces.get(0); + List trace = traces.get(0); Assertions.assertEquals(1, trace.size()); - SpanData spanData = trace.get(0); + Span span = trace.get(0); Assertions.assertEquals( REQUEST_HEADER_VALUE, - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpRequestHeader(REQUEST_HEADER_NAME))); + TEST_WRITER + .getAttributesMap(span) + .get("http.request.header." + REQUEST_HEADER_NAME) + .getStringValue()); Assertions.assertNull( - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpResponseHeader(RESPONSE_HEADER_NAME))); - Assertions.assertNull( - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER.getAttributesMap(span).get("http.response.header." + RESPONSE_HEADER_NAME)); + Assertions.assertNull(TEST_WRITER.getAttributesMap(span).get("http.response.body")); RequestBody requestBody = blockedRequestBody(true, 3000, 75); Request request2 = @@ -183,24 +187,24 @@ public void blocking() throws IOException, TimeoutException, InterruptedExceptio Assertions.assertEquals("Hypertrace Blocked Request", response.body().string()); } - List> traces2 = TEST_WRITER.getTraces(); TEST_WRITER.waitForTraces(2); + List> traces2 = + TEST_WRITER.waitForSpans( + 2, span1 -> span1.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT)); Assertions.assertEquals(2, traces2.size()); - List trace2 = traces2.get(1); + List trace2 = traces2.get(1); Assertions.assertEquals(1, trace2.size()); - SpanData spanData2 = trace2.get(0); + Span span2 = trace2.get(0); Assertions.assertEquals( REQUEST_HEADER_VALUE, - spanData2 - .getAttributes() - .get(HypertraceSemanticAttributes.httpRequestHeader(REQUEST_HEADER_NAME))); - Assertions.assertNull( - spanData2 - .getAttributes() - .get(HypertraceSemanticAttributes.httpResponseHeader(RESPONSE_HEADER_NAME))); + TEST_WRITER + .getAttributesMap(span2) + .get("http.request.header." + REQUEST_HEADER_NAME) + .getStringValue()); Assertions.assertNull( - spanData2.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER.getAttributesMap(span2).get("http.response.header." + RESPONSE_HEADER_NAME)); + Assertions.assertNull(TEST_WRITER.getAttributesMap(span2).get("http.response.body")); } @Test @@ -219,34 +223,36 @@ public void connectionKeepAlive() throws IOException, TimeoutException, Interrup Assertions.assertEquals(RESPONSE_BODY, response.body().string()); } - List> traces = TEST_WRITER.getTraces(); TEST_WRITER.waitForTraces(1); + List> traces = + TEST_WRITER.waitForSpans(1, span -> span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT)); Assertions.assertEquals(1, traces.size()); - List trace = traces.get(0); + List trace = traces.get(0); Assertions.assertEquals(1, trace.size()); - SpanData spanData = trace.get(0); + Span span = trace.get(0); Assertions.assertEquals( REQUEST_HEADER_VALUE, - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpRequestHeader(REQUEST_HEADER_NAME))); + TEST_WRITER + .getAttributesMap(span) + .get("http.request.header." + REQUEST_HEADER_NAME) + .getStringValue()); Assertions.assertEquals( "1st", - spanData.getAttributes().get(HypertraceSemanticAttributes.httpRequestHeader("first"))); + TEST_WRITER.getAttributesMap(span).get("http.request.header.first").getStringValue()); Assertions.assertEquals( "keep-alive", - spanData.getAttributes().get(HypertraceSemanticAttributes.httpRequestHeader("connection"))); + TEST_WRITER.getAttributesMap(span).get("http.request.header.connection").getStringValue()); Assertions.assertEquals( RESPONSE_HEADER_VALUE, - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpResponseHeader(RESPONSE_HEADER_NAME))); - Assertions.assertNull( - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); + TEST_WRITER + .getAttributesMap(span) + .get("http.response.header." + RESPONSE_HEADER_NAME) + .getStringValue()); + Assertions.assertNull(TEST_WRITER.getAttributesMap(span).get("http.request.body")); Assertions.assertEquals( RESPONSE_BODY, - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER.getAttributesMap(span).get("http.response.body").getStringValue()); RequestBody requestBody = blockedRequestBody(true, 3000, 75); Request request2 = @@ -263,35 +269,30 @@ public void connectionKeepAlive() throws IOException, TimeoutException, Interrup Assertions.assertEquals("Hypertrace Blocked Request", response.body().string()); } - List> traces2 = TEST_WRITER.getTraces(); TEST_WRITER.waitForTraces(2); + List> traces2 = + TEST_WRITER.waitForSpans( + 2, span1 -> span1.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT)); Assertions.assertEquals(2, traces2.size()); - List trace2 = traces2.get(1); + List trace2 = traces2.get(1); Assertions.assertEquals(1, trace2.size()); - SpanData spanData2 = trace2.get(0); + Span span2 = trace2.get(0); Assertions.assertEquals( "REQUEST_HEADER_VALUE", - spanData2 - .getAttributes() - .get(HypertraceSemanticAttributes.httpRequestHeader(REQUEST_HEADER_NAME))); + TEST_WRITER + .getAttributesMap(span2) + .get("http.request.header." + REQUEST_HEADER_NAME) + .getStringValue()); Assertions.assertEquals( "2nd", - spanData2.getAttributes().get(HypertraceSemanticAttributes.httpRequestHeader("second"))); + TEST_WRITER.getAttributesMap(span2).get("http.request.header.second").getStringValue()); Assertions.assertEquals( "keep-alive", - spanData2 - .getAttributes() - .get(HypertraceSemanticAttributes.httpRequestHeader("connection"))); - Assertions.assertNull( - spanData2.getAttributes().get(HypertraceSemanticAttributes.httpRequestHeader("first"))); - Assertions.assertNull( - spanData2 - .getAttributes() - .get(HypertraceSemanticAttributes.httpResponseHeader(RESPONSE_HEADER_NAME))); + TEST_WRITER.getAttributesMap(span2).get("http.request.header.connection").getStringValue()); + Assertions.assertNull(TEST_WRITER.getAttributesMap(span2).get("http.request.header.first")); Assertions.assertNull( - spanData2 - .getAttributes() - .get(HypertraceSemanticAttributes.httpResponseHeader(RESPONSE_BODY))); + TEST_WRITER.getAttributesMap(span2).get("http.response.header." + RESPONSE_HEADER_NAME)); + Assertions.assertNull(TEST_WRITER.getAttributesMap(span2).get("http.response.body")); } } diff --git a/instrumentation/netty/netty-4.1/build.gradle.kts b/instrumentation/netty/netty-4.1/build.gradle.kts index 50b12c314..43fb115cd 100644 --- a/instrumentation/netty/netty-4.1/build.gradle.kts +++ b/instrumentation/netty/netty-4.1/build.gradle.kts @@ -49,11 +49,11 @@ dependencies { implementation("io.opentelemetry.instrumentation:opentelemetry-netty-4-common:${versions["opentelemetry_java_agent"]}") implementation("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-netty-4.1:${versions["opentelemetry_java_agent"]}") implementation("io.opentelemetry.instrumentation:opentelemetry-netty-4.1:${versions["opentelemetry_java_agent"]}") - implementation("io.opentelemetry:opentelemetry-semconv:${versions["opentelemetry_semconv"]}") - implementation("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-semconv:${versions["opentelemetry_semconv"]}") + implementation("io.opentelemetry.semconv:opentelemetry-semconv:${versions["opentelemetry_semconv"]}") + implementation("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-semconv:${versions["opentelemetry_api_semconv"]}") library("io.netty:netty-codec-http:4.1.0.Final") - testImplementation(testFixtures(project(":testing-common"))) + testImplementation(project(":testing-common")) testImplementation("io.netty:netty-handler:4.1.0.Final") testImplementation("org.asynchttpclient:async-http-client:2.1.0") } diff --git a/instrumentation/netty/netty-4.1/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_1/NettyChannelPipelineInstrumentation.java b/instrumentation/netty/netty-4.1/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_1/NettyChannelPipelineInstrumentation.java index 6bf6fd483..1e55f9a39 100644 --- a/instrumentation/netty/netty-4.1/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_1/NettyChannelPipelineInstrumentation.java +++ b/instrumentation/netty/netty-4.1/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_1/NettyChannelPipelineInstrumentation.java @@ -39,7 +39,6 @@ import io.opentelemetry.javaagent.instrumentation.hypertrace.netty.v4_1.server.HttpServerBlockingRequestHandler; import io.opentelemetry.javaagent.instrumentation.hypertrace.netty.v4_1.server.HttpServerRequestTracingHandler; import io.opentelemetry.javaagent.instrumentation.hypertrace.netty.v4_1.server.HttpServerResponseTracingHandler; -import io.opentelemetry.javaagent.instrumentation.hypertrace.netty.v4_1.server.HttpServerTracingHandler; import io.opentelemetry.javaagent.instrumentation.netty.v4_1.NettyClientSingletons; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; @@ -100,26 +99,16 @@ public static void addHandler( try { // Server pipeline handlers if (handler instanceof HttpServerCodec) { - // replace OTEL response handler because it closes request span before body (especially - // chunked) is captured - pipeline.replace( - io.opentelemetry.instrumentation.netty.v4_1.internal.server.HttpServerTracingHandler - .class - .getName(), - HttpServerTracingHandler.class.getName(), - new HttpServerTracingHandler()); - - pipeline.addBefore( - HttpServerTracingHandler.class.getName(), - io.opentelemetry.instrumentation.netty.v4_1.internal.server - .HttpServerRequestTracingHandler.class - .getName(), - new io.opentelemetry.instrumentation.netty.v4_1.internal.server - .HttpServerRequestTracingHandler(NettyClientSingletons.instrumenter())); + pipeline.addLast( + HttpServerRequestTracingHandler.class.getName(), + new HttpServerRequestTracingHandler()); pipeline.addLast( HttpServerBlockingRequestHandler.class.getName(), new HttpServerBlockingRequestHandler()); + pipeline.addLast( + HttpServerResponseTracingHandler.class.getName(), + new HttpServerResponseTracingHandler()); } else if (handler instanceof HttpRequestDecoder) { pipeline.addLast( HttpServerRequestTracingHandler.class.getName(), diff --git a/instrumentation/netty/netty-4.1/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_1/client/HttpClientResponseTracingHandler.java b/instrumentation/netty/netty-4.1/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_1/client/HttpClientResponseTracingHandler.java index c056b47bf..6f31be4ca 100644 --- a/instrumentation/netty/netty-4.1/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_1/client/HttpClientResponseTracingHandler.java +++ b/instrumentation/netty/netty-4.1/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_1/client/HttpClientResponseTracingHandler.java @@ -32,7 +32,7 @@ import io.opentelemetry.javaagent.instrumentation.hypertrace.netty.v4_1.AttributeKeys; import io.opentelemetry.javaagent.instrumentation.hypertrace.netty.v4_1.DataCaptureUtils; import io.opentelemetry.javaagent.instrumentation.netty.v4_1.NettyClientSingletons; -import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import io.opentelemetry.semconv.SemanticAttributes; import java.nio.charset.Charset; import java.util.Map; import org.hypertrace.agent.core.config.InstrumentationConfig; @@ -100,7 +100,7 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) { if (msg instanceof HttpResponse) { HttpResponse httpResponse = (HttpResponse) msg; int code = httpResponse.status().code(); - span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, code); + span.setAttribute(SemanticAttributes.HTTP_RESPONSE_STATUS_CODE, code); span.setStatus(code >= 100 && code < 400 ? StatusCode.UNSET : StatusCode.ERROR); } if (msg instanceof LastHttpContent) { diff --git a/instrumentation/netty/netty-4.1/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_1/server/HttpServerBlockingRequestHandler.java b/instrumentation/netty/netty-4.1/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_1/server/HttpServerBlockingRequestHandler.java index c4bb452a2..33a1351c7 100644 --- a/instrumentation/netty/netty-4.1/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_1/server/HttpServerBlockingRequestHandler.java +++ b/instrumentation/netty/netty-4.1/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_1/server/HttpServerBlockingRequestHandler.java @@ -28,10 +28,10 @@ import io.netty.util.Attribute; import io.netty.util.ReferenceCountUtil; import io.opentelemetry.api.trace.Span; -import io.opentelemetry.context.Context; -import io.opentelemetry.instrumentation.netty.v4.common.HttpRequestAndChannel; +import io.opentelemetry.instrumentation.netty.v4_1.internal.ServerContext; import io.opentelemetry.javaagent.instrumentation.hypertrace.netty.v4_1.AttributeKeys; import java.nio.charset.StandardCharsets; +import java.util.Deque; import java.util.Map; import org.hypertrace.agent.core.filter.FilterResult; import org.hypertrace.agent.filter.FilterRegistry; @@ -41,15 +41,15 @@ public class HttpServerBlockingRequestHandler extends ChannelInboundHandlerAdapt @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { Channel channel = ctx.channel(); - Context context = + Deque serverContexts = channel .attr(io.opentelemetry.instrumentation.netty.v4_1.internal.AttributeKeys.SERVER_CONTEXT) .get(); - if (context == null) { + if (serverContexts == null || serverContexts.isEmpty()) { ctx.fireChannelRead(msg); return; } - Span span = Span.fromContext(context); + Span span = Span.fromContext(serverContexts.element().context()); if (msg instanceof HttpRequest) { Attribute> headersAttr = channel.attr(AttributeKeys.REQUEST_HEADERS); @@ -66,8 +66,7 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) { if (msg instanceof HttpContent) { FilterResult filterResult = FilterRegistry.getFilter().evaluateRequestBody(span, null, null); if (filterResult.shouldBlock()) { - Attribute requestAttr = channel.attr(AttributeKeys.REQUEST); - HttpRequest req = ((HttpRequestAndChannel) (requestAttr.get())).request(); + HttpRequest req = serverContexts.element().request().request(); forbidden(ctx, req, filterResult); return; } diff --git a/instrumentation/netty/netty-4.1/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_1/server/HttpServerRequestTracingHandler.java b/instrumentation/netty/netty-4.1/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_1/server/HttpServerRequestTracingHandler.java index 30ba443c0..9cc9c7c1d 100644 --- a/instrumentation/netty/netty-4.1/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_1/server/HttpServerRequestTracingHandler.java +++ b/instrumentation/netty/netty-4.1/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_1/server/HttpServerRequestTracingHandler.java @@ -26,10 +26,11 @@ import io.netty.util.Attribute; import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.trace.Span; -import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.netty.v4_1.internal.ServerContext; import io.opentelemetry.javaagent.instrumentation.hypertrace.netty.v4_1.AttributeKeys; import io.opentelemetry.javaagent.instrumentation.hypertrace.netty.v4_1.DataCaptureUtils; import java.nio.charset.Charset; +import java.util.Deque; import java.util.HashMap; import java.util.Map; import org.hypertrace.agent.core.config.InstrumentationConfig; @@ -48,15 +49,15 @@ public class HttpServerRequestTracingHandler extends ChannelInboundHandlerAdapte @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { Channel channel = ctx.channel(); - Context context = + Deque serverContexts = channel .attr(io.opentelemetry.instrumentation.netty.v4_1.internal.AttributeKeys.SERVER_CONTEXT) .get(); - if (context == null) { + if (serverContexts == null || serverContexts.isEmpty()) { ctx.fireChannelRead(msg); return; } - Span span = Span.fromContext(context); + Span span = Span.fromContext(serverContexts.element().context()); if (msg instanceof HttpRequest) { HttpRequest httpRequest = (HttpRequest) msg; diff --git a/instrumentation/netty/netty-4.1/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_1/server/HttpServerResponseTracingHandler.java b/instrumentation/netty/netty-4.1/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_1/server/HttpServerResponseTracingHandler.java index f2831aa8f..c68dacf89 100644 --- a/instrumentation/netty/netty-4.1/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_1/server/HttpServerResponseTracingHandler.java +++ b/instrumentation/netty/netty-4.1/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_1/server/HttpServerResponseTracingHandler.java @@ -27,13 +27,14 @@ import io.netty.util.Attribute; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.StatusCode; -import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; +import io.opentelemetry.instrumentation.netty.v4_1.internal.ServerContext; import io.opentelemetry.javaagent.instrumentation.hypertrace.netty.v4_1.AttributeKeys; import io.opentelemetry.javaagent.instrumentation.hypertrace.netty.v4_1.DataCaptureUtils; import io.opentelemetry.javaagent.instrumentation.netty.v4_1.NettyServerSingletons; -import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import io.opentelemetry.semconv.SemanticAttributes; import java.nio.charset.Charset; +import java.util.Deque; import java.util.Map; import org.hypertrace.agent.core.config.InstrumentationConfig; import org.hypertrace.agent.core.instrumentation.HypertraceSemanticAttributes; @@ -50,15 +51,15 @@ public class HttpServerResponseTracingHandler extends ChannelOutboundHandlerAdap @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise prm) { - Context context = + Deque serverContexts = ctx.channel() .attr(io.opentelemetry.instrumentation.netty.v4_1.internal.AttributeKeys.SERVER_CONTEXT) .get(); - if (context == null) { + if (serverContexts == null || serverContexts.isEmpty()) { ctx.write(msg, prm); return; } - Span span = Span.fromContext(context); + Span span = Span.fromContext(serverContexts.element().context()); if (msg instanceof HttpResponse) { HttpResponse httpResponse = (HttpResponse) msg; @@ -90,16 +91,21 @@ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise prm) { DataCaptureUtils.captureBody(span, ctx.channel(), AttributeKeys.RESPONSE_BODY_BUFFER, msg); } - try (Scope ignored = context.makeCurrent()) { + try (Scope ignored = serverContexts.element().context().makeCurrent()) { ctx.write(msg, prm); } catch (Throwable throwable) { - NettyServerSingletons.instrumenter().end(context, null, null, throwable); + NettyServerSingletons.instrumenter() + .end( + serverContexts.element().context(), + serverContexts.element().request(), + null, + throwable); throw throwable; } if (msg instanceof HttpResponse) { HttpResponse httpResponse = (HttpResponse) msg; int code = httpResponse.status().code(); - span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, code); + span.setAttribute(SemanticAttributes.HTTP_RESPONSE_STATUS_CODE, code); span.setStatus(code >= 100 && code < 500 ? StatusCode.UNSET : StatusCode.ERROR); } if (msg instanceof LastHttpContent) { diff --git a/instrumentation/netty/netty-4.1/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_1/server/AbstractNetty41ServerInstrumentationTest.java b/instrumentation/netty/netty-4.1/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_1/server/AbstractNetty41ServerInstrumentationTest.java index 5ed840db0..4a312157d 100644 --- a/instrumentation/netty/netty-4.1/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_1/server/AbstractNetty41ServerInstrumentationTest.java +++ b/instrumentation/netty/netty-4.1/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_1/server/AbstractNetty41ServerInstrumentationTest.java @@ -20,7 +20,7 @@ import static io.opentelemetry.javaagent.instrumentation.hypertrace.netty.v4_1.server.NettyTestServer.RESPONSE_HEADER_NAME; import static io.opentelemetry.javaagent.instrumentation.hypertrace.netty.v4_1.server.NettyTestServer.RESPONSE_HEADER_VALUE; -import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.proto.trace.v1.Span; import java.io.IOException; import java.util.List; import java.util.concurrent.ExecutionException; @@ -29,13 +29,14 @@ import okhttp3.RequestBody; import okhttp3.Response; import okio.Buffer; -import org.hypertrace.agent.core.instrumentation.HypertraceSemanticAttributes; import org.hypertrace.agent.testing.AbstractInstrumenterTest; -import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +@TestInstance(TestInstance.Lifecycle.PER_CLASS) public abstract class AbstractNetty41ServerInstrumentationTest extends AbstractInstrumenterTest { public static final String REQUEST_HEADER_NAME = "reqheader"; @@ -44,13 +45,13 @@ public abstract class AbstractNetty41ServerInstrumentationTest extends AbstractI private static int port; private static NettyTestServer nettyTestServer; - @BeforeEach + @BeforeAll private void startServer() throws IOException, InterruptedException { nettyTestServer = createNetty(); port = nettyTestServer.create(); } - @AfterEach + @AfterAll private void stopServer() throws ExecutionException, InterruptedException { nettyTestServer.stopServer(); } @@ -70,27 +71,28 @@ public void get() throws IOException, TimeoutException, InterruptedException { Assertions.assertEquals(204, response.code()); } - List> traces = TEST_WRITER.getTraces(); TEST_WRITER.waitForTraces(1); + List> traces = + TEST_WRITER.waitForSpans(1, span -> span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT)); Assertions.assertEquals(1, traces.size()); - List trace = traces.get(0); + List trace = traces.get(0); Assertions.assertEquals(1, trace.size()); - SpanData spanData = trace.get(0); + Span span = trace.get(0); Assertions.assertEquals( REQUEST_HEADER_VALUE, - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpRequestHeader(REQUEST_HEADER_NAME))); + TEST_WRITER + .getAttributesMap(span) + .get("http.request.header." + REQUEST_HEADER_NAME) + .getStringValue()); Assertions.assertEquals( RESPONSE_HEADER_VALUE, - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpResponseHeader(RESPONSE_HEADER_NAME))); - Assertions.assertNull( - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); - Assertions.assertNull( - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER + .getAttributesMap(span) + .get("http.response.header." + RESPONSE_HEADER_NAME) + .getStringValue()); + Assertions.assertNull(TEST_WRITER.getAttributesMap(span).get("http.request.body")); + Assertions.assertNull(TEST_WRITER.getAttributesMap(span).get("http.response.body")); } @Test @@ -109,31 +111,34 @@ public void postJson() throws IOException, TimeoutException, InterruptedExceptio Assertions.assertEquals(RESPONSE_BODY, response.body().string()); } - List> traces = TEST_WRITER.getTraces(); TEST_WRITER.waitForTraces(1); + List> traces = + TEST_WRITER.waitForSpans(1, span -> span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT)); Assertions.assertEquals(1, traces.size()); - List trace = traces.get(0); + List trace = traces.get(0); Assertions.assertEquals(1, trace.size()); - SpanData spanData = trace.get(0); + Span span = trace.get(0); Assertions.assertEquals( REQUEST_HEADER_VALUE, - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpRequestHeader(REQUEST_HEADER_NAME))); + TEST_WRITER + .getAttributesMap(span) + .get("http.request.header." + REQUEST_HEADER_NAME) + .getStringValue()); Assertions.assertEquals( RESPONSE_HEADER_VALUE, - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpResponseHeader(RESPONSE_HEADER_NAME))); + TEST_WRITER + .getAttributesMap(span) + .get("http.response.header." + RESPONSE_HEADER_NAME) + .getStringValue()); Buffer requestBodyBuffer = new Buffer(); requestBody.writeTo(requestBodyBuffer); Assertions.assertEquals( new String(requestBodyBuffer.readByteArray()), - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); + TEST_WRITER.getAttributesMap(span).get("http.request.body").getStringValue()); Assertions.assertEquals( RESPONSE_BODY, - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER.getAttributesMap(span).get("http.response.body").getStringValue()); } @Test @@ -151,24 +156,23 @@ public void blocking() throws IOException, TimeoutException, InterruptedExceptio Assertions.assertEquals("Hypertrace Blocked Request", response.body().string()); } - List> traces = TEST_WRITER.getTraces(); TEST_WRITER.waitForTraces(1); + List> traces = + TEST_WRITER.waitForSpans(1, span -> span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT)); Assertions.assertEquals(1, traces.size()); - List trace = traces.get(0); + List trace = traces.get(0); Assertions.assertEquals(1, trace.size()); - SpanData spanData = trace.get(0); + Span span = trace.get(0); Assertions.assertEquals( REQUEST_HEADER_VALUE, - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpRequestHeader(REQUEST_HEADER_NAME))); + TEST_WRITER + .getAttributesMap(span) + .get("http.request.header." + REQUEST_HEADER_NAME) + .getStringValue()); Assertions.assertNull( - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpResponseHeader(RESPONSE_HEADER_NAME))); - Assertions.assertNull( - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER.getAttributesMap(span).get("http.response.header." + RESPONSE_HEADER_NAME)); + Assertions.assertNull(TEST_WRITER.getAttributesMap(span).get("http.response.body")); RequestBody requestBody = blockedRequestBody(true, 3000, 75); Request request2 = @@ -183,24 +187,24 @@ public void blocking() throws IOException, TimeoutException, InterruptedExceptio Assertions.assertEquals("Hypertrace Blocked Request", response.body().string()); } - List> traces2 = TEST_WRITER.getTraces(); TEST_WRITER.waitForTraces(2); + List> traces2 = + TEST_WRITER.waitForSpans( + 2, span1 -> span1.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT)); Assertions.assertEquals(2, traces2.size()); - List trace2 = traces2.get(1); + List trace2 = traces2.get(1); Assertions.assertEquals(1, trace2.size()); - SpanData spanData2 = trace2.get(0); + Span span2 = trace2.get(0); Assertions.assertEquals( REQUEST_HEADER_VALUE, - spanData2 - .getAttributes() - .get(HypertraceSemanticAttributes.httpRequestHeader(REQUEST_HEADER_NAME))); - Assertions.assertNull( - spanData2 - .getAttributes() - .get(HypertraceSemanticAttributes.httpResponseHeader(RESPONSE_HEADER_NAME))); + TEST_WRITER + .getAttributesMap(span2) + .get("http.request.header." + REQUEST_HEADER_NAME) + .getStringValue()); Assertions.assertNull( - spanData2.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER.getAttributesMap(span2).get("http.response.header." + RESPONSE_HEADER_NAME)); + Assertions.assertNull(TEST_WRITER.getAttributesMap(span2).get("http.response.body")); } @Test @@ -219,34 +223,36 @@ public void connectionKeepAlive() throws IOException, TimeoutException, Interrup Assertions.assertEquals(RESPONSE_BODY, response.body().string()); } - List> traces = TEST_WRITER.getTraces(); TEST_WRITER.waitForTraces(1); + List> traces = + TEST_WRITER.waitForSpans(1, span -> span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT)); Assertions.assertEquals(1, traces.size()); - List trace = traces.get(0); + List trace = traces.get(0); Assertions.assertEquals(1, trace.size()); - SpanData spanData = trace.get(0); + Span span = trace.get(0); Assertions.assertEquals( REQUEST_HEADER_VALUE, - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpRequestHeader(REQUEST_HEADER_NAME))); + TEST_WRITER + .getAttributesMap(span) + .get("http.request.header." + REQUEST_HEADER_NAME) + .getStringValue()); Assertions.assertEquals( "1st", - spanData.getAttributes().get(HypertraceSemanticAttributes.httpRequestHeader("first"))); + TEST_WRITER.getAttributesMap(span).get("http.request.header.first").getStringValue()); Assertions.assertEquals( "keep-alive", - spanData.getAttributes().get(HypertraceSemanticAttributes.httpRequestHeader("connection"))); + TEST_WRITER.getAttributesMap(span).get("http.request.header.connection").getStringValue()); Assertions.assertEquals( RESPONSE_HEADER_VALUE, - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpResponseHeader(RESPONSE_HEADER_NAME))); - Assertions.assertNull( - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); + TEST_WRITER + .getAttributesMap(span) + .get("http.response.header." + RESPONSE_HEADER_NAME) + .getStringValue()); + Assertions.assertNull(TEST_WRITER.getAttributesMap(span).get("http.request.body")); Assertions.assertEquals( RESPONSE_BODY, - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER.getAttributesMap(span).get("http.response.body").getStringValue()); RequestBody requestBody = blockedRequestBody(true, 3000, 75); Request request2 = @@ -263,35 +269,30 @@ public void connectionKeepAlive() throws IOException, TimeoutException, Interrup Assertions.assertEquals("Hypertrace Blocked Request", response.body().string()); } - List> traces2 = TEST_WRITER.getTraces(); TEST_WRITER.waitForTraces(2); + List> traces2 = + TEST_WRITER.waitForSpans( + 2, span1 -> span1.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT)); Assertions.assertEquals(2, traces2.size()); - List trace2 = traces2.get(1); + List trace2 = traces2.get(1); Assertions.assertEquals(1, trace2.size()); - SpanData spanData2 = trace2.get(0); + Span span2 = trace2.get(0); Assertions.assertEquals( "REQUEST_HEADER_VALUE", - spanData2 - .getAttributes() - .get(HypertraceSemanticAttributes.httpRequestHeader(REQUEST_HEADER_NAME))); + TEST_WRITER + .getAttributesMap(span2) + .get("http.request.header." + REQUEST_HEADER_NAME) + .getStringValue()); Assertions.assertEquals( "2nd", - spanData2.getAttributes().get(HypertraceSemanticAttributes.httpRequestHeader("second"))); + TEST_WRITER.getAttributesMap(span2).get("http.request.header.second").getStringValue()); Assertions.assertEquals( "keep-alive", - spanData2 - .getAttributes() - .get(HypertraceSemanticAttributes.httpRequestHeader("connection"))); - Assertions.assertNull( - spanData2.getAttributes().get(HypertraceSemanticAttributes.httpRequestHeader("first"))); - Assertions.assertNull( - spanData2 - .getAttributes() - .get(HypertraceSemanticAttributes.httpResponseHeader(RESPONSE_HEADER_NAME))); + TEST_WRITER.getAttributesMap(span2).get("http.request.header.connection").getStringValue()); + Assertions.assertNull(TEST_WRITER.getAttributesMap(span2).get("http.request.header.first")); Assertions.assertNull( - spanData2 - .getAttributes() - .get(HypertraceSemanticAttributes.httpResponseHeader(RESPONSE_BODY))); + TEST_WRITER.getAttributesMap(span2).get("http.response.header." + RESPONSE_HEADER_NAME)); + Assertions.assertNull(TEST_WRITER.getAttributesMap(span2).get("http.response.body")); } } diff --git a/instrumentation/netty/netty-4.1/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_1/server/Netty41HttpServerCodecInstrumentationTest.java b/instrumentation/netty/netty-4.1/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_1/server/Netty41HttpServerCodecInstrumentationTest.java index fd4b9644d..4636fdac7 100644 --- a/instrumentation/netty/netty-4.1/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_1/server/Netty41HttpServerCodecInstrumentationTest.java +++ b/instrumentation/netty/netty-4.1/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_1/server/Netty41HttpServerCodecInstrumentationTest.java @@ -17,13 +17,13 @@ package io.opentelemetry.javaagent.instrumentation.hypertrace.netty.v4_1.server; import io.netty.handler.codec.http.HttpServerCodec; -import java.util.Arrays; +import java.util.Collections; public class Netty41HttpServerCodecInstrumentationTest extends AbstractNetty41ServerInstrumentationTest { @Override protected NettyTestServer createNetty() { - return new NettyTestServer(Arrays.asList(HttpServerCodec.class)); + return new NettyTestServer(Collections.singletonList(HttpServerCodec.class)); } } diff --git a/instrumentation/okhttp/okhttp-3.0/build.gradle.kts b/instrumentation/okhttp/okhttp-3.0/build.gradle.kts index e91e2aa23..3c6371dc5 100644 --- a/instrumentation/okhttp/okhttp-3.0/build.gradle.kts +++ b/instrumentation/okhttp/okhttp-3.0/build.gradle.kts @@ -27,6 +27,5 @@ val versions: Map by extra dependencies { compileOnly("com.squareup.okhttp3:okhttp:3.0.0") - testImplementation("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-okhttp-3.0:${versions["opentelemetry_java_agent"]}") - testImplementation(testFixtures(project(":testing-common"))) + testImplementation(project(":testing-common")) } diff --git a/instrumentation/okhttp/okhttp-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/okhttp/v3_0/OkHttp3BodyInstrumentationModule.java b/instrumentation/okhttp/okhttp-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/okhttp/v3_0/OkHttp3BodyInstrumentationModule.java index 51e1ddb81..e445d4b4d 100644 --- a/instrumentation/okhttp/okhttp-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/okhttp/v3_0/OkHttp3BodyInstrumentationModule.java +++ b/instrumentation/okhttp/okhttp-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/okhttp/v3_0/OkHttp3BodyInstrumentationModule.java @@ -72,7 +72,7 @@ public static void addTracingInterceptor(@Advice.Argument(0) OkHttpClient.Builde } } OkHttpTracingInterceptor interceptor = new OkHttpTracingInterceptor(); - builder.addInterceptor(interceptor); + builder.addNetworkInterceptor(interceptor); } } } diff --git a/instrumentation/servlet/servlet-3.0/build.gradle.kts b/instrumentation/servlet/servlet-3.0/build.gradle.kts index 31b407f11..57c1805e3 100644 --- a/instrumentation/servlet/servlet-3.0/build.gradle.kts +++ b/instrumentation/servlet/servlet-3.0/build.gradle.kts @@ -33,19 +33,13 @@ val versions: Map by extra dependencies { implementation("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-servlet-common:${versions["opentelemetry_java_agent"]}") implementation("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-servlet-3.0:${versions["opentelemetry_java_agent"]}") // Servlet3Accessor - testImplementation("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-servlet-3.0:${versions["opentelemetry_java_agent"]}") - testImplementation("io.opentelemetry.javaagent:opentelemetry-muzzle:${versions["opentelemetry_java_agent"]}") compileOnly("io.opentelemetry.javaagent:opentelemetry-javaagent-bootstrap:${versions["opentelemetry_java_agent"]}") compileOnly("javax.servlet:javax.servlet-api:3.1.0") - testRuntimeOnly("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-servlet-common-bootstrap:${versions["opentelemetry_java_agent"]}") muzzleBootstrap("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-servlet-common-bootstrap:${versions["opentelemetry_java_agent"]}") - testImplementation(project(":instrumentation:servlet:servlet-rw")) - testImplementation(files(project(":instrumentation:servlet:servlet-rw").dependencyProject.sourceSets.main.map { it.output })) - testImplementation(testFixtures(project(":testing-common")) as ProjectDependency) { + testImplementation(project(":testing-common") as ProjectDependency) { exclude(group = "org.eclipse.jetty", module = "jetty-server") } testImplementation("org.eclipse.jetty:jetty-server:8.1.22.v20160922") testImplementation("org.eclipse.jetty:jetty-servlet:8.1.22.v20160922") - testImplementation("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-semconv:${versions["opentelemetry_semconv"]}") } diff --git a/instrumentation/servlet/servlet-3.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/Servlet30InstrumentationTest.java b/instrumentation/servlet/servlet-3.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/Servlet30InstrumentationTest.java index 848c390fe..0a50a4956 100644 --- a/instrumentation/servlet/servlet-3.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/Servlet30InstrumentationTest.java +++ b/instrumentation/servlet/servlet-3.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/Servlet30InstrumentationTest.java @@ -26,11 +26,17 @@ import io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.v3_0.nowrapping.TestServlets.EchoStream_single_byte; import io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.v3_0.nowrapping.TestServlets.EchoWriter_single_char; import io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.v3_0.nowrapping.TestServlets.GetHello; -import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.proto.trace.v1.Span; import java.io.IOException; import java.util.EnumSet; import java.util.List; -import javax.servlet.*; +import javax.servlet.DispatcherType; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; import okhttp3.FormBody; import okhttp3.MediaType; import okhttp3.Request; @@ -39,8 +45,6 @@ import org.WrappingFilter; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletContextHandler; -import org.hypertrace.agent.core.instrumentation.HypertraceEvaluationException; -import org.hypertrace.agent.core.instrumentation.HypertraceSemanticAttributes; import org.hypertrace.agent.testing.AbstractInstrumenterTest; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; @@ -66,7 +70,7 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha try { chain.doFilter(request, response); } catch (Throwable t) { - if (t instanceof HypertraceEvaluationException) { + if (t.getClass().getName().contains("HypertraceEvaluationException")) { throw new RuntimeException("wrapped exception", t); } throw t; @@ -211,27 +215,30 @@ public void portUrlEncoded() throws Exception { } TEST_WRITER.waitForTraces(1); - List> traces = TEST_WRITER.getTraces(); + List> traces = + TEST_WRITER.waitForSpans(1, span -> span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT)); Assertions.assertEquals(1, traces.size()); - List spans = traces.get(0); + List spans = traces.get(0); Assertions.assertEquals(1, spans.size()); - SpanData spanData = spans.get(0); + Span span = spans.get(0); Assertions.assertEquals( REQUEST_HEADER_VALUE, - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpRequestHeader(REQUEST_HEADER))); + TEST_WRITER + .getAttributesMap(span) + .get("http.request.header." + REQUEST_HEADER) + .getStringValue()); Assertions.assertEquals( TestServlets.RESPONSE_HEADER_VALUE, - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpResponseHeader(TestServlets.RESPONSE_HEADER))); + TEST_WRITER + .getAttributesMap(span) + .get("http.response.header." + TestServlets.RESPONSE_HEADER) + .getStringValue()); Assertions.assertEquals( "key1=value1&key2=value2", - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); + TEST_WRITER.getAttributesMap(span).get("http.request.body").getStringValue()); Assertions.assertEquals( TestServlets.RESPONSE_BODY, - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER.getAttributesMap(span).get("http.response.body").getStringValue()); } @Test @@ -247,25 +254,26 @@ public void getHello() throws Exception { } TEST_WRITER.waitForTraces(1); - List> traces = TEST_WRITER.getTraces(); + List> traces = + TEST_WRITER.waitForSpans(1, span -> span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT)); Assertions.assertEquals(1, traces.size()); - List spans = traces.get(0); + List spans = traces.get(0); Assertions.assertEquals(1, spans.size()); - SpanData spanData = spans.get(0); + Span span = spans.get(0); Assertions.assertEquals( REQUEST_HEADER_VALUE, - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpRequestHeader(REQUEST_HEADER))); + TEST_WRITER + .getAttributesMap(span) + .get("http.request.header." + REQUEST_HEADER) + .getStringValue()); Assertions.assertEquals( TestServlets.RESPONSE_HEADER_VALUE, - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpResponseHeader(TestServlets.RESPONSE_HEADER))); - Assertions.assertNull( - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); - Assertions.assertNull( - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER + .getAttributesMap(span) + .get("http.response.header." + TestServlets.RESPONSE_HEADER) + .getStringValue()); + Assertions.assertNull(TEST_WRITER.getAttributesMap(span).get("http.request.body")); + Assertions.assertNull(TEST_WRITER.getAttributesMap(span).get("http.response.body")); } @Test @@ -282,19 +290,18 @@ public void block() throws Exception { } TEST_WRITER.waitForTraces(1); - List> traces = TEST_WRITER.getTraces(); + List> traces = + TEST_WRITER.waitForSpans(1, span -> span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT)); Assertions.assertEquals(1, traces.size()); - List spans = traces.get(0); + List spans = traces.get(0); Assertions.assertEquals(1, spans.size()); - SpanData spanData = spans.get(0); - Assertions.assertNull( - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpResponseHeader(TestServlets.RESPONSE_HEADER))); + Span span = spans.get(0); Assertions.assertNull( - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); - Assertions.assertNull( - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER + .getAttributesMap(span) + .get("http.response.header." + TestServlets.RESPONSE_HEADER)); + Assertions.assertNull(TEST_WRITER.getAttributesMap(span).get("http.request.body")); + Assertions.assertNull(TEST_WRITER.getAttributesMap(span).get("http.response.body")); } @Test @@ -312,19 +319,19 @@ public void blockBody() throws Exception { } TEST_WRITER.waitForTraces(1); - List> traces = TEST_WRITER.getTraces(); + List> traces = + TEST_WRITER.waitForSpans(1, span -> span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT)); Assertions.assertEquals(1, traces.size()); - List spans = traces.get(0); + List spans = traces.get(0); Assertions.assertEquals(1, spans.size()); - SpanData spanData = spans.get(0); + Span span = spans.get(0); Assertions.assertNull( - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpResponseHeader(TestServlets.RESPONSE_HEADER))); + TEST_WRITER + .getAttributesMap(span) + .get("http.response.header." + TestServlets.RESPONSE_HEADER)); Assertions.assertEquals( - "block=true", spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); - Assertions.assertNull( - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + "block=true", TEST_WRITER.getAttributesMap(span).get("http.request.body").getStringValue()); + Assertions.assertNull(TEST_WRITER.getAttributesMap(span).get("http.response.body")); } @Test @@ -342,19 +349,19 @@ public void blockBodyWrappedException() throws Exception { } TEST_WRITER.waitForTraces(1); - List> traces = TEST_WRITER.getTraces(); + List> traces = + TEST_WRITER.waitForSpans(1, span -> span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT)); Assertions.assertEquals(1, traces.size()); - List spans = traces.get(0); + List spans = traces.get(0); Assertions.assertEquals(1, spans.size()); - SpanData spanData = spans.get(0); + Span span = spans.get(0); Assertions.assertNull( - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpResponseHeader(TestServlets.RESPONSE_HEADER))); + TEST_WRITER + .getAttributesMap(span) + .get("http.response.header." + TestServlets.RESPONSE_HEADER)); Assertions.assertEquals( - "block=true", spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); - Assertions.assertNull( - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + "block=true", TEST_WRITER.getAttributesMap(span).get("http.request.body").getStringValue()); + Assertions.assertNull(TEST_WRITER.getAttributesMap(span).get("http.response.body")); } public void postJson(String url) throws Exception { @@ -370,25 +377,28 @@ public void postJson(String url) throws Exception { } TEST_WRITER.waitForTraces(1); - List> traces = TEST_WRITER.getTraces(); + List> traces = + TEST_WRITER.waitForSpans(1, span -> span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT)); Assertions.assertEquals(1, traces.size()); - List spans = traces.get(0); + List spans = traces.get(0); Assertions.assertEquals(1, spans.size()); - SpanData spanData = spans.get(0); + Span span = spans.get(0); Assertions.assertEquals( REQUEST_HEADER_VALUE, - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpRequestHeader(REQUEST_HEADER))); + TEST_WRITER + .getAttributesMap(span) + .get("http.request.header." + REQUEST_HEADER) + .getStringValue()); Assertions.assertEquals( TestServlets.RESPONSE_HEADER_VALUE, - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpResponseHeader(TestServlets.RESPONSE_HEADER))); + TEST_WRITER + .getAttributesMap(span) + .get("http.response.header." + TestServlets.RESPONSE_HEADER) + .getStringValue()); Assertions.assertEquals( - REQUEST_BODY, spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); + REQUEST_BODY, TEST_WRITER.getAttributesMap(span).get("http.request.body").getStringValue()); Assertions.assertEquals( TestServlets.RESPONSE_BODY, - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER.getAttributesMap(span).get("http.response.body").getStringValue()); } } diff --git a/instrumentation/servlet/servlet-3.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/request/ServletInputStreamContextAccessInstrumentationModule.java b/instrumentation/servlet/servlet-3.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/request/ServletInputStreamContextAccessInstrumentationModule.java deleted file mode 100644 index 8caab5cc4..000000000 --- a/instrumentation/servlet/servlet-3.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/request/ServletInputStreamContextAccessInstrumentationModule.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright The Hypertrace Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.v3_0.nowrapping.request; - -import static net.bytebuddy.matcher.ElementMatchers.isPublic; -import static net.bytebuddy.matcher.ElementMatchers.named; -import static net.bytebuddy.matcher.ElementMatchers.takesArguments; - -import io.opentelemetry.instrumentation.api.util.VirtualField; -import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; -import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; -import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; -import io.opentelemetry.javaagent.tooling.muzzle.InstrumentationModuleMuzzle; -import io.opentelemetry.javaagent.tooling.muzzle.VirtualFieldMappingsBuilder; -import io.opentelemetry.javaagent.tooling.muzzle.references.ClassRef; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import javax.servlet.ServletInputStream; -import net.bytebuddy.asm.Advice; -import net.bytebuddy.description.type.TypeDescription; -import net.bytebuddy.matcher.ElementMatcher; -import org.hypertrace.agent.core.instrumentation.buffer.ByteBufferSpanPair; - -// SPI explicitly added in META-INF/services/... -public class ServletInputStreamContextAccessInstrumentationModule extends InstrumentationModule - implements InstrumentationModuleMuzzle { - - public ServletInputStreamContextAccessInstrumentationModule() { - super("test-servlet-input-stream", "ht"); - } - - @Override - public List typeInstrumentations() { - return Collections.singletonList(new InputStreamTriggerInstrumentation()); - } - - @Override - public Map getMuzzleReferences() { - return Collections.emptyMap(); - } - - @Override - public void registerMuzzleVirtualFields(VirtualFieldMappingsBuilder builder) { - builder.register("javax.servlet.ServletInputStream", ByteBufferSpanPair.class.getName()); - } - - @Override - public List getMuzzleHelperClassNames() { - return Collections.emptyList(); - } - - static final class InputStreamTriggerInstrumentation implements TypeInstrumentation { - - @Override - public ElementMatcher typeMatcher() { - return named("org.ServletStreamContextAccess"); - } - - @Override - public void transform(TypeTransformer transformer) { - transformer.applyAdviceToMethod( - named("addToInputStreamContext").and(takesArguments(2)).and(isPublic()), - ServletInputStreamContextAccessInstrumentationModule.class.getName() + "$TestAdvice"); - } - } - - static class TestAdvice { - - @Advice.OnMethodEnter(suppress = Throwable.class) - public static void enter( - @Advice.Argument(0) ServletInputStream servletInputStream, - @Advice.Argument(1) ByteBufferSpanPair metadata) { - VirtualField contextStore = - VirtualField.find(ServletInputStream.class, ByteBufferSpanPair.class); - contextStore.set(servletInputStream, metadata); - } - } -} diff --git a/instrumentation/servlet/servlet-3.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/request/ServletInputStreamInstrumentationTest.java b/instrumentation/servlet/servlet-3.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/request/ServletInputStreamInstrumentationTest.java deleted file mode 100644 index 8b4b4b936..000000000 --- a/instrumentation/servlet/servlet-3.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/request/ServletInputStreamInstrumentationTest.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright The Hypertrace Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.v3_0.nowrapping.request; - -import io.opentelemetry.api.trace.Span; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.Collections; -import java.util.Map; -import javax.servlet.ServletInputStream; -import org.ServletStreamContextAccess; -import org.TestServletInputStream; -import org.hypertrace.agent.core.TriFunction; -import org.hypertrace.agent.core.filter.FilterResult; -import org.hypertrace.agent.core.instrumentation.buffer.BoundedBuffersFactory; -import org.hypertrace.agent.core.instrumentation.buffer.BoundedByteArrayOutputStream; -import org.hypertrace.agent.core.instrumentation.buffer.ByteBufferSpanPair; -import org.hypertrace.agent.testing.AbstractInstrumenterTest; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -public class ServletInputStreamInstrumentationTest extends AbstractInstrumenterTest { - - private static final String TEST_SPAN_NAME = "foo"; - private static final String BODY = "boobar"; - - @Test - public void read() throws IOException { - Span span = TEST_TRACER.spanBuilder(TEST_SPAN_NAME).startSpan(); - - ServletInputStream servletInputStream = - new TestServletInputStream(new ByteArrayInputStream(BODY.getBytes())); - - BoundedByteArrayOutputStream buffer = - BoundedBuffersFactory.createStream(StandardCharsets.UTF_8); - ByteBufferSpanPair bufferSpanPair = - new ByteBufferSpanPair(span, buffer, NOOP_FILTER, Collections.emptyMap()); - ServletStreamContextAccess.addToInputStreamContext(servletInputStream, bufferSpanPair); - - while (servletInputStream.read() != -1) {} - Assertions.assertEquals(BODY, buffer.toStringWithSuppliedCharset()); - } - - @Test - public void read_callDepth_is_cleared() throws IOException { - Span span = TEST_TRACER.spanBuilder(TEST_SPAN_NAME).startSpan(); - - ServletInputStream servletInputStream = - new TestServletInputStream(new ByteArrayInputStream(BODY.getBytes())); - servletInputStream.read(); - - BoundedByteArrayOutputStream buffer = - BoundedBuffersFactory.createStream(StandardCharsets.UTF_8); - ByteBufferSpanPair bufferSpanPair = - new ByteBufferSpanPair(span, buffer, NOOP_FILTER, Collections.emptyMap()); - ServletStreamContextAccess.addToInputStreamContext(servletInputStream, bufferSpanPair); - - while (servletInputStream.read() != -1) {} - Assertions.assertEquals(BODY.substring(1), buffer.toStringWithSuppliedCharset()); - } - - @Test - public void read_call_depth_read_calls_read() throws IOException { - Span span = TEST_TRACER.spanBuilder(TEST_SPAN_NAME).startSpan(); - - ServletInputStream servletInputStream = - new TestServletInputStream(new ByteArrayInputStream(BODY.getBytes())); - servletInputStream.read(new byte[2]); - - BoundedByteArrayOutputStream buffer = - BoundedBuffersFactory.createStream(StandardCharsets.UTF_8); - ByteBufferSpanPair bufferSpanPair = - new ByteBufferSpanPair(span, buffer, NOOP_FILTER, Collections.emptyMap()); - ServletStreamContextAccess.addToInputStreamContext(servletInputStream, bufferSpanPair); - - servletInputStream.read(new byte[BODY.length()]); - Assertions.assertEquals(BODY.substring(2), buffer.toStringWithSuppliedCharset()); - } - - private static final TriFunction, FilterResult> NOOP_FILTER = - (span, body, headers) -> new FilterResult(false, 0, ""); -} diff --git a/instrumentation/servlet/servlet-3.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/response/ServletOutputStreamContextAccessInstrumentationModule.java b/instrumentation/servlet/servlet-3.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/response/ServletOutputStreamContextAccessInstrumentationModule.java deleted file mode 100644 index 0263a10c3..000000000 --- a/instrumentation/servlet/servlet-3.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/response/ServletOutputStreamContextAccessInstrumentationModule.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright The Hypertrace Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.v3_0.nowrapping.response; - -import static net.bytebuddy.matcher.ElementMatchers.isPublic; -import static net.bytebuddy.matcher.ElementMatchers.named; -import static net.bytebuddy.matcher.ElementMatchers.takesArguments; - -import io.opentelemetry.instrumentation.api.util.VirtualField; -import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; -import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; -import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; -import io.opentelemetry.javaagent.tooling.muzzle.InstrumentationModuleMuzzle; -import io.opentelemetry.javaagent.tooling.muzzle.VirtualFieldMappingsBuilder; -import io.opentelemetry.javaagent.tooling.muzzle.references.ClassRef; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import javax.servlet.ServletOutputStream; -import net.bytebuddy.asm.Advice; -import net.bytebuddy.description.type.TypeDescription; -import net.bytebuddy.matcher.ElementMatcher; -import org.hypertrace.agent.core.instrumentation.buffer.BoundedByteArrayOutputStream; - -public class ServletOutputStreamContextAccessInstrumentationModule extends InstrumentationModule - implements InstrumentationModuleMuzzle { - - public ServletOutputStreamContextAccessInstrumentationModule() { - super("test-servlet-output-stream", "ht"); - } - - @Override - public List typeInstrumentations() { - return Collections.singletonList(new OutputStreamTriggerInstrumentation()); - } - - @Override - public Map getMuzzleReferences() { - return Collections.emptyMap(); - } - - @Override - public void registerMuzzleVirtualFields(VirtualFieldMappingsBuilder builder) { - builder.register( - "javax.servlet.ServletOutputStream", BoundedByteArrayOutputStream.class.getName()); - } - - @Override - public List getMuzzleHelperClassNames() { - return Collections.emptyList(); - } - - static final class OutputStreamTriggerInstrumentation implements TypeInstrumentation { - - @Override - public ElementMatcher typeMatcher() { - return named("org.ServletStreamContextAccess"); - } - - @Override - public void transform(TypeTransformer transformer) { - transformer.applyAdviceToMethod( - named("addToOutputStreamContext").and(takesArguments(2)).and(isPublic()), - ServletOutputStreamContextAccessInstrumentationModule.class.getName() + "$TestAdvice"); - } - } - - static class TestAdvice { - @Advice.OnMethodEnter(suppress = Throwable.class) - public static void enter( - @Advice.Argument(0) ServletOutputStream servletOutputStream, - @Advice.Argument(1) BoundedByteArrayOutputStream buffer) { - VirtualField contextStore = - VirtualField.find(ServletOutputStream.class, BoundedByteArrayOutputStream.class); - contextStore.set(servletOutputStream, buffer); - } - } -} diff --git a/instrumentation/servlet/servlet-3.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/response/ServletOutputStreamInstrumentationTest.java b/instrumentation/servlet/servlet-3.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/response/ServletOutputStreamInstrumentationTest.java deleted file mode 100644 index d4b2be599..000000000 --- a/instrumentation/servlet/servlet-3.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/response/ServletOutputStreamInstrumentationTest.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright The Hypertrace Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.v3_0.nowrapping.response; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import javax.servlet.ServletOutputStream; -import org.ServletStreamContextAccess; -import org.TestServletOutputStream; -import org.hypertrace.agent.core.instrumentation.buffer.BoundedBuffersFactory; -import org.hypertrace.agent.core.instrumentation.buffer.BoundedByteArrayOutputStream; -import org.hypertrace.agent.testing.AbstractInstrumenterTest; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -public class ServletOutputStreamInstrumentationTest extends AbstractInstrumenterTest { - - private static final String BODY = "boobar"; - - @Test - public void write_single_byte() throws IOException { - ServletOutputStream servletOutputStream = new TestServletOutputStream(); - BoundedByteArrayOutputStream buffer = - BoundedBuffersFactory.createStream(StandardCharsets.UTF_8); - ServletStreamContextAccess.addToOutputStreamContext(servletOutputStream, buffer); - - byte[] bytes = BODY.getBytes(); - for (int i = 0; i < BODY.length(); i++) { - servletOutputStream.write(bytes[i]); - } - Assertions.assertEquals(BODY, buffer.toStringWithSuppliedCharset()); - } - - @Test - public void write_arr() throws IOException { - ServletOutputStream servletOutputStream = new TestServletOutputStream(); - BoundedByteArrayOutputStream buffer = - BoundedBuffersFactory.createStream(StandardCharsets.UTF_8); - ServletStreamContextAccess.addToOutputStreamContext(servletOutputStream, buffer); - - servletOutputStream.write(BODY.getBytes()); - Assertions.assertEquals(BODY, buffer.toStringWithSuppliedCharset()); - } - - @Test - public void write_arr_offset() throws IOException { - ServletOutputStream servletOutputStream = new TestServletOutputStream(); - BoundedByteArrayOutputStream buffer = - BoundedBuffersFactory.createStream(StandardCharsets.UTF_8); - ServletStreamContextAccess.addToOutputStreamContext(servletOutputStream, buffer); - - byte[] bytes = BODY.getBytes(); - servletOutputStream.write(bytes, 0, 2); - servletOutputStream.write(bytes, 2, bytes.length - 2); - Assertions.assertEquals(BODY, buffer.toStringWithSuppliedCharset()); - } - - @Test - public void print_str() throws IOException { - ServletOutputStream servletOutputStream = new TestServletOutputStream(); - BoundedByteArrayOutputStream buffer = - BoundedBuffersFactory.createStream(StandardCharsets.UTF_8); - ServletStreamContextAccess.addToOutputStreamContext(servletOutputStream, buffer); - - servletOutputStream.print(BODY); - Assertions.assertEquals(BODY, buffer.toStringWithSuppliedCharset()); - } - - @Test - public void println_str() throws IOException { - ServletOutputStream servletOutputStream = new TestServletOutputStream(); - BoundedByteArrayOutputStream buffer = - BoundedBuffersFactory.createStream(StandardCharsets.UTF_8); - ServletStreamContextAccess.addToOutputStreamContext(servletOutputStream, buffer); - - servletOutputStream.println(BODY); - Assertions.assertEquals(BODY + "\r\n", buffer.toStringWithSuppliedCharset()); - } -} diff --git a/instrumentation/servlet/servlet-3.0/src/test/resources/META-INF/services/io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule b/instrumentation/servlet/servlet-3.0/src/test/resources/META-INF/services/io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule deleted file mode 100644 index d764364bb..000000000 --- a/instrumentation/servlet/servlet-3.0/src/test/resources/META-INF/services/io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule +++ /dev/null @@ -1,2 +0,0 @@ -io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.v3_0.nowrapping.request.ServletInputStreamContextAccessInstrumentationModule -io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.v3_0.nowrapping.response.ServletOutputStreamContextAccessInstrumentationModule diff --git a/instrumentation/servlet/servlet-rw/build.gradle.kts b/instrumentation/servlet/servlet-rw/build.gradle.kts index 7aabd1cce..acb447342 100644 --- a/instrumentation/servlet/servlet-rw/build.gradle.kts +++ b/instrumentation/servlet/servlet-rw/build.gradle.kts @@ -23,6 +23,6 @@ afterEvaluate{ val versions: Map by extra dependencies { - testImplementation(testFixtures(project(":testing-common"))) - testImplementation("io.opentelemetry.javaagent:opentelemetry-muzzle:${versions["opentelemetry_java_agent"]}") + testImplementation(project(":testing-common")) + testImplementation("org.eclipse.jetty:jetty-servlet:8.1.22.v20160922") } diff --git a/instrumentation/servlet/servlet-rw/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/rw/reader/BufferedReaderInstrumentation.java b/instrumentation/servlet/servlet-rw/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/rw/reader/BufferedReaderInstrumentation.java index f3dcfafec..121e4feef 100644 --- a/instrumentation/servlet/servlet-rw/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/rw/reader/BufferedReaderInstrumentation.java +++ b/instrumentation/servlet/servlet-rw/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/rw/reader/BufferedReaderInstrumentation.java @@ -32,6 +32,7 @@ import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; import org.hypertrace.agent.core.instrumentation.HypertraceCallDepthThreadLocalMap; +import org.hypertrace.agent.core.instrumentation.HypertraceEvaluationException; import org.hypertrace.agent.core.instrumentation.HypertraceSemanticAttributes; import org.hypertrace.agent.core.instrumentation.buffer.CharBufferSpanPair; @@ -79,23 +80,31 @@ public static CharBufferSpanPair enter(@Advice.This BufferedReader thizz) { return bufferSpanPair; } - @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) + @Advice.OnMethodExit(onThrowable = Throwable.class) public static void exit( @Advice.This BufferedReader thizz, @Advice.Return int read, @Advice.Enter CharBufferSpanPair bufferSpanPair) { - if (bufferSpanPair == null) { - return; - } - int callDepth = HypertraceCallDepthThreadLocalMap.decrementCallDepth(BufferedReader.class); - if (callDepth > 0) { - return; - } - - if (read == -1) { - bufferSpanPair.captureBody(HypertraceSemanticAttributes.HTTP_REQUEST_BODY); - } else { - bufferSpanPair.writeToBuffer((byte) read); + try { + if (bufferSpanPair == null) { + return; + } + int callDepth = HypertraceCallDepthThreadLocalMap.decrementCallDepth(BufferedReader.class); + if (callDepth > 0) { + return; + } + + if (read == -1) { + bufferSpanPair.captureBody(HypertraceSemanticAttributes.HTTP_REQUEST_BODY); + } else { + bufferSpanPair.writeToBuffer((byte) read); + } + } catch (Throwable t) { + if (t instanceof HypertraceEvaluationException) { + throw t; + } else { + // ignore + } } } } @@ -113,23 +122,31 @@ public static CharBufferSpanPair enter(@Advice.This BufferedReader thizz) { return bufferSpanPair; } - @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) + @Advice.OnMethodExit(onThrowable = Throwable.class) public static void exit( @Advice.Return int read, @Advice.Argument(0) char c[], @Advice.Enter CharBufferSpanPair bufferSpanPair) { - if (bufferSpanPair == null) { - return; - } - int callDepth = HypertraceCallDepthThreadLocalMap.decrementCallDepth(BufferedReader.class); - if (callDepth > 0) { - return; - } - - if (read == -1) { - bufferSpanPair.captureBody(HypertraceSemanticAttributes.HTTP_REQUEST_BODY); - } else { - bufferSpanPair.writeToBuffer(c, 0, read); + try { + if (bufferSpanPair == null) { + return; + } + int callDepth = HypertraceCallDepthThreadLocalMap.decrementCallDepth(BufferedReader.class); + if (callDepth > 0) { + return; + } + + if (read == -1) { + bufferSpanPair.captureBody(HypertraceSemanticAttributes.HTTP_REQUEST_BODY); + } else { + bufferSpanPair.writeToBuffer(c, 0, read); + } + } catch (Throwable t) { + if (t instanceof HypertraceEvaluationException) { + throw t; + } else { + // ignore + } } } } @@ -147,25 +164,33 @@ public static CharBufferSpanPair enter(@Advice.This BufferedReader thizz) { return bufferSpanPair; } - @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) + @Advice.OnMethodExit(onThrowable = Throwable.class) public static void exit( @Advice.Return int read, @Advice.Argument(0) char c[], @Advice.Argument(1) int off, @Advice.Argument(2) int len, @Advice.Enter CharBufferSpanPair bufferSpanPair) { - if (bufferSpanPair == null) { - return; - } - int callDepth = HypertraceCallDepthThreadLocalMap.decrementCallDepth(BufferedReader.class); - if (callDepth > 0) { - return; - } - - if (read == -1) { - bufferSpanPair.captureBody(HypertraceSemanticAttributes.HTTP_REQUEST_BODY); - } else { - bufferSpanPair.writeToBuffer(c, off, read); + try { + if (bufferSpanPair == null) { + return; + } + int callDepth = HypertraceCallDepthThreadLocalMap.decrementCallDepth(BufferedReader.class); + if (callDepth > 0) { + return; + } + + if (read == -1) { + bufferSpanPair.captureBody(HypertraceSemanticAttributes.HTTP_REQUEST_BODY); + } else { + bufferSpanPair.writeToBuffer(c, off, read); + } + } catch (Throwable t) { + if (t instanceof HypertraceEvaluationException) { + throw t; + } else { + // ignore + } } } } @@ -183,22 +208,30 @@ public static CharBufferSpanPair enter(@Advice.This BufferedReader thizz) { return bufferSpanPair; } - @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) + @Advice.OnMethodExit(onThrowable = Throwable.class) public static void exit( @Advice.Return String line, @Advice.Enter CharBufferSpanPair bufferSpanPair) throws IOException { - if (bufferSpanPair == null) { - return; - } - int callDepth = HypertraceCallDepthThreadLocalMap.decrementCallDepth(BufferedReader.class); - if (callDepth > 0) { - return; - } - - if (line == null) { - bufferSpanPair.captureBody(HypertraceSemanticAttributes.HTTP_REQUEST_BODY); - } else { - bufferSpanPair.writeLine(line); + try { + if (bufferSpanPair == null) { + return; + } + int callDepth = HypertraceCallDepthThreadLocalMap.decrementCallDepth(BufferedReader.class); + if (callDepth > 0) { + return; + } + + if (line == null) { + bufferSpanPair.captureBody(HypertraceSemanticAttributes.HTTP_REQUEST_BODY); + } else { + bufferSpanPair.writeLine(line); + } + } catch (Throwable t) { + if (t instanceof HypertraceEvaluationException) { + throw t; + } else { + // ignore + } } } } diff --git a/instrumentation/servlet/servlet-rw/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/rw/reader/BufferedReaderContextAccessInstrumentationModule.java b/instrumentation/servlet/servlet-rw/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/rw/reader/BufferedReaderContextAccessInstrumentationModule.java deleted file mode 100644 index 08fe4cff0..000000000 --- a/instrumentation/servlet/servlet-rw/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/rw/reader/BufferedReaderContextAccessInstrumentationModule.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright The Hypertrace Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.rw.reader; - -import static net.bytebuddy.matcher.ElementMatchers.*; - -import io.opentelemetry.instrumentation.api.util.VirtualField; -import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; -import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; -import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; -import io.opentelemetry.javaagent.tooling.muzzle.InstrumentationModuleMuzzle; -import io.opentelemetry.javaagent.tooling.muzzle.VirtualFieldMappingsBuilder; -import io.opentelemetry.javaagent.tooling.muzzle.references.ClassRef; -import java.io.BufferedReader; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import net.bytebuddy.asm.Advice; -import net.bytebuddy.description.type.TypeDescription; -import net.bytebuddy.matcher.ElementMatcher; -import org.hypertrace.agent.core.instrumentation.buffer.CharBufferSpanPair; - -// SPI explicitly added in META-INF/services/... -public class BufferedReaderContextAccessInstrumentationModule extends InstrumentationModule - implements InstrumentationModuleMuzzle { - - public BufferedReaderContextAccessInstrumentationModule() { - super("test-buffered-reader", "ht"); - } - - @Override - public List typeInstrumentations() { - return Collections.singletonList(new BufferedReaderTriggerInstrumentation()); - } - - @Override - public Map getMuzzleReferences() { - return Collections.emptyMap(); - } - - @Override - public void registerMuzzleVirtualFields(VirtualFieldMappingsBuilder builder) { - builder.register("java.io.BufferedReader", CharBufferSpanPair.class.getName()); - } - - @Override - public List getMuzzleHelperClassNames() { - return Collections.emptyList(); - } - - static class BufferedReaderTriggerInstrumentation implements TypeInstrumentation { - - @Override - public ElementMatcher typeMatcher() { - return named("org.BufferedReaderPrintWriterContextAccess"); - } - - @Override - public void transform(TypeTransformer transformer) { - transformer.applyAdviceToMethod( - named("addToBufferedReaderContext").and(takesArguments(2)).and(isPublic()), - BufferedReaderContextAccessInstrumentationModule.class.getName() + "$TestAdvice"); - } - } - - static class TestAdvice { - - @Advice.OnMethodEnter(suppress = Throwable.class) - public static void enter( - @Advice.Argument(0) BufferedReader bufferedReader, - @Advice.Argument(1) CharBufferSpanPair metadata) { - VirtualField contextStore = - VirtualField.find(BufferedReader.class, CharBufferSpanPair.class); - contextStore.set(bufferedReader, metadata); - } - } -} diff --git a/instrumentation/servlet/servlet-rw/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/rw/reader/BufferedReaderInstrumentationTest.java b/instrumentation/servlet/servlet-rw/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/rw/reader/BufferedReaderInstrumentationTest.java deleted file mode 100644 index 3f65b1cb9..000000000 --- a/instrumentation/servlet/servlet-rw/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/rw/reader/BufferedReaderInstrumentationTest.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright The Hypertrace Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.rw.reader; - -import io.opentelemetry.api.trace.Span; -import java.io.BufferedReader; -import java.io.CharArrayReader; -import java.io.IOException; -import java.util.Collections; -import java.util.Map; -import org.BufferedReaderPrintWriterContextAccess; -import org.TestBufferedReader; -import org.hypertrace.agent.core.TriFunction; -import org.hypertrace.agent.core.filter.FilterResult; -import org.hypertrace.agent.core.instrumentation.buffer.*; -import org.hypertrace.agent.testing.AbstractInstrumenterTest; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -public class BufferedReaderInstrumentationTest extends AbstractInstrumenterTest { - - private static final String TEST_SPAN_NAME = "foo"; - private static final String BODY = "boobar"; - private static final TriFunction, FilterResult> NOOP_FILTER = - (span, s, stringStringMap) -> new FilterResult(false, 0, ""); - - @Test - public void read() throws IOException { - Span span = TEST_TRACER.spanBuilder(TEST_SPAN_NAME).startSpan(); - - BufferedReader bufferedReader = new BufferedReader(new CharArrayReader(BODY.toCharArray())); - - BoundedCharArrayWriter buffer = BoundedBuffersFactory.createWriter(); - CharBufferSpanPair bufferSpanPair = - new CharBufferSpanPair(span, buffer, NOOP_FILTER, Collections.emptyMap()); - - BufferedReaderPrintWriterContextAccess.addToBufferedReaderContext( - bufferedReader, bufferSpanPair); - - while (bufferedReader.read() != -1) {} - Assertions.assertEquals(BODY, buffer.toString()); - } - - @Test - public void read_callDepth_isCleared() throws IOException { - Span span = TEST_TRACER.spanBuilder(TEST_SPAN_NAME).startSpan(); - - BufferedReader bufferedReader = new BufferedReader(new CharArrayReader(BODY.toCharArray())); - bufferedReader.read(); - - BoundedCharArrayWriter buffer = BoundedBuffersFactory.createWriter(); - CharBufferSpanPair bufferSpanPair = - new CharBufferSpanPair(span, buffer, NOOP_FILTER, Collections.emptyMap()); - - BufferedReaderPrintWriterContextAccess.addToBufferedReaderContext( - bufferedReader, bufferSpanPair); - - while (bufferedReader.read() != -1) {} - Assertions.assertEquals(BODY.substring(1), buffer.toString()); - } - - @Test - public void read_char_arr() throws IOException { - Span span = TEST_TRACER.spanBuilder(TEST_SPAN_NAME).startSpan(); - - BufferedReader bufferedReader = new BufferedReader(new CharArrayReader(BODY.toCharArray())); - - BoundedCharArrayWriter buffer = BoundedBuffersFactory.createWriter(); - CharBufferSpanPair bufferSpanPair = - new CharBufferSpanPair(span, buffer, NOOP_FILTER, Collections.emptyMap()); - - BufferedReaderPrintWriterContextAccess.addToBufferedReaderContext( - bufferedReader, bufferSpanPair); - - while (bufferedReader.read(new char[BODY.length()]) != -1) {} - Assertions.assertEquals(BODY, buffer.toString()); - } - - @Test - public void read_callDepth_char_arr() throws IOException { - Span span = TEST_TRACER.spanBuilder(TEST_SPAN_NAME).startSpan(); - - BufferedReader bufferedReader = new BufferedReader(new CharArrayReader(BODY.toCharArray())); - bufferedReader.read(new char[2]); - - BoundedCharArrayWriter buffer = BoundedBuffersFactory.createWriter(); - CharBufferSpanPair bufferSpanPair = - new CharBufferSpanPair(span, buffer, NOOP_FILTER, Collections.emptyMap()); - - BufferedReaderPrintWriterContextAccess.addToBufferedReaderContext( - bufferedReader, bufferSpanPair); - - while (bufferedReader.read(new char[BODY.length()]) != -1) {} - Assertions.assertEquals(BODY.substring(2), buffer.toString()); - } - - @Test - public void read_char_arr_offset() throws IOException { - Span span = TEST_TRACER.spanBuilder(TEST_SPAN_NAME).startSpan(); - - BufferedReader bufferedReader = new BufferedReader(new CharArrayReader(BODY.toCharArray())); - - BoundedCharArrayWriter buffer = BoundedBuffersFactory.createWriter(); - CharBufferSpanPair bufferSpanPair = - new CharBufferSpanPair(span, buffer, NOOP_FILTER, Collections.emptyMap()); - - BufferedReaderPrintWriterContextAccess.addToBufferedReaderContext( - bufferedReader, bufferSpanPair); - - bufferedReader.read(new char[BODY.length()], 0, 2); - bufferedReader.read(new char[BODY.length()], 2, BODY.length() - 2); - Assertions.assertEquals(BODY, buffer.toString()); - } - - @Test - public void readLine() throws IOException { - Span span = TEST_TRACER.spanBuilder(TEST_SPAN_NAME).startSpan(); - - BufferedReader bufferedReader = - new TestBufferedReader(new CharArrayReader((BODY + "\n").toCharArray())); - - BoundedCharArrayWriter buffer = BoundedBuffersFactory.createWriter(); - CharBufferSpanPair bufferSpanPair = - new CharBufferSpanPair(span, buffer, NOOP_FILTER, Collections.emptyMap()); - - BufferedReaderPrintWriterContextAccess.addToBufferedReaderContext( - bufferedReader, bufferSpanPair); - - bufferedReader.readLine(); - Assertions.assertEquals(BODY, buffer.toString()); - } -} diff --git a/instrumentation/servlet/servlet-rw/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/rw/writer/PrintWriterContextAccessInstrumentationModule.java b/instrumentation/servlet/servlet-rw/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/rw/writer/PrintWriterContextAccessInstrumentationModule.java deleted file mode 100644 index da44d1028..000000000 --- a/instrumentation/servlet/servlet-rw/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/rw/writer/PrintWriterContextAccessInstrumentationModule.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright The Hypertrace Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.rw.writer; - -import static net.bytebuddy.matcher.ElementMatchers.isPublic; -import static net.bytebuddy.matcher.ElementMatchers.named; -import static net.bytebuddy.matcher.ElementMatchers.takesArguments; - -import io.opentelemetry.instrumentation.api.util.VirtualField; -import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; -import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; -import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; -import io.opentelemetry.javaagent.tooling.muzzle.InstrumentationModuleMuzzle; -import io.opentelemetry.javaagent.tooling.muzzle.VirtualFieldMappingsBuilder; -import io.opentelemetry.javaagent.tooling.muzzle.references.ClassRef; -import java.io.PrintWriter; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import net.bytebuddy.asm.Advice; -import net.bytebuddy.description.type.TypeDescription; -import net.bytebuddy.matcher.ElementMatcher; -import org.hypertrace.agent.core.instrumentation.buffer.BoundedCharArrayWriter; - -// SPI explicitly added in META-INF/services/... -public class PrintWriterContextAccessInstrumentationModule extends InstrumentationModule - implements InstrumentationModuleMuzzle { - - public PrintWriterContextAccessInstrumentationModule() { - super("test-print-writer", "ht"); - } - - @Override - public List typeInstrumentations() { - return Collections.singletonList(new PrintWriterTriggerInstrumentation()); - } - - @Override - public Map getMuzzleReferences() { - return Collections.emptyMap(); - } - - @Override - public void registerMuzzleVirtualFields(VirtualFieldMappingsBuilder builder) { - builder.register("java.io.PrintWriter", BoundedCharArrayWriter.class.getName()); - } - - @Override - public List getMuzzleHelperClassNames() { - return Collections.emptyList(); - } - - class PrintWriterTriggerInstrumentation implements TypeInstrumentation { - - @Override - public ElementMatcher typeMatcher() { - return named("org.BufferedReaderPrintWriterContextAccess"); - } - - @Override - public void transform(TypeTransformer transformer) { - transformer.applyAdviceToMethod( - named("addToPrintWriterContext").and(takesArguments(2)).and(isPublic()), - PrintWriterContextAccessInstrumentationModule.class.getName() + "$TestAdvice"); - } - } - - static class TestAdvice { - @Advice.OnMethodEnter(suppress = Throwable.class) - public static void enter( - @Advice.Argument(0) PrintWriter printWriter, - @Advice.Argument(1) BoundedCharArrayWriter metadata) { - VirtualField contextStore = - VirtualField.find(PrintWriter.class, BoundedCharArrayWriter.class); - contextStore.set(printWriter, metadata); - } - } -} diff --git a/instrumentation/servlet/servlet-rw/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/rw/writer/PrintWriterInstrumentationTest.java b/instrumentation/servlet/servlet-rw/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/rw/writer/PrintWriterInstrumentationTest.java deleted file mode 100644 index da71ea8ec..000000000 --- a/instrumentation/servlet/servlet-rw/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/rw/writer/PrintWriterInstrumentationTest.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright The Hypertrace Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.rw.writer; - -import java.io.CharArrayWriter; -import java.io.PrintWriter; -import org.BufferedReaderPrintWriterContextAccess; -import org.hypertrace.agent.core.instrumentation.buffer.BoundedBuffersFactory; -import org.hypertrace.agent.core.instrumentation.buffer.BoundedCharArrayWriter; -import org.hypertrace.agent.testing.AbstractInstrumenterTest; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -public class PrintWriterInstrumentationTest extends AbstractInstrumenterTest { - - private static final String BODY = "boobar"; - - @Test - public void write_single_char() { - PrintWriter printWriter = new PrintWriter(new CharArrayWriter()); - - BoundedCharArrayWriter buffer = BoundedBuffersFactory.createWriter(); - BufferedReaderPrintWriterContextAccess.addToPrintWriterContext(printWriter, buffer); - - char[] chars = BODY.toCharArray(); - for (int i = 0; i < BODY.length(); i++) { - printWriter.write(chars[i]); - } - Assertions.assertEquals(BODY, buffer.toString()); - } - - @Test - public void write_char_arr() { - PrintWriter printWriter = new PrintWriter(new CharArrayWriter()); - - BoundedCharArrayWriter buffer = BoundedBuffersFactory.createWriter(); - BufferedReaderPrintWriterContextAccess.addToPrintWriterContext(printWriter, buffer); - - printWriter.write(BODY.toCharArray()); - Assertions.assertEquals(BODY, buffer.toString()); - } - - @Test - public void write_char_arr_offset() { - PrintWriter printWriter = new PrintWriter(new CharArrayWriter()); - - BoundedCharArrayWriter buffer = BoundedBuffersFactory.createWriter(); - BufferedReaderPrintWriterContextAccess.addToPrintWriterContext(printWriter, buffer); - - char[] chars = BODY.toCharArray(); - printWriter.write(chars, 0, 2); - printWriter.write(chars, 2, chars.length - 2); - Assertions.assertEquals(BODY, buffer.toString()); - } - - @Test - public void write_str() { - PrintWriter printWriter = new PrintWriter(new CharArrayWriter()); - - BoundedCharArrayWriter buffer = BoundedBuffersFactory.createWriter(); - BufferedReaderPrintWriterContextAccess.addToPrintWriterContext(printWriter, buffer); - - printWriter.write(BODY); - Assertions.assertEquals(BODY, buffer.toString()); - } - - @Test - public void write_str_offset() { - PrintWriter printWriter = new PrintWriter(new CharArrayWriter()); - - BoundedCharArrayWriter buffer = BoundedBuffersFactory.createWriter(); - BufferedReaderPrintWriterContextAccess.addToPrintWriterContext(printWriter, buffer); - - printWriter.write(BODY, 0, 2); - printWriter.write(BODY, 2, BODY.length() - 2); - Assertions.assertEquals(BODY, buffer.toString()); - } - - @Test - public void print_str() { - PrintWriter printWriter = new PrintWriter(new CharArrayWriter()); - - BoundedCharArrayWriter buffer = BoundedBuffersFactory.createWriter(); - BufferedReaderPrintWriterContextAccess.addToPrintWriterContext(printWriter, buffer); - - printWriter.print(BODY); - Assertions.assertEquals(BODY, buffer.toString()); - } - - @Test - public void println() { - PrintWriter printWriter = new PrintWriter(new CharArrayWriter()); - - BoundedCharArrayWriter buffer = BoundedBuffersFactory.createWriter(); - BufferedReaderPrintWriterContextAccess.addToPrintWriterContext(printWriter, buffer); - - printWriter.println(); - Assertions.assertEquals("\n", buffer.toString()); - } - - @Test - public void println_str() { - PrintWriter printWriter = new PrintWriter(new CharArrayWriter()); - - BoundedCharArrayWriter buffer = BoundedBuffersFactory.createWriter(); - BufferedReaderPrintWriterContextAccess.addToPrintWriterContext(printWriter, buffer); - - printWriter.println(BODY); - Assertions.assertEquals(BODY + "\n", buffer.toString()); - } -} diff --git a/instrumentation/servlet/servlet-rw/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/rw/ServletRWInstrumentationTest.java b/instrumentation/servlet/servlet-rw/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/rw/ServletRWInstrumentationTest.java new file mode 100644 index 000000000..8adf756bc --- /dev/null +++ b/instrumentation/servlet/servlet-rw/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/rw/ServletRWInstrumentationTest.java @@ -0,0 +1,326 @@ +/* + * Copyright The Hypertrace Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.v3_0.rw; + +import io.opentelemetry.proto.trace.v1.Span; +import java.util.EnumSet; +import java.util.List; +import javax.servlet.DispatcherType; +import okhttp3.FormBody; +import okhttp3.MediaType; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.hypertrace.agent.testing.AbstractInstrumenterTest; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import rw.WrappingFilter; + +public class ServletRWInstrumentationTest extends AbstractInstrumenterTest { + private static final String REQUEST_BODY = "hello"; + private static final String REQUEST_HEADER = "requestheader"; + private static final String REQUEST_HEADER_VALUE = "requestvalue"; + + private static Server server = new Server(0); + private static int serverPort; + + @BeforeAll + public static void startServer() throws Exception { + ServletContextHandler handler = new ServletContextHandler(); + + handler.addFilter(WrappingFilter.class, "/*", EnumSet.allOf(DispatcherType.class)); + + handler.addServlet(TestServlets.GetHello.class, "/hello"); + handler.addServlet(TestServlets.EchoWriter_single_char.class, "/echo_writer_single_char"); + handler.addServlet(TestServlets.EchoWriter_arr.class, "/echo_writer_arr"); + handler.addServlet(TestServlets.EchoWriter_arr_offset.class, "/echo_writer_arr_offset"); + handler.addServlet(TestServlets.EchoWriter_readLine_write.class, "/echo_writer_readLine_write"); + handler.addServlet(TestServlets.EchoWriter_readLines.class, "/echo_writer_readLines"); + handler.addServlet( + TestServlets.EchoWriter_readLine_print_str.class, "/echo_writer_readLine_print_str"); + handler.addServlet( + TestServlets.EchoWriter_readLine_print_arr.class, "/echo_writer_readLine_print_arr"); + handler.addServlet(TestServlets.EchoAsyncResponse_writer.class, "/echo_async_response_writer"); + handler.addServlet( + TestServlets.EchoStream_read_large_array.class, "/echo_stream_read_large_array"); + handler.addServlet( + TestServlets.EchoReader_read_large_array.class, "/echo_reader_read_large_array"); + server.setHandler(handler); + server.start(); + serverPort = server.getConnectors()[0].getLocalPort(); + } + + @AfterAll + public static void stopServer() throws Exception { + server.stop(); + } + + @Test + public void echo_async_response_writer() throws Exception { + postJson(String.format("http://localhost:%d/echo_async_response_writer", serverPort)); + } + + @Test + public void postJson_stream_read_large_array() throws Exception { + postJson(String.format("http://localhost:%d/echo_stream_read_large_array", serverPort)); + } + + @Test + public void postJson_reader_read_large_array() throws Exception { + postJson(String.format("http://localhost:%d/echo_reader_read_large_array", serverPort)); + } + + @Test + public void postJson_writer_single_char() throws Exception { + postJson(String.format("http://localhost:%d/echo_writer_single_char", serverPort)); + } + + @Test + public void postJson_writer_arr() throws Exception { + postJson(String.format("http://localhost:%d/echo_writer_arr", serverPort)); + } + + @Test + public void postJson_writer_arr_offset() throws Exception { + postJson(String.format("http://localhost:%d/echo_writer_arr_offset", serverPort)); + } + + @Test + public void postJson_writer_readLine_write() throws Exception { + postJson(String.format("http://localhost:%d/echo_writer_readLine_write", serverPort)); + } + + @Test + public void postJson_writer_readLine_print_str() throws Exception { + postJson(String.format("http://localhost:%d/echo_writer_readLine_print_str", serverPort)); + } + + @Test + public void postJson_writer_readLines() throws Exception { + postJson(String.format("http://localhost:%d/echo_writer_readLines", serverPort)); + } + + @Test + public void postJson_writer_readLine_print_arr() throws Exception { + postJson(String.format("http://localhost:%d/echo_writer_readLine_print_arr", serverPort)); + } + + @Test + public void portUrlEncoded() throws Exception { + FormBody formBody = new FormBody.Builder().add("key1", "value1").add("key2", "value2").build(); + Request request = + new Request.Builder() + .url(String.format("http://localhost:%d/echo_writer_single_char", serverPort)) + .post(formBody) + .header(REQUEST_HEADER, REQUEST_HEADER_VALUE) + .build(); + try (Response response = httpClient.newCall(request).execute()) { + Assertions.assertEquals(200, response.code()); + } + + TEST_WRITER.waitForTraces(1); + List> traces = + TEST_WRITER.waitForSpans(1, span -> span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT)); + Assertions.assertEquals(1, traces.size()); + List spans = traces.get(0); + Assertions.assertEquals(1, spans.size()); + Span span = spans.get(0); + Assertions.assertEquals( + REQUEST_HEADER_VALUE, + TEST_WRITER + .getAttributesMap(span) + .get("http.request.header." + REQUEST_HEADER) + .getStringValue()); + Assertions.assertEquals( + TestServlets.RESPONSE_HEADER_VALUE, + TEST_WRITER + .getAttributesMap(span) + .get("http.response.header." + TestServlets.RESPONSE_HEADER) + .getStringValue()); + Assertions.assertEquals( + "key1=value1&key2=value2", + TEST_WRITER.getAttributesMap(span).get("http.request.body").getStringValue()); + Assertions.assertEquals( + TestServlets.RESPONSE_BODY, + TEST_WRITER.getAttributesMap(span).get("http.response.body").getStringValue()); + } + + @Test + public void getHello() throws Exception { + Request request = + new Request.Builder() + .url(String.format("http://localhost:%d/hello", serverPort)) + .get() + .header(REQUEST_HEADER, REQUEST_HEADER_VALUE) + .build(); + try (Response response = httpClient.newCall(request).execute()) { + Assertions.assertEquals(204, response.code()); + } + + TEST_WRITER.waitForTraces(1); + List> traces = + TEST_WRITER.waitForSpans(1, span -> span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT)); + Assertions.assertEquals(1, traces.size()); + List spans = traces.get(0); + Assertions.assertEquals(1, spans.size()); + Span span = spans.get(0); + Assertions.assertEquals( + REQUEST_HEADER_VALUE, + TEST_WRITER + .getAttributesMap(span) + .get("http.request.header." + REQUEST_HEADER) + .getStringValue()); + Assertions.assertEquals( + TestServlets.RESPONSE_HEADER_VALUE, + TEST_WRITER + .getAttributesMap(span) + .get("http.response.header." + TestServlets.RESPONSE_HEADER) + .getStringValue()); + Assertions.assertNull(TEST_WRITER.getAttributesMap(span).get("http.request.body")); + Assertions.assertNull(TEST_WRITER.getAttributesMap(span).get("http.response.body")); + } + + @Test + public void block() throws Exception { + Request request = + new Request.Builder() + .url(String.format("http://localhost:%d/hello", serverPort)) + .get() + .header("mockblock", "true") + .build(); + try (Response response = httpClient.newCall(request).execute()) { + Assertions.assertEquals(403, response.code()); + Assertions.assertEquals("Hypertrace Blocked Request", response.body().string()); + } + + TEST_WRITER.waitForTraces(1); + List> traces = + TEST_WRITER.waitForSpans(1, span -> span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT)); + Assertions.assertEquals(1, traces.size()); + List spans = traces.get(0); + Assertions.assertEquals(1, spans.size()); + Span span = spans.get(0); + Assertions.assertNull( + TEST_WRITER + .getAttributesMap(span) + .get("http.response.header." + TestServlets.RESPONSE_HEADER)); + Assertions.assertNull(TEST_WRITER.getAttributesMap(span).get("http.request.body")); + Assertions.assertNull(TEST_WRITER.getAttributesMap(span).get("http.response.body")); + } + + @Test + public void blockBody() throws Exception { + FormBody formBody = new FormBody.Builder().add("block", "true").build(); + Request request = + new Request.Builder() + .url(String.format("http://localhost:%d/echo_writer_single_char", serverPort)) + .post(formBody) + .header(REQUEST_HEADER, REQUEST_HEADER_VALUE) + .build(); + try (Response response = httpClient.newCall(request).execute()) { + Assertions.assertEquals(403, response.code()); + Assertions.assertEquals("Hypertrace Blocked Request", response.body().string()); + } + + TEST_WRITER.waitForTraces(1); + List> traces = + TEST_WRITER.waitForSpans(1, span -> span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT)); + Assertions.assertEquals(1, traces.size()); + List spans = traces.get(0); + Assertions.assertEquals(1, spans.size()); + Span span = spans.get(0); + Assertions.assertNull( + TEST_WRITER + .getAttributesMap(span) + .get("http.response.header." + TestServlets.RESPONSE_HEADER)); + Assertions.assertEquals( + "block=true", TEST_WRITER.getAttributesMap(span).get("http.request.body").getStringValue()); + Assertions.assertNull(TEST_WRITER.getAttributesMap(span).get("http.response.body")); + } + + @Test + public void blockBodyWrappedException() throws Exception { + FormBody formBody = new FormBody.Builder().add("block", "true").build(); + Request request = + new Request.Builder() + .url(String.format("http://localhost:%d/echo_writer_single_char", serverPort)) + .post(formBody) + .header(REQUEST_HEADER, REQUEST_HEADER_VALUE) + .build(); + try (Response response = httpClient.newCall(request).execute()) { + Assertions.assertEquals(403, response.code()); + Assertions.assertEquals("Hypertrace Blocked Request", response.body().string()); + } + + TEST_WRITER.waitForTraces(1); + List> traces = + TEST_WRITER.waitForSpans(1, span -> span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT)); + Assertions.assertEquals(1, traces.size()); + List spans = traces.get(0); + Assertions.assertEquals(1, spans.size()); + Span span = spans.get(0); + Assertions.assertNull( + TEST_WRITER + .getAttributesMap(span) + .get("http.response.header." + TestServlets.RESPONSE_HEADER)); + Assertions.assertEquals( + "block=true", TEST_WRITER.getAttributesMap(span).get("http.request.body").getStringValue()); + Assertions.assertNull(TEST_WRITER.getAttributesMap(span).get("http.response.body")); + } + + public void postJson(String url) throws Exception { + Request request = + new Request.Builder() + .url(url) + .post(RequestBody.create(REQUEST_BODY, MediaType.get("application/json"))) + .header(REQUEST_HEADER, REQUEST_HEADER_VALUE) + .build(); + try (Response response = httpClient.newCall(request).execute()) { + Assertions.assertEquals(200, response.code()); + Assertions.assertEquals(TestServlets.RESPONSE_BODY, response.body().string()); + } + + TEST_WRITER.waitForTraces(1); + List> traces = + TEST_WRITER.waitForSpans(1, span -> span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT)); + Assertions.assertEquals(1, traces.size()); + List spans = traces.get(0); + Assertions.assertEquals(1, spans.size()); + Span span = spans.get(0); + Assertions.assertEquals( + REQUEST_HEADER_VALUE, + TEST_WRITER + .getAttributesMap(span) + .get("http.request.header." + REQUEST_HEADER) + .getStringValue()); + Assertions.assertEquals( + TestServlets.RESPONSE_HEADER_VALUE, + TEST_WRITER + .getAttributesMap(span) + .get("http.response.header." + TestServlets.RESPONSE_HEADER) + .getStringValue()); + Assertions.assertEquals( + REQUEST_BODY, TEST_WRITER.getAttributesMap(span).get("http.request.body").getStringValue()); + Assertions.assertEquals( + TestServlets.RESPONSE_BODY, + TEST_WRITER.getAttributesMap(span).get("http.response.body").getStringValue()); + } +} diff --git a/instrumentation/servlet/servlet-rw/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/rw/TestServlets.java b/instrumentation/servlet/servlet-rw/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/rw/TestServlets.java new file mode 100644 index 000000000..e7344df9f --- /dev/null +++ b/instrumentation/servlet/servlet-rw/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/rw/TestServlets.java @@ -0,0 +1,198 @@ +/* + * Copyright The Hypertrace Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.v3_0.rw; + +import java.io.IOException; +import java.util.stream.Stream; +import javax.servlet.AsyncContext; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public class TestServlets { + + public static final String RESPONSE_BODY = "{\"key\": \"val\"}"; + + public static final String RESPONSE_HEADER = "responseheader"; + public static final String RESPONSE_HEADER_VALUE = "responsevalue"; + + public static class GetHello extends HttpServlet { + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException { + while (req.getReader().read() != -1) {} + resp.setStatus(204); + resp.setHeader(RESPONSE_HEADER, RESPONSE_HEADER_VALUE); + resp.getWriter().write("hello"); + } + } + + public static class EchoWriter_single_char extends HttpServlet { + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException { + while (req.getReader().read() != -1) {} + + resp.setStatus(200); + resp.setContentType("application/json"); + resp.setHeader(RESPONSE_HEADER, RESPONSE_HEADER_VALUE); + + for (int i = 0; i < RESPONSE_BODY.length(); i++) + resp.getWriter().write(RESPONSE_BODY.charAt(i)); + } + } + + public static class EchoWriter_arr extends HttpServlet { + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException { + while (req.getReader().read(new char[2]) != -1) {} + + resp.setStatus(200); + resp.setContentType("application/json"); + resp.setHeader(RESPONSE_HEADER, RESPONSE_HEADER_VALUE); + resp.getWriter().write(RESPONSE_BODY.toCharArray()); + } + } + + public static class EchoWriter_arr_offset extends HttpServlet { + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException { + while (req.getReader().read(new char[12], 3, 2) != -1) {} + + resp.setStatus(200); + resp.setContentType("application/json"); + resp.setHeader(RESPONSE_HEADER, RESPONSE_HEADER_VALUE); + + char[] chars = RESPONSE_BODY.toCharArray(); + resp.getWriter().write(chars, 0, 2); + resp.getWriter().write(chars, 2, 2); + resp.getWriter().write(chars, 4, chars.length - 4); + } + } + + public static class EchoWriter_readLine_write extends HttpServlet { + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException { + while (req.getReader().readLine() != null) {} + + resp.setStatus(200); + resp.setContentType("application/json"); + resp.setHeader(RESPONSE_HEADER, RESPONSE_HEADER_VALUE); + + resp.getWriter().write(RESPONSE_BODY); + } + } + + public static class EchoWriter_readLines extends HttpServlet { + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException { + Stream lines = req.getReader().lines(); + lines.forEach(s -> {}); + + resp.setStatus(200); + resp.setContentType("application/json"); + resp.setHeader(RESPONSE_HEADER, RESPONSE_HEADER_VALUE); + + resp.getWriter().write(RESPONSE_BODY); + } + } + + public static class EchoWriter_readLine_print_str extends HttpServlet { + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException { + while (req.getReader().readLine() != null) {} + + resp.setStatus(200); + resp.setContentType("application/json"); + resp.setHeader(RESPONSE_HEADER, RESPONSE_HEADER_VALUE); + + resp.getWriter().print(RESPONSE_BODY); + } + } + + public static class EchoWriter_readLine_print_arr extends HttpServlet { + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException { + while (req.getReader().readLine() != null) {} + + resp.setStatus(200); + resp.setContentType("application/json"); + resp.setHeader(RESPONSE_HEADER, RESPONSE_HEADER_VALUE); + + resp.getWriter().print(RESPONSE_BODY.toCharArray()); + } + } + + public static class EchoAsyncResponse_writer extends HttpServlet { + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) { + + AsyncContext asyncContext = req.startAsync(); + asyncContext.start( + () -> { + while (true) { + try { + if (!(req.getReader().read() != -1)) break; + } catch (IOException e) { + e.printStackTrace(); + } + } + + try { + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + HttpServletResponse httpServletResponse = + (HttpServletResponse) asyncContext.getResponse(); + httpServletResponse.setStatus(200); + httpServletResponse.setContentType("application/json"); + httpServletResponse.setHeader(RESPONSE_HEADER, RESPONSE_HEADER_VALUE); + try { + httpServletResponse.getWriter().print(RESPONSE_BODY); + } catch (IOException e) { + e.printStackTrace(); + } + asyncContext.complete(); + }); + } + } + + public static class EchoStream_read_large_array extends HttpServlet { + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException { + req.getInputStream().read(new byte[1000], 0, 1000); + + resp.setStatus(200); + resp.setContentType("application/json"); + resp.setHeader(RESPONSE_HEADER, RESPONSE_HEADER_VALUE); + + resp.getWriter().print(RESPONSE_BODY.toCharArray()); + } + } + + public static class EchoReader_read_large_array extends HttpServlet { + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException { + req.getReader().read(new char[1000], 0, 1000); + + resp.setStatus(200); + resp.setContentType("application/json"); + resp.setHeader(RESPONSE_HEADER, RESPONSE_HEADER_VALUE); + + resp.getWriter().print(RESPONSE_BODY.toCharArray()); + } + } +} diff --git a/instrumentation/servlet/servlet-rw/src/test/java/org/BufferedReaderPrintWriterContextAccess.java b/instrumentation/servlet/servlet-rw/src/test/java/org/BufferedReaderPrintWriterContextAccess.java deleted file mode 100644 index 867f6e20b..000000000 --- a/instrumentation/servlet/servlet-rw/src/test/java/org/BufferedReaderPrintWriterContextAccess.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright The Hypertrace Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org; - -import java.io.BufferedReader; -import java.io.PrintWriter; -import org.hypertrace.agent.core.instrumentation.buffer.BoundedCharArrayWriter; -import org.hypertrace.agent.core.instrumentation.buffer.CharBufferSpanPair; - -public class BufferedReaderPrintWriterContextAccess { - - private BufferedReaderPrintWriterContextAccess() {} - - public static void addToBufferedReaderContext( - BufferedReader bufferedReader, CharBufferSpanPair buffer) {} - - public static void addToPrintWriterContext( - PrintWriter printWriter, BoundedCharArrayWriter buffer) {} -} diff --git a/instrumentation/servlet/servlet-rw/src/test/java/org/TestBufferedReader.java b/instrumentation/servlet/servlet-rw/src/test/java/rw/DelegatingBufferedReader.java similarity index 70% rename from instrumentation/servlet/servlet-rw/src/test/java/org/TestBufferedReader.java rename to instrumentation/servlet/servlet-rw/src/test/java/rw/DelegatingBufferedReader.java index f760633f8..1952a1f06 100644 --- a/instrumentation/servlet/servlet-rw/src/test/java/org/TestBufferedReader.java +++ b/instrumentation/servlet/servlet-rw/src/test/java/rw/DelegatingBufferedReader.java @@ -14,20 +14,23 @@ * limitations under the License. */ -package org; +package rw; import java.io.BufferedReader; import java.io.IOException; import java.io.Reader; -public class TestBufferedReader extends BufferedReader { +public class DelegatingBufferedReader extends BufferedReader { - public TestBufferedReader(Reader in) { - super(in); + private final Reader delegate; + + public DelegatingBufferedReader(Reader delegate) { + super(delegate); + this.delegate = delegate; } @Override - public String readLine() throws IOException { - return super.readLine(); + public int read(char[] cbuf) throws IOException { + return delegate.read(cbuf); } } diff --git a/testing-common/src/testFixtures/java/org/hypertrace/agent/testing/TestAgentStarter.java b/instrumentation/servlet/servlet-rw/src/test/java/rw/DelegatingPrintWriter.java similarity index 59% rename from testing-common/src/testFixtures/java/org/hypertrace/agent/testing/TestAgentStarter.java rename to instrumentation/servlet/servlet-rw/src/test/java/rw/DelegatingPrintWriter.java index cf8f646ed..db471db09 100644 --- a/testing-common/src/testFixtures/java/org/hypertrace/agent/testing/TestAgentStarter.java +++ b/instrumentation/servlet/servlet-rw/src/test/java/rw/DelegatingPrintWriter.java @@ -14,22 +14,27 @@ * limitations under the License. */ -package org.hypertrace.agent.testing; +package rw; -import io.opentelemetry.javaagent.bootstrap.AgentStarter; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.Writer; -final class TestAgentStarter implements AgentStarter { +public class DelegatingPrintWriter extends PrintWriter { - @Override - public boolean delayStart() { - return false; - } + private final Writer delegate; - @Override - public void start() {} + public DelegatingPrintWriter(Writer delegate) { + super(delegate); + this.delegate = delegate; + } @Override - public ClassLoader getExtensionClassLoader() { - return ClassLoader.getSystemClassLoader(); + public void write(char[] buf) { + try { + this.delegate.write(buf); + } catch (IOException e) { + e.printStackTrace(); + } } } diff --git a/instrumentation/servlet/servlet-rw/src/test/java/rw/WrappingFilter.java b/instrumentation/servlet/servlet-rw/src/test/java/rw/WrappingFilter.java new file mode 100644 index 000000000..b5452e59d --- /dev/null +++ b/instrumentation/servlet/servlet-rw/src/test/java/rw/WrappingFilter.java @@ -0,0 +1,86 @@ +/* + * Copyright The Hypertrace Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package rw; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.PrintWriter; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpServletResponseWrapper; + +public class WrappingFilter implements Filter { + + @Override + public void init(FilterConfig filterConfig) {} + + @Override + public void destroy() {} + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + + HttpServletRequest httpServletRequest = (HttpServletRequest) request; + HttpServletResponse httpServletResponse = (HttpServletResponse) response; + + ReqWrapper reqWrapper = new ReqWrapper(httpServletRequest); + RespWrapper respWrapper = new RespWrapper(httpServletResponse); + chain.doFilter(reqWrapper, respWrapper); + } + + static class ReqWrapper extends HttpServletRequestWrapper { + + private BufferedReader bufferedReader; + + public ReqWrapper(HttpServletRequest request) { + super(request); + } + + @Override + public BufferedReader getReader() throws IOException { + if (bufferedReader == null) { + bufferedReader = new DelegatingBufferedReader(super.getReader()); + } + return bufferedReader; + } + } + + static class RespWrapper extends HttpServletResponseWrapper { + + private PrintWriter printWriter; + + public RespWrapper(HttpServletResponse response) { + super(response); + } + + @Override + public PrintWriter getWriter() throws IOException { + if (printWriter == null) { + printWriter = new DelegatingPrintWriter(super.getWriter()); + } + return printWriter; + } + } +} diff --git a/instrumentation/servlet/servlet-rw/src/test/resources/META-INF/services/io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule b/instrumentation/servlet/servlet-rw/src/test/resources/META-INF/services/io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule deleted file mode 100644 index 61ffe7f24..000000000 --- a/instrumentation/servlet/servlet-rw/src/test/resources/META-INF/services/io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule +++ /dev/null @@ -1,2 +0,0 @@ -io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.rw.reader.BufferedReaderContextAccessInstrumentationModule -io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.rw.writer.PrintWriterContextAccessInstrumentationModule diff --git a/instrumentation/spark-2.3/build.gradle.kts b/instrumentation/spark-2.3/build.gradle.kts index 9561e42f6..f48819b8c 100644 --- a/instrumentation/spark-2.3/build.gradle.kts +++ b/instrumentation/spark-2.3/build.gradle.kts @@ -29,14 +29,10 @@ val versions: Map by extra dependencies { api(project(":instrumentation:servlet:servlet-3.0")) - testRuntimeOnly("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-servlet-common-bootstrap:${versions["opentelemetry_java_agent"]}") muzzleBootstrap("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-servlet-common-bootstrap:${versions["opentelemetry_java_agent"]}") compileOnly("com.sparkjava:spark-core:2.3") - testImplementation(project(":instrumentation:servlet:servlet-rw")) - testImplementation(files(project(":instrumentation:servlet:servlet-rw").dependencyProject.sourceSets.main.map { it.output })) - testImplementation(testFixtures(project(":testing-common"))) + testImplementation(project(":testing-common")) testImplementation("com.sparkjava:spark-core:2.3") - testImplementation("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-semconv:${versions["opentelemetry_semconv"]}") } diff --git a/instrumentation/spark-2.3/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/sparkjava/SparkJavaInstrumentationTest.java b/instrumentation/spark-2.3/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/sparkjava/SparkJavaInstrumentationTest.java index 0375277dc..562707cdd 100644 --- a/instrumentation/spark-2.3/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/sparkjava/SparkJavaInstrumentationTest.java +++ b/instrumentation/spark-2.3/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/sparkjava/SparkJavaInstrumentationTest.java @@ -16,7 +16,7 @@ package io.opentelemetry.javaagent.instrumentation.hypertrace.sparkjava; -import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.proto.trace.v1.Span; import java.io.IOException; import java.util.List; import java.util.concurrent.TimeoutException; @@ -25,7 +25,6 @@ import okhttp3.Request.Builder; import okhttp3.RequestBody; import okhttp3.Response; -import org.hypertrace.agent.core.instrumentation.HypertraceSemanticAttributes; import org.hypertrace.agent.testing.AbstractInstrumenterTest; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; @@ -37,14 +36,14 @@ public class SparkJavaInstrumentationTest extends AbstractInstrumenterTest { private static final int PORT = 8099; private static final String RESPONSE_BODY = "{\"key\": \"val\"}"; - private static final String RESPONSE_HEADER = "responseHeader"; + private static final String RESPONSE_HEADER = "responseheader"; private static final String RESPONSE_HEADER_VALUE = "responseHeaderValue"; private static final String REQUEST_BODY = "Hi!"; - private static final String REQUEST_HEADER = "requestHeader"; + private static final String REQUEST_HEADER = "requestheader"; private static final String REQUEST_HEADER_VALUE = "responseHeader"; @BeforeAll - public static void postJson() { + public static void postJson() throws Exception { AbstractInstrumenterTest.beforeAll(); Spark.port(PORT); Spark.post( @@ -85,27 +84,30 @@ public void postRequest() throws IOException, InterruptedException, TimeoutExcep } TEST_WRITER.waitForTraces(1); - List> traces = TEST_WRITER.getTraces(); + List> traces = + TEST_WRITER.waitForSpans(1, span -> span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT)); Assertions.assertEquals(1, traces.size()); - List spans = traces.get(0); + List spans = traces.get(0); Assertions.assertEquals(1, spans.size()); - SpanData spanData = spans.get(0); + Span span = spans.get(0); Assertions.assertEquals( - REQUEST_BODY, spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); + REQUEST_BODY, TEST_WRITER.getAttributesMap(span).get("http.request.body").getStringValue()); Assertions.assertEquals( REQUEST_HEADER_VALUE, - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpRequestHeader(REQUEST_HEADER))); + TEST_WRITER + .getAttributesMap(span) + .get("http.request.header." + REQUEST_HEADER) + .getStringValue()); Assertions.assertEquals( RESPONSE_BODY, - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER.getAttributesMap(span).get("http.response.body").getStringValue()); Assertions.assertEquals( RESPONSE_HEADER_VALUE, - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpResponseHeader(RESPONSE_HEADER))); + TEST_WRITER + .getAttributesMap(span) + .get("http.response.header." + RESPONSE_HEADER) + .getStringValue()); } @Test @@ -119,24 +121,27 @@ public void exceptionInHandler() throws IOException, InterruptedException, Timeo try (Response response = httpClient.newCall(request).execute()) {} TEST_WRITER.waitForTraces(1); - List> traces = TEST_WRITER.getTraces(); + List> traces = + TEST_WRITER.waitForSpans(1, span -> span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT)); Assertions.assertEquals(1, traces.size()); - List spans = traces.get(0); + List spans = traces.get(0); Assertions.assertEquals(1, spans.size()); - SpanData spanData = spans.get(0); + Span span = spans.get(0); Assertions.assertEquals( REQUEST_HEADER_VALUE, - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpRequestHeader(REQUEST_HEADER))); + TEST_WRITER + .getAttributesMap(span) + .get("http.request.header." + REQUEST_HEADER) + .getStringValue()); Assertions.assertEquals( "

500 Internal Error

", - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER.getAttributesMap(span).get("http.response.body").getStringValue()); Assertions.assertEquals( RESPONSE_HEADER_VALUE, - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpResponseHeader(RESPONSE_HEADER))); + TEST_WRITER + .getAttributesMap(span) + .get("http.response.header." + RESPONSE_HEADER) + .getStringValue()); } } diff --git a/instrumentation/spring/spring-webflux-5.0/build.gradle.kts b/instrumentation/spring/spring-webflux-5.0/build.gradle.kts index e200a02cf..0d1da7685 100644 --- a/instrumentation/spring/spring-webflux-5.0/build.gradle.kts +++ b/instrumentation/spring/spring-webflux-5.0/build.gradle.kts @@ -15,14 +15,11 @@ configurations.testRuntimeClasspath { } dependencies { - testImplementation(testFixtures(project(":testing-common"))) - testImplementation(project(":instrumentation:netty:netty-4.1")) - testImplementation(files(project(":instrumentation:netty:netty-4.1").dependencyProject.sourceSets.main.map { it.output })) + testImplementation(project(":testing-common")) testImplementation("org.springframework:spring-webflux:5.0.0.RELEASE") testImplementation("io.projectreactor.ipc:reactor-netty:0.7.0.RELEASE") testImplementation("org.springframework.boot:spring-boot-starter-webflux:2.0.0.RELEASE") testImplementation("org.springframework.boot:spring-boot-starter-test:2.0.0.RELEASE") testImplementation("org.springframework.boot:spring-boot-starter-reactor-netty:2.0.0.RELEASE") - testImplementation("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-semconv:${versions["opentelemetry_semconv"]}") } diff --git a/instrumentation/spring/spring-webflux-5.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/spring/webflux/SpringWebfluxServerTest.java b/instrumentation/spring/spring-webflux-5.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/spring/webflux/SpringWebfluxServerTest.java index 759844ae3..f4b930063 100644 --- a/instrumentation/spring/spring-webflux-5.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/spring/webflux/SpringWebfluxServerTest.java +++ b/instrumentation/spring/spring-webflux-5.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/spring/webflux/SpringWebfluxServerTest.java @@ -18,7 +18,7 @@ import io.opentelemetry.javaagent.instrumentation.hypertrace.spring.webflux.SpringWebFluxTestApplication.GreetingHandler; import io.opentelemetry.javaagent.instrumentation.hypertrace.spring.webflux.SpringWebfluxServerTest.ForceNettyAutoConfiguration; -import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.proto.trace.v1.Span; import java.io.IOException; import java.util.List; import java.util.concurrent.TimeoutException; @@ -26,7 +26,6 @@ import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; -import org.hypertrace.agent.core.instrumentation.HypertraceSemanticAttributes; import org.hypertrace.agent.testing.AbstractInstrumenterTest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -73,29 +72,32 @@ public void get() throws IOException, TimeoutException, InterruptedException { Assertions.assertEquals(200, response.code()); } - List> traces = TEST_WRITER.getTraces(); + List> traces = + TEST_WRITER.waitForSpans( + 1, + span -> + span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT) + || span.getKind().equals(Span.SpanKind.SPAN_KIND_INTERNAL)); TEST_WRITER.waitForTraces(1); Assertions.assertEquals(1, traces.size()); - List trace = traces.get(0); + List trace = traces.get(0); Assertions.assertEquals(1, trace.size()); - SpanData spanData = trace.get(0); + Span span = trace.get(0); Assertions.assertEquals( REQUEST_HEADER_VALUE, - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpRequestHeader(REQUEST_HEADER_NAME))); + TEST_WRITER + .getAttributesMap(span) + .get("http.request.header." + REQUEST_HEADER_NAME) + .getStringValue()); Assertions.assertEquals( SpringWebFluxTestApplication.RESPONSE_HEADER_VALUE, - spanData - .getAttributes() - .get( - HypertraceSemanticAttributes.httpResponseHeader( - SpringWebFluxTestApplication.RESPONSE_HEADER_NAME))); - Assertions.assertNull( - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); - Assertions.assertNull( - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER + .getAttributesMap(span) + .get("http.response.header." + SpringWebFluxTestApplication.RESPONSE_HEADER_NAME) + .getStringValue()); + Assertions.assertNull(TEST_WRITER.getAttributesMap(span).get("http.request.body")); + Assertions.assertNull(TEST_WRITER.getAttributesMap(span).get("http.response.body")); } @Test @@ -124,15 +126,20 @@ public void getStream() throws IOException, TimeoutException, InterruptedExcepti Assertions.assertEquals(responseBodyStr.toString(), response.body().string()); } - List> traces = TEST_WRITER.getTraces(); + List> traces = + TEST_WRITER.waitForSpans( + 1, + span -> + span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT) + || span.getKind().equals(Span.SpanKind.SPAN_KIND_INTERNAL)); TEST_WRITER.waitForTraces(1); Assertions.assertEquals(1, traces.size()); - List trace = traces.get(0); + List trace = traces.get(0); Assertions.assertEquals(1, trace.size()); - SpanData spanData = trace.get(0); + Span span = trace.get(0); Assertions.assertEquals( responseBodyStr.toString(), - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER.getAttributesMap(span).get("http.response.body").getStringValue()); } @Test @@ -149,30 +156,35 @@ public void post() throws IOException, TimeoutException, InterruptedException { Assertions.assertEquals(REQUEST_BODY, response.body().string()); } - List> traces = TEST_WRITER.getTraces(); + List> traces = + TEST_WRITER.waitForSpans( + 1, + span -> + span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT) + || span.getKind().equals(Span.SpanKind.SPAN_KIND_INTERNAL)); TEST_WRITER.waitForTraces(1); Assertions.assertEquals(1, traces.size()); - List trace = traces.get(0); + List trace = traces.get(0); Assertions.assertEquals(1, trace.size()); - SpanData spanData = trace.get(0); + Span span = trace.get(0); Assertions.assertEquals( REQUEST_HEADER_VALUE, - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpRequestHeader(REQUEST_HEADER_NAME))); + TEST_WRITER + .getAttributesMap(span) + .get("http.request.header." + REQUEST_HEADER_NAME) + .getStringValue()); Assertions.assertEquals( SpringWebFluxTestApplication.RESPONSE_HEADER_VALUE, - spanData - .getAttributes() - .get( - HypertraceSemanticAttributes.httpResponseHeader( - SpringWebFluxTestApplication.RESPONSE_HEADER_NAME))); + TEST_WRITER + .getAttributesMap(span) + .get("http.response.header." + SpringWebFluxTestApplication.RESPONSE_HEADER_NAME) + .getStringValue()); Assertions.assertEquals( - REQUEST_BODY, spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); + REQUEST_BODY, TEST_WRITER.getAttributesMap(span).get("http.request.body").getStringValue()); Assertions.assertEquals( REQUEST_BODY, - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER.getAttributesMap(span).get("http.response.body").getStringValue()); } @Test @@ -190,28 +202,31 @@ public void blocking() throws IOException, TimeoutException, InterruptedExceptio Assertions.assertEquals("Hypertrace Blocked Request", response.body().string()); } - List> traces = TEST_WRITER.getTraces(); + List> traces = + TEST_WRITER.waitForSpans( + 1, + span -> + span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT) + || span.getKind().equals(Span.SpanKind.SPAN_KIND_INTERNAL)); TEST_WRITER.waitForTraces(1); Assertions.assertEquals(1, traces.size()); - List trace = traces.get(0); + List trace = traces.get(0); Assertions.assertEquals(1, trace.size()); - SpanData spanData = trace.get(0); + Span span = trace.get(0); Assertions.assertEquals( REQUEST_HEADER_VALUE, - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpRequestHeader(REQUEST_HEADER_NAME))); + TEST_WRITER + .getAttributesMap(span) + .get("http.request.header." + REQUEST_HEADER_NAME) + .getStringValue()); Assertions.assertNull( - spanData - .getAttributes() - .get( - HypertraceSemanticAttributes.httpResponseHeader( - SpringWebFluxTestApplication.RESPONSE_HEADER_NAME))); + TEST_WRITER + .getAttributesMap(span) + .get("http.response.header." + SpringWebFluxTestApplication.RESPONSE_HEADER_NAME)); Assertions.assertNull( - spanData - .getAttributes() - .get( - HypertraceSemanticAttributes.httpResponseHeader(GreetingHandler.DEFAULT_RESPONSE))); + TEST_WRITER + .getAttributesMap(span) + .get("http.response.header." + GreetingHandler.DEFAULT_RESPONSE)); } } diff --git a/instrumentation/struts-2.3/build.gradle.kts b/instrumentation/struts-2.3/build.gradle.kts index e72e7b3cf..04ac3557f 100644 --- a/instrumentation/struts-2.3/build.gradle.kts +++ b/instrumentation/struts-2.3/build.gradle.kts @@ -6,20 +6,13 @@ plugins { val versions: Map by extra dependencies { - testImplementation(project(":instrumentation:servlet:servlet-rw")) - testImplementation(files(project(":instrumentation:servlet:servlet-rw").dependencyProject.sourceSets.main.map { it.output })) - testImplementation(project(":instrumentation:servlet:servlet-3.0")) - testImplementation(files(project(":instrumentation:servlet:servlet-3.0").dependencyProject.sourceSets.main.map { it.output })) - testImplementation(testFixtures(project(":testing-common")) as ProjectDependency) { + testImplementation(project(":testing-common") as ProjectDependency) { exclude(group = "org.eclipse.jetty", module = "jetty-server") } - testImplementation("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-servlet-3.0:${versions["opentelemetry_java_agent"]}") - testRuntimeOnly("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-servlet-common-bootstrap:${versions["opentelemetry_java_agent"]}") testImplementation("org.apache.struts:struts2-core:2.3.1") testImplementation("org.apache.struts:struts2-json-plugin:2.3.1") testImplementation("org.eclipse.jetty:jetty-server:8.0.0.v20110901") testImplementation("org.eclipse.jetty:jetty-servlet:8.0.0.v20110901") testImplementation("javax.servlet:javax.servlet-api:3.0.1") testImplementation("javax.servlet:jsp-api:2.0") - testImplementation("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-semconv:${versions["opentelemetry_semconv"]}") } diff --git a/instrumentation/struts-2.3/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/struts/StrutsInstrumentationTest.java b/instrumentation/struts-2.3/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/struts/StrutsInstrumentationTest.java index 627e3e4f5..8365e41f2 100644 --- a/instrumentation/struts-2.3/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/struts/StrutsInstrumentationTest.java +++ b/instrumentation/struts-2.3/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/struts/StrutsInstrumentationTest.java @@ -18,7 +18,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.proto.trace.v1.Span; import java.io.IOException; import java.util.EnumSet; import java.util.List; @@ -33,7 +33,6 @@ import org.eclipse.jetty.servlet.DefaultServlet; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.util.resource.FileResource; -import org.hypertrace.agent.core.instrumentation.HypertraceSemanticAttributes; import org.hypertrace.agent.testing.AbstractInstrumenterTest; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; @@ -44,8 +43,8 @@ public class StrutsInstrumentationTest extends AbstractInstrumenterTest { private static final String REQUEST_BODY = "hello"; private static final String REQUEST_HEADER = "requestheader"; - private static final String REQUEST_HEADER_VALUE = "requestvalue"; - private static final String RESPONSE_HEADER = "headerName"; + private static final String REQUEST_HEADER_VALUE = "requestValue"; + private static final String RESPONSE_HEADER = "headername"; private static final String RESPONSE_HEADER_VALUE = "headerValue"; private static Server server = new Server(0); @@ -84,16 +83,21 @@ public void postUrlEncoded() throws IOException, TimeoutException, InterruptedEx } TEST_WRITER.waitForTraces(1); - List> traces = TEST_WRITER.getTraces(); + List> traces = + TEST_WRITER.waitForSpans( + 1, + span -> + span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT) + || span.getKind().equals(Span.SpanKind.SPAN_KIND_INTERNAL)); assertEquals(1, traces.size()); - List spans = traces.get(0); + List spans = traces.get(0); assertEquals(1, spans.size()); - SpanData spanData = spans.get(0); + Span span = spans.get(0); assertEquals( "\"" + new Struts2Action().getJsonString() + "\"", - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER.getAttributesMap(span).get("http.response.body").getStringValue()); assertEquals( - REQUEST_BODY, spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); + REQUEST_BODY, TEST_WRITER.getAttributesMap(span).get("http.request.body").getStringValue()); } @Test @@ -109,21 +113,28 @@ public void getHeaders() throws IOException, TimeoutException, InterruptedExcept } TEST_WRITER.waitForTraces(1); - List> traces = TEST_WRITER.getTraces(); + List> traces = + TEST_WRITER.waitForSpans( + 1, + span -> + span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT) + || span.getKind().equals(Span.SpanKind.SPAN_KIND_INTERNAL)); assertEquals(1, traces.size()); - List spans = traces.get(0); + List spans = traces.get(0); assertEquals(1, spans.size()); - SpanData spanData = spans.get(0); + Span span = spans.get(0); assertEquals( RESPONSE_HEADER_VALUE, - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpResponseHeader(RESPONSE_HEADER))); + TEST_WRITER + .getAttributesMap(span) + .get("http.response.header." + RESPONSE_HEADER) + .getStringValue()); assertEquals( REQUEST_HEADER_VALUE, - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpRequestHeader(REQUEST_HEADER))); + TEST_WRITER + .getAttributesMap(span) + .get("http.request.header." + REQUEST_HEADER) + .getStringValue()); } @Test @@ -139,18 +150,19 @@ public void block() throws IOException, TimeoutException, InterruptedException { } TEST_WRITER.waitForTraces(1); - List> traces = TEST_WRITER.getTraces(); + List> traces = + TEST_WRITER.waitForSpans( + 1, + span -> + span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT) + || span.getKind().equals(Span.SpanKind.SPAN_KIND_INTERNAL)); Assertions.assertEquals(1, traces.size()); - List spans = traces.get(0); + List spans = traces.get(0); Assertions.assertEquals(1, spans.size()); - SpanData spanData = spans.get(0); + Span span = spans.get(0); Assertions.assertNull( - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpResponseHeader(RESPONSE_HEADER))); - Assertions.assertNull( - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); - Assertions.assertNull( - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER.getAttributesMap(span).get("http.response.header." + RESPONSE_HEADER)); + Assertions.assertNull(TEST_WRITER.getAttributesMap(span).get("http.request.body")); + Assertions.assertNull(TEST_WRITER.getAttributesMap(span).get("http.response.body")); } } diff --git a/instrumentation/undertow/undertow-1.4/build.gradle.kts b/instrumentation/undertow/undertow-1.4/build.gradle.kts index 5234c9c0e..af029001c 100644 --- a/instrumentation/undertow/undertow-1.4/build.gradle.kts +++ b/instrumentation/undertow/undertow-1.4/build.gradle.kts @@ -29,12 +29,7 @@ dependencies { implementation("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-undertow-1.4:${versions["opentelemetry_java_agent"]}") library("io.undertow:undertow-core:1.4.0.Final") implementation(project(":instrumentation:undertow:undertow-common")) - testImplementation(testFixtures(project(":testing-common"))) + testImplementation(project(":testing-common")) testImplementation("javax.servlet:javax.servlet-api:3.1.0") testImplementation("io.undertow:undertow-servlet:2.0.0.Final") - testImplementation("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-semconv:${versions["opentelemetry_semconv"]}") - testRuntimeOnly(project(":instrumentation:servlet:servlet-3.0")) - testRuntimeOnly(project(":instrumentation:undertow:undertow-servlet-1.4")) - testRuntimeOnly("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-servlet-common-bootstrap:${versions["opentelemetry_java_agent"]}") } - diff --git a/instrumentation/undertow/undertow-1.4/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/undertow/v1_4/UndertowInstrumentationTest.java b/instrumentation/undertow/undertow-1.4/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/undertow/v1_4/UndertowInstrumentationTest.java index 0bd1926c8..bcaeb3af2 100644 --- a/instrumentation/undertow/undertow-1.4/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/undertow/v1_4/UndertowInstrumentationTest.java +++ b/instrumentation/undertow/undertow-1.4/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/undertow/v1_4/UndertowInstrumentationTest.java @@ -19,7 +19,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; -import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.proto.trace.v1.Span; import io.undertow.Handlers; import io.undertow.Undertow; import io.undertow.server.HttpHandler; @@ -44,7 +44,6 @@ import okhttp3.Request.Builder; import okhttp3.RequestBody; import okhttp3.Response; -import org.hypertrace.agent.core.instrumentation.HypertraceSemanticAttributes; import org.hypertrace.agent.testing.AbstractInstrumenterTest; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; @@ -109,16 +108,21 @@ void postUrlEncoded() throws InterruptedException, TimeoutException, IOException } TEST_WRITER.waitForTraces(1); - List> traces = TEST_WRITER.getTraces(); + List> traces = + TEST_WRITER.waitForSpans( + 1, + span -> + span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT) + || span.getKind().equals(Span.SpanKind.SPAN_KIND_INTERNAL)); Assertions.assertEquals(1, traces.size()); - List trace = traces.get(0); + List trace = traces.get(0); Assertions.assertEquals(1, trace.size()); - SpanData spanData = trace.get(0); + Span span = trace.get(0); assertEquals( "{\"echo\": \"bar\"}", - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER.getAttributesMap(span).get("http.response.body").getStringValue()); assertEquals( - requestBody, spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); + requestBody, TEST_WRITER.getAttributesMap(span).get("http.request.body").getStringValue()); // make a second request to an endpoint that should leverage servlet instrumentation try (Response response = @@ -135,17 +139,22 @@ void postUrlEncoded() throws InterruptedException, TimeoutException, IOException } TEST_WRITER.waitForTraces(2); - List> putTraces = TEST_WRITER.getTraces(); + List> putTraces = + TEST_WRITER.waitForSpans( + 2, + span1 -> + span1.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT) + || span1.getKind().equals(Span.SpanKind.SPAN_KIND_INTERNAL)); Assertions.assertEquals(2, putTraces.size()); - List putTrace = putTraces.get(1); + List putTrace = putTraces.get(1); Assertions.assertEquals(1, putTrace.size()); - SpanData putSpanData = putTrace.get(0); + Span putSpan = putTrace.get(0); assertEquals( "echo=bar&append=true", - putSpanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER.getAttributesMap(putSpan).get("http.response.body").getStringValue()); assertEquals( requestBody, - putSpanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); + TEST_WRITER.getAttributesMap(putSpan).get("http.request.body").getStringValue()); // make a third request to an endpoint that should return JSON try (Response response = @@ -160,16 +169,21 @@ void postUrlEncoded() throws InterruptedException, TimeoutException, IOException } TEST_WRITER.waitForTraces(3); - final List> getJsonTraces = TEST_WRITER.getTraces(); + final List> getJsonTraces = + TEST_WRITER.waitForSpans( + 3, + span1 -> + span1.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT) + || span1.getKind().equals(Span.SpanKind.SPAN_KIND_INTERNAL)); Assertions.assertEquals(3, getJsonTraces.size()); - final List getJsonTrace = getJsonTraces.get(2); + final List getJsonTrace = getJsonTraces.get(2); Assertions.assertEquals(1, getJsonTrace.size()); - final SpanData getJsonSpanData = getJsonTrace.get(0); + final Span getJsonSpan = getJsonTrace.get(0); assertEquals( "{\"message\": \"Hello World\"}", - getJsonSpanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER.getAttributesMap(getJsonSpan).get("http.response.body").getStringValue()); // empty request body should not be captured - assertNull(getJsonSpanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); + assertNull(TEST_WRITER.getAttributesMap(getJsonSpan).get("http.request.body")); // make a four request to an endpoint that should return HTML try (Response response = @@ -181,16 +195,20 @@ void postUrlEncoded() throws InterruptedException, TimeoutException, IOException } TEST_WRITER.waitForTraces(4); - final List> getHtmlTraces = TEST_WRITER.getTraces(); + final List> getHtmlTraces = + TEST_WRITER.waitForSpans( + 4, + span1 -> + span1.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT) + || span1.getKind().equals(Span.SpanKind.SPAN_KIND_INTERNAL)); Assertions.assertEquals(4, getHtmlTraces.size()); - final List getHtmlTrace = getHtmlTraces.get(3); + final List getHtmlTrace = getHtmlTraces.get(3); Assertions.assertEquals(1, getHtmlTrace.size()); - final SpanData getHtmlSpanData = getHtmlTrace.get(0); + final Span getHtmlSpan = getHtmlTrace.get(0); // HTML body should not be captured - assertNull( - getHtmlSpanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + assertNull(TEST_WRITER.getAttributesMap(getHtmlSpan).get("http.response.body")); // request body should not be captured - assertNull(getJsonSpanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); + assertNull(TEST_WRITER.getAttributesMap(getHtmlSpan).get("http.request.body")); } public static final class TestServlet extends HttpServlet { diff --git a/instrumentation/vertx/vertx-web-3.0/build.gradle.kts b/instrumentation/vertx/vertx-web-3.0/build.gradle.kts index a4af0a9aa..942e1e732 100644 --- a/instrumentation/vertx/vertx-web-3.0/build.gradle.kts +++ b/instrumentation/vertx/vertx-web-3.0/build.gradle.kts @@ -32,9 +32,7 @@ dependencies { implementation("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-vertx-http-client-common:${versions["opentelemetry_java_agent"]}") library("io.vertx:vertx-web:3.0.0") - testImplementation(testFixtures(project(":testing-common"))) - testImplementation(project(":instrumentation:netty:netty-4.0")) - testImplementation(files(project(":instrumentation:netty:netty-4.0").dependencyProject.sourceSets.main.map { it.output })) + testImplementation(project(":testing-common")) testImplementation("io.netty:netty-codec-http:${nettyVersion}") { version { @@ -67,4 +65,3 @@ dependencies { } } } - diff --git a/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/ResponseBodyWrappingHandler.java b/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/ResponseBodyWrappingHandler.java index 06adf1f4a..2ee406c01 100644 --- a/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/ResponseBodyWrappingHandler.java +++ b/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/ResponseBodyWrappingHandler.java @@ -38,21 +38,7 @@ public class ResponseBodyWrappingHandler implements Handler { private static final Logger log = LoggerFactory.getLogger(ResponseBodyWrappingHandler.class); private static Method getAttribute = null; - - static { - try { - getAttribute = - Class.forName("io.opentelemetry.sdk.trace.SdkSpan") - .getDeclaredMethod("getAttribute", AttributeKey.class); - } catch (NoSuchMethodException e) { - log.error("getAttribute method not found in SdkSpan class", e); - } catch (ClassNotFoundException e) { - log.error("SdkSpan class not found", e); - } - if (getAttribute != null) { - getAttribute.setAccessible(true); - } - } + private static boolean shouldCallGetAttribute = true; private final Handler wrapped; private final Span span; @@ -75,8 +61,21 @@ public void handle(Buffer event) { .setAttribute(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY, responseBody); // Also add content type if present + if (shouldCallGetAttribute && getAttribute == null) { + try { + getAttribute = span.getClass().getDeclaredMethod("getAttribute", AttributeKey.class); + } catch (NoSuchMethodException e) { + log.error("getAttribute method not found in SdkSpan class", e); + } + if (getAttribute != null) { + getAttribute.setAccessible(true); + } + // only try once and set the value, + // if not found, do not attempt to try again + shouldCallGetAttribute = false; + } if (getAttribute != null - && span.getClass().getName().equals("io.opentelemetry.sdk.trace.SdkSpan")) { + && span.getClass().getName().contains("io.opentelemetry.sdk.trace.SdkSpan")) { try { Object resContentType = getAttribute.invoke( diff --git a/instrumentation/vertx/vertx-web-3.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/VertxClientInstrumentationTest.java b/instrumentation/vertx/vertx-web-3.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/VertxClientInstrumentationTest.java index 29b5ae2f6..efe1f1875 100644 --- a/instrumentation/vertx/vertx-web-3.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/VertxClientInstrumentationTest.java +++ b/instrumentation/vertx/vertx-web-3.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/VertxClientInstrumentationTest.java @@ -16,7 +16,7 @@ package io.opentelemetry.javaagent.instrumentation.hypertrace.vertx; -import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.proto.trace.v1.Span; import io.vertx.core.Handler; import io.vertx.core.Vertx; import io.vertx.core.VertxOptions; @@ -30,7 +30,6 @@ import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeoutException; -import org.hypertrace.agent.core.instrumentation.HypertraceSemanticAttributes; import org.hypertrace.agent.testing.AbstractHttpClientTest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -141,12 +140,17 @@ public void postJson_write_end() throws TimeoutException, InterruptedException { countDownLatch.await(); TEST_WRITER.waitForTraces(1); - List> traces = TEST_WRITER.getTraces(); + List> traces = + TEST_WRITER.waitForSpans( + 1, + span -> + span.getKind().equals(Span.SpanKind.SPAN_KIND_SERVER) + || span.getKind().equals(Span.SpanKind.SPAN_KIND_INTERNAL)); Assertions.assertEquals(1, traces.size()); - SpanData clientSpan = traces.get(0).get(0); + Span clientSpan = traces.get(0).get(0); Assertions.assertEquals( "write buffer str_encoding ", - clientSpan.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); + TEST_WRITER.getAttributesMap(clientSpan).get("http.request.body").getStringValue()); } @Test @@ -169,12 +173,17 @@ public void postJson_write_end_string() throws TimeoutException, InterruptedExce countDownLatch.await(); TEST_WRITER.waitForTraces(1); - List> traces = TEST_WRITER.getTraces(); + List> traces = + TEST_WRITER.waitForSpans( + 1, + span -> + span.getKind().equals(Span.SpanKind.SPAN_KIND_SERVER) + || span.getKind().equals(Span.SpanKind.SPAN_KIND_INTERNAL)); Assertions.assertEquals(1, traces.size(), String.format("was: %d", traces.size())); - SpanData clientSpan = traces.get(0).get(0); + Span clientSpan = traces.get(0).get(0); Assertions.assertEquals( "write buffer str_encoding end", - clientSpan.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); + TEST_WRITER.getAttributesMap(clientSpan).get("http.request.body").getStringValue()); } @Test @@ -197,11 +206,20 @@ public void postJson_write_end_buffer() throws TimeoutException, InterruptedExce countDownLatch.await(); TEST_WRITER.waitForTraces(1); - List> traces = TEST_WRITER.getTraces(); + List> traces = + TEST_WRITER.waitForSpans( + 1, + span -> + !span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT) + || span.getAttributesList().stream() + .noneMatch( + keyValue -> + keyValue.getKey().equals("http.url") + && keyValue.getValue().getStringValue().contains("/echo"))); Assertions.assertEquals(1, traces.size(), String.format("was: %d", traces.size())); - SpanData clientSpan = traces.get(0).get(0); + Span clientSpan = traces.get(0).get(0); Assertions.assertEquals( "write buffer str_encoding end", - clientSpan.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); + TEST_WRITER.getAttributesMap(clientSpan).get("http.request.body").getStringValue()); } } diff --git a/instrumentation/vertx/vertx-web-3.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/VertxServerInstrumentationTest.java b/instrumentation/vertx/vertx-web-3.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/VertxServerInstrumentationTest.java index c2579eba2..7f33390bd 100644 --- a/instrumentation/vertx/vertx-web-3.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/VertxServerInstrumentationTest.java +++ b/instrumentation/vertx/vertx-web-3.0/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/VertxServerInstrumentationTest.java @@ -16,7 +16,7 @@ package io.opentelemetry.javaagent.instrumentation.hypertrace.vertx; -import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.proto.trace.v1.Span; import io.vertx.core.DeploymentOptions; import io.vertx.core.Vertx; import io.vertx.core.VertxOptions; @@ -32,7 +32,6 @@ import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; -import org.hypertrace.agent.core.instrumentation.HypertraceSemanticAttributes; import org.hypertrace.agent.testing.AbstractInstrumenterTest; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; @@ -108,28 +107,27 @@ public void get() throws IOException, TimeoutException, InterruptedException { Assertions.assertEquals(200, response.code()); } - List> traces = TEST_WRITER.getTraces(); TEST_WRITER.waitForTraces(1); + List> traces = + TEST_WRITER.waitForSpans(1, span -> span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT)); Assertions.assertEquals(1, traces.size()); - List trace = traces.get(0); + List trace = traces.get(0); Assertions.assertEquals(1, trace.size()); - SpanData spanData = trace.get(0); - Assertions.assertNull( - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); + Span span = trace.get(0); + Assertions.assertNull(TEST_WRITER.getAttributesMap(span).get("http.request.body")); Assertions.assertEquals( REQUEST_HEADER_VALUE, - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpRequestHeader(REQUEST_HEADER_NAME))); - Assertions.assertNull( - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER + .getAttributesMap(span) + .get("http.request.header." + REQUEST_HEADER_NAME) + .getStringValue()); + Assertions.assertNull(TEST_WRITER.getAttributesMap(span).get("http.response.body")); Assertions.assertEquals( VertxWebServer.RESPONSE_HEADER_VALUE, - spanData - .getAttributes() - .get( - HypertraceSemanticAttributes.httpResponseHeader( - VertxWebServer.RESPONSE_HEADER_NAME))); + TEST_WRITER + .getAttributesMap(span) + .get("http.response.header." + VertxWebServer.RESPONSE_HEADER_NAME) + .getStringValue()); } @Test @@ -147,28 +145,25 @@ public void blocking() throws IOException, TimeoutException, InterruptedExceptio Assertions.assertEquals("Hypertrace Blocked Request", response.body().string()); } - List> traces = TEST_WRITER.getTraces(); TEST_WRITER.waitForTraces(1); + List> traces = + TEST_WRITER.waitForSpans(1, span -> span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT)); Assertions.assertEquals(1, traces.size()); - List trace = traces.get(0); + List trace = traces.get(0); Assertions.assertEquals(1, trace.size()); - SpanData spanData = trace.get(0); + Span span = trace.get(0); Assertions.assertEquals( REQUEST_HEADER_VALUE, - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpRequestHeader(REQUEST_HEADER_NAME))); - Assertions.assertNull( - spanData - .getAttributes() - .get( - HypertraceSemanticAttributes.httpResponseHeader( - VertxWebServer.RESPONSE_HEADER_NAME))); + TEST_WRITER + .getAttributesMap(span) + .get("http.request.header." + REQUEST_HEADER_NAME) + .getStringValue()); Assertions.assertNull( - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpResponseHeader(VertxWebServer.RESPONSE_BODY))); + TEST_WRITER + .getAttributesMap(span) + .get("http.response.header." + VertxWebServer.RESPONSE_HEADER_NAME)); + Assertions.assertNull(TEST_WRITER.getAttributesMap(span).get("http.response.body")); } public void postJson(String url) throws IOException, TimeoutException, InterruptedException { @@ -183,28 +178,29 @@ public void postJson(String url) throws IOException, TimeoutException, Interrupt Assertions.assertEquals(VertxWebServer.RESPONSE_BODY, response.body().string()); } - List> traces = TEST_WRITER.getTraces(); + List> traces = + TEST_WRITER.waitForSpans(1, span -> span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT)); TEST_WRITER.waitForTraces(1); Assertions.assertEquals(1, traces.size()); - List trace = traces.get(0); + List trace = traces.get(0); Assertions.assertEquals(1, trace.size()); - SpanData spanData = trace.get(0); + Span span = trace.get(0); Assertions.assertEquals( - REQUEST_BODY, spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); + REQUEST_BODY, TEST_WRITER.getAttributesMap(span).get("http.request.body").getStringValue()); Assertions.assertEquals( REQUEST_HEADER_VALUE, - spanData - .getAttributes() - .get(HypertraceSemanticAttributes.httpRequestHeader(REQUEST_HEADER_NAME))); + TEST_WRITER + .getAttributesMap(span) + .get("http.request.header." + REQUEST_HEADER_NAME) + .getStringValue()); Assertions.assertEquals( VertxWebServer.RESPONSE_BODY, - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER.getAttributesMap(span).get("http.response.body").getStringValue()); Assertions.assertEquals( VertxWebServer.RESPONSE_HEADER_VALUE, - spanData - .getAttributes() - .get( - HypertraceSemanticAttributes.httpResponseHeader( - VertxWebServer.RESPONSE_HEADER_NAME))); + TEST_WRITER + .getAttributesMap(span) + .get("http.response.header." + VertxWebServer.RESPONSE_HEADER_NAME) + .getStringValue()); } } diff --git a/otel-extensions/build.gradle.kts b/otel-extensions/build.gradle.kts index a752d48cb..a87b831ca 100644 --- a/otel-extensions/build.gradle.kts +++ b/otel-extensions/build.gradle.kts @@ -30,10 +30,10 @@ dependencies { api(project(":filter-api")) compileOnly("io.opentelemetry:opentelemetry-sdk:${versions["opentelemetry"]}") - compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure:${versions["opentelemetry"]}-alpha") + compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure:${versions["opentelemetry"]}") compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:${versions["opentelemetry"]}") - implementation("io.opentelemetry:opentelemetry-semconv:${versions["opentelemetry"]}-alpha") + implementation("io.opentelemetry.semconv:opentelemetry-semconv:${versions["opentelemetry_semconv"]}") implementation("io.opentelemetry.javaagent:opentelemetry-javaagent-extension-api:${versions["opentelemetry_java_agent"]}") implementation("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api:${versions["opentelemetry"]}") implementation("io.opentelemetry.javaagent:opentelemetry-javaagent-tooling:${versions["opentelemetry_java_agent-tooling"]}") { @@ -51,7 +51,6 @@ dependencies { implementation("org.slf4j:slf4j-api:${versions["slf4j"]}") compileOnly("com.google.auto.service:auto-service-annotations:1.0") - implementation("net.bytebuddy:byte-buddy-dep:${versions["byte_buddy"]}") annotationProcessor("com.google.auto.service:auto-service:1.0") @@ -65,6 +64,6 @@ dependencies { api("com.google.code.gson:gson:2.8.9") } - testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure:${versions["opentelemetry"]}-alpha") + testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure:${versions["opentelemetry"]}") testImplementation("io.opentelemetry:opentelemetry-sdk:${versions["opentelemetry"]}") } diff --git a/otel-extensions/src/main/java/org/hypertrace/agent/otel/extensions/HypertracePropertySource.java b/otel-extensions/src/main/java/org/hypertrace/agent/otel/extensions/HypertracePropertySource.java new file mode 100644 index 000000000..c4a1a6251 --- /dev/null +++ b/otel-extensions/src/main/java/org/hypertrace/agent/otel/extensions/HypertracePropertySource.java @@ -0,0 +1,85 @@ +/* + * Copyright The Hypertrace Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.hypertrace.agent.otel.extensions; + +import com.google.auto.service.AutoService; +import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer; +import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +@AutoService(AutoConfigurationCustomizerProvider.class) +public final class HypertracePropertySource implements AutoConfigurationCustomizerProvider { + + /** + * Add all the properties needed at runtime by default. We have added the properties to disable + * all instrumentations by default. Only enable the ones we support. For testing these, add all + * the following properties in static block in + * org.hypertrace.agent.testing.AbstractInstrumenterTest in hypertrace javaagent repo and run all + * the tests in hypertrace javaagent instrumentation. + */ + public Map getProperties() { + final Map configProperties = new HashMap<>(); + // Make everything off by default + configProperties.put("otel.instrumentation.common.default-enabled", "false"); + + // Enable the server instrumentations that we support(and need) in hypertrace + configProperties.put("otel.instrumentation.netty.enabled", "true"); + configProperties.put("otel.instrumentation.servlet.enabled", "true"); + configProperties.put("otel.instrumentation.vertx-web.enabled", "true"); + configProperties.put("otel.instrumentation.undertow.enabled", "true"); + configProperties.put("otel.instrumentation.grpc.enabled", "true"); + + // Enable the client instrumentations that we support(and need) in hypertrace + configProperties.put("otel.instrumentation.apache-httpasyncclient.enabled", "true"); + configProperties.put("otel.instrumentation.apache-httpclient.enabled", "true"); + configProperties.put("otel.instrumentation.okhttp.enabled", "true"); + configProperties.put("otel.instrumentation.http-url-connection.enabled", "true"); + configProperties.put("otel.instrumentation.vertx.enabled", "true"); + + // Enable the db instrumentations + configProperties.put("otel.instrumentation.apache-dbcp.enabled", "true"); + configProperties.put("otel.instrumentation.jdbc.enabled", "true"); + configProperties.put("otel.instrumentation.jdbc-datasource.enabled", "true"); + configProperties.put("otel.instrumentation.mongo.enabled", "true"); + configProperties.put("otel.instrumentation.r2dbc.enabled", "true"); + configProperties.put("otel.instrumentation.tomcat-jdbc.enabled", "true"); + configProperties.put("otel.instrumentation.vibur-dbcp.enabled", "true"); + + // Enable all hypertrace instrumentations + configProperties.put("otel.instrumentation.inputstream.enabled", "true"); + configProperties.put("otel.instrumentation.outputstream.enabled", "true"); + configProperties.put("otel.instrumentation.ht.enabled", "true"); + + // Enable otel specific instrumentations + configProperties.put("otel.instrumentation.methods.enabled", "true"); + configProperties.put("otel.instrumentation.external-annotations.enabled", "true"); + configProperties.put( + "otel.instrumentation.opentelemetry-extension-annotations.enabled", "true"); + configProperties.put( + "otel.instrumentation.opentelemetry-instrumentation-annotations.enabled", "true"); + configProperties.put("otel.instrumentation.opentelemetry-api.enabled", "true"); + + return Collections.unmodifiableMap(configProperties); + } + + @Override + public void customize(AutoConfigurationCustomizer autoConfigurationCustomizer) { + autoConfigurationCustomizer.addPropertiesSupplier(this::getProperties); + } +} diff --git a/otel-extensions/src/main/java/org/hypertrace/agent/otel/extensions/HypertraceResourceProvider.java b/otel-extensions/src/main/java/org/hypertrace/agent/otel/extensions/HypertraceResourceProvider.java index cbb81cd2e..b92f12691 100644 --- a/otel-extensions/src/main/java/org/hypertrace/agent/otel/extensions/HypertraceResourceProvider.java +++ b/otel-extensions/src/main/java/org/hypertrace/agent/otel/extensions/HypertraceResourceProvider.java @@ -22,7 +22,7 @@ import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider; import io.opentelemetry.sdk.resources.Resource; -import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; +import io.opentelemetry.semconv.ResourceAttributes; import org.hypertrace.agent.config.v1.Config.AgentConfig; import org.hypertrace.agent.otel.extensions.config.HypertraceConfig; import org.slf4j.Logger; diff --git a/otel-extensions/src/main/java/org/hypertrace/agent/otel/extensions/processor/AddTagsSpanProcessor.java b/otel-extensions/src/main/java/org/hypertrace/agent/otel/extensions/processor/AddTagsSpanProcessor.java index 06356b6ee..175598d06 100644 --- a/otel-extensions/src/main/java/org/hypertrace/agent/otel/extensions/processor/AddTagsSpanProcessor.java +++ b/otel-extensions/src/main/java/org/hypertrace/agent/otel/extensions/processor/AddTagsSpanProcessor.java @@ -21,7 +21,7 @@ import io.opentelemetry.sdk.trace.ReadWriteSpan; import io.opentelemetry.sdk.trace.ReadableSpan; import io.opentelemetry.sdk.trace.SpanProcessor; -import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; +import io.opentelemetry.semconv.ResourceAttributes; import org.hypertrace.agent.otel.extensions.CgroupsReader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/settings.gradle.kts b/settings.gradle.kts index 2edeb428b..abe38b05d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -66,3 +66,4 @@ include("instrumentation:undertow:undertow-1.4") findProject(":instrumentation:undertow:undertow-1.4")?.name = "undertow-1.4" include("instrumentation:undertow:undertow-servlet-1.4") findProject(":instrumentation:undertow:undertow-servlet-1.4")?.name = "undertow-servlet-1.4" +include("tests-extension") diff --git a/smoke-tests/build.gradle.kts b/smoke-tests/build.gradle.kts index 6de59095c..2ddd12ea9 100644 --- a/smoke-tests/build.gradle.kts +++ b/smoke-tests/build.gradle.kts @@ -10,7 +10,7 @@ apply { val versions: Map by extra dependencies{ - testImplementation(testFixtures(project(":testing-common"))) + testImplementation(project(":testing-common")) testImplementation(project(":javaagent-core")) testImplementation("org.testcontainers:testcontainers:1.15.2") testImplementation("com.squareup.okhttp3:okhttp:4.9.0") @@ -26,7 +26,7 @@ dependencies{ testImplementation("info.solidsoft.spock:spock-global-unroll:0.5.1") testImplementation("com.fasterxml.jackson.core:jackson-databind:2.16.0") testImplementation("org.codehaus.groovy:groovy-all:2.5.11") - testImplementation("io.opentelemetry:opentelemetry-semconv:${versions["opentelemetry"]}-alpha") + testImplementation("io.opentelemetry.semconv:opentelemetry-semconv:${versions["opentelemetry_semconv"]}") } tasks.test { diff --git a/smoke-tests/src/test/groovy/org/hypertrace/agent/smoketest/AppServerTest.groovy b/smoke-tests/src/test/groovy/org/hypertrace/agent/smoketest/AppServerTest.groovy index e98de3e9f..c203ae544 100644 --- a/smoke-tests/src/test/groovy/org/hypertrace/agent/smoketest/AppServerTest.groovy +++ b/smoke-tests/src/test/groovy/org/hypertrace/agent/smoketest/AppServerTest.groovy @@ -5,16 +5,10 @@ package org.hypertrace.agent.smoketest -import io.opentelemetry.proto.common.v1.KeyValue import okhttp3.MediaType import okhttp3.RequestBody -import spock.lang.Ignore import spock.lang.IgnoreIf -import java.util.function.Function -import java.util.function.Predicate -import java.util.stream.Stream - import static org.junit.Assume.assumeTrue import io.opentelemetry.proto.trace.v1.Span @@ -171,126 +165,6 @@ abstract class AppServerTest extends SmokeTest { [appServer, jdk] << getTestParams() } - @Unroll - def "#appServer test static file found on JDK #jdk"(String appServer, String jdk) { - def port = target.getMappedPort(8080) - String url = "http://localhost:${port}/app/hello.txt" - def request = new Request.Builder().url(url).get().build() - - when: - def response = CLIENT.newCall(request).execute() - TraceInspector traces = new TraceInspector(waitForTraces()) - Set traceIds = traces.traceIds - String responseBody = response.body().string() - - then: "There is one trace" - traceIds.size() == 1 - - and: "Response contains Hello" - responseBody.contains("Hello") - - and: "There is one server span" - traces.countSpansByKind(Span.SpanKind.SPAN_KIND_SERVER) == 1 - - and: "Expected span names" - traces.countSpansByName(getSpanName('/app/hello.txt')) == 1 - - and: "The span for the initial web request" - traces.countFilteredAttributes("http.scheme", "http") == 1 - traces.countFilteredAttributes("http.target", "/app/hello.txt") == 1 - traces.filterSpansByAttributes(traces.filterSpansByAttributes(traces.getSpanStream(), "net.host.name", "localhost", "string"), "net.host.port", port, "int").count() == 1 || - traces.filterSpansByAttributes(traces.filterSpansByAttributes(traces.getSpanStream(), "net.host.name", "localhost", "string"), "net.sock.host.port", port, "int").count() == 1 - - - cleanup: - response?.close() - - where: - [appServer, jdk] << getTestParams() - } - - @Unroll - def "#appServer test static file not found on JDK #jdk"(String appServer, String jdk) { - def port = target.getMappedPort(8080) - String url = "http://localhost:${port}/app/file-that-does-not-exist" - def request = new Request.Builder().url(url).get().build() - - when: - def response = CLIENT.newCall(request).execute() - TraceInspector traces = new TraceInspector(waitForTraces()) - Set traceIds = traces.traceIds - - then: "There is one trace" - traceIds.size() == 1 - - and: "Response code is 404" - response.code() == 404 - - and: "There is one server span" - traces.countSpansByKind(Span.SpanKind.SPAN_KIND_SERVER) == 1 - - and: "Expected span names" - traces.countSpansByName(getSpanName('/app/file-that-does-not-exist')) == 1 - - and: "The span for the initial web request" - traces.countFilteredAttributes("http.scheme", "http") == 1 - traces.countFilteredAttributes("http.target", "/app/file-that-does-not-exist") == 1 - traces.filterSpansByAttributes(traces.filterSpansByAttributes(traces.getSpanStream(), "net.host.name", "localhost", "string"), "net.host.port", port, "int").count() == 1 || - traces.filterSpansByAttributes(traces.filterSpansByAttributes(traces.getSpanStream(), "net.host.name", "localhost", "string"), "net.sock.host.port", port, "int").count() == 1 - - - cleanup: - response?.close() - - where: - [appServer, jdk] << getTestParams() - } - - @Unroll - @IgnoreIf({ System.getProperty("os.name").contains("windows") }) - def "#appServer test request for WEB-INF/web.xml on JDK #jdk"(String appServer, String jdk) { - // TODO https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/2499 - if (getTargetImage(appServer, jdk).toLowerCase().contains("wildfly")) { - return - } - - assumeTrue(testRequestWebInfWebXml()) - - def port = target.getMappedPort(8080) - String url = "http://localhost:${ port}/app/WEB-INF/web.xml" - def request = new Request.Builder().url(url).get().build() - - when: - def response = CLIENT.newCall(request).execute() - TraceInspector traces = new TraceInspector(waitForTraces()) - Set traceIds = traces.traceIds - - then: "There is one trace" - traceIds.size() == 1 - - and: "Response code is 404" - response.code() == 404 - - and: "There is one server span" - traces.countSpansByKind(Span.SpanKind.SPAN_KIND_SERVER) == 1 - - and: "Expected span names" - traces.countSpansByName(getSpanName('/app/WEB-INF/web.xml')) == 1 - - and: "The span for the initial web request" - traces.countFilteredAttributes("http.scheme", "http") == 1 - traces.countFilteredAttributes("http.target", "/app/WEB-INF/web.xml") == 1 - traces.filterSpansByAttributes(traces.filterSpansByAttributes(traces.getSpanStream(), "net.host.name", "localhost", "string"), "net.host.port", port, "int").count() == 1 || - traces.filterSpansByAttributes(traces.filterSpansByAttributes(traces.getSpanStream(), "net.host.name", "localhost", "string"), "net.sock.host.port", port, "int").count() == 1 - - - cleanup: - response?.close() - - where: - [appServer, jdk] << getTestParams() - } - @Unroll def "#appServer test request with error JDK #jdk"(String appServer, String jdk) { assumeTrue(testException()) @@ -333,48 +207,6 @@ abstract class AppServerTest extends SmokeTest { [appServer, jdk] << getTestParams() } - @Unroll - def "#appServer test request outside deployed application JDK #jdk"(String appServer, String jdk) { - // TODO https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/2499 - if (getTargetImage(appServer, jdk).toLowerCase().contains("wildfly")) { - return - } - - - def port = target.getMappedPort(8080) - String url = "http://localhost:${port}/this-is-definitely-not-there-but-there-should-be-a-trace-nevertheless" - def request = new Request.Builder().url(url).get().build() - - when: - def response = CLIENT.newCall(request).execute() - TraceInspector traces = new TraceInspector(waitForTraces()) - Set traceIds = traces.traceIds - - then: "There is one trace" - traceIds.size() == 1 - - and: "Response code is 404" - response.code() == 404 - - and: "There is one server span" - traces.countSpansByKind(Span.SpanKind.SPAN_KIND_SERVER) == 1 - - and: "Expected span names" - traces.countSpansByName(getSpanName('/this-is-definitely-not-there-but-there-should-be-a-trace-nevertheless')) == 1 - - and: "The span for the initial web request" - traces.countFilteredAttributes("http.scheme", "http") == 1 - traces.countFilteredAttributes("http.target", "/this-is-definitely-not-there-but-there-should-be-a-trace-nevertheless") == 1 - traces.filterSpansByAttributes(traces.filterSpansByAttributes(traces.getSpanStream(), "net.host.name", "localhost", "string"), "net.host.port", 8080, "int").count() == 1 || - traces.filterSpansByAttributes(traces.filterSpansByAttributes(traces.getSpanStream(), "net.host.name", "localhost", "string"), "net.sock.host.port", 8080, "int").count() == 1 - - cleanup: - response?.close() - - where: - [appServer, jdk] << getTestParams() - } - @Unroll def "#appServer async smoke test on JDK #jdk"(String appServer, String jdk) { assumeTrue(testAsyncSmoke()) diff --git a/smoke-tests/src/test/groovy/org/hypertrace/agent/smoketest/PlaySmokeTest.groovy b/smoke-tests/src/test/groovy/org/hypertrace/agent/smoketest/PlaySmokeTest.groovy deleted file mode 100644 index 152377552..000000000 --- a/smoke-tests/src/test/groovy/org/hypertrace/agent/smoketest/PlaySmokeTest.groovy +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.hypertrace.agent.smoketest - -import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest -import okhttp3.Request - -class PlaySmokeTest extends SmokeTest { - - protected String getTargetImage(String jdk, String serverVersion) { - "ghcr.io/open-telemetry/java-test-containers:smoke-play-jdk$jdk-20201128.1734635" - } - - def "play smoke test on JDK #jdk"(int jdk) { - setup: - startTarget(jdk) - String url = "http://localhost:${target.getMappedPort(8080)}/welcome?id=1" - def request = new Request.Builder().url(url).get().build() - - when: - def response = CLIENT.newCall(request).execute() - Collection traces = waitForTraces() - - then: - response.body().string() == "Welcome 1." - - // Play produces one Internal span and akka produces a server span - countSpansByName(traces, '/welcome') == 1 // play Internal span - countSpansByName(traces, 'GET /welcome') == 1 // akka Server span - - cleanup: - stopTarget() - - where: - jdk << [8, 11, 15] - } - -} diff --git a/smoke-tests/src/test/groovy/org/hypertrace/agent/smoketest/SmokeTest.groovy b/smoke-tests/src/test/groovy/org/hypertrace/agent/smoketest/SmokeTest.groovy index a394924b0..73774acf0 100644 --- a/smoke-tests/src/test/groovy/org/hypertrace/agent/smoketest/SmokeTest.groovy +++ b/smoke-tests/src/test/groovy/org/hypertrace/agent/smoketest/SmokeTest.groovy @@ -105,7 +105,7 @@ abstract class SmokeTest extends Specification { def cleanup() { CLIENT.newCall(new Request.Builder() - .url("http://localhost:${backend.getMappedPort(8080)}/clear-requests") + .url("http://localhost:${backend.getMappedPort(8080)}/clear") .build()) .execute() .close() @@ -135,13 +135,13 @@ abstract class SmokeTest extends Specification { protected static Stream getSpanStream(Collection traces) { return traces.stream() .flatMap { it.getResourceSpansList().stream() } - .flatMap { it.getInstrumentationLibrarySpansList().stream() } + .flatMap { it.getScopeSpansList().stream() } .flatMap { it.getSpansList().stream() } } protected Collection waitForTraces() { def content = waitForContent() - + println(content); return OBJECT_MAPPER.readTree(content).collect { def builder = ExportTraceServiceRequest.newBuilder() // TODO(anuraaga): Register parser into object mapper to avoid de -> re -> deserialize. @@ -156,7 +156,7 @@ abstract class SmokeTest extends Specification { String content = "[]" while (System.currentTimeMillis() < deadline) { def body = content = CLIENT.newCall(new Request.Builder() - .url("http://localhost:${backend.getMappedPort(8080)}/get-requests") + .url("http://localhost:${backend.getMappedPort(8080)}/get-traces") .build()) .execute() .body() @@ -246,7 +246,7 @@ abstract class SmokeTest extends Specification { started = true Runtime.addShutdownHook { stop() } - backend = new GenericContainer<>("ghcr.io/open-telemetry/java-test-containers:smoke-fake-backend-20201128.1734635") + backend = new GenericContainer<>("ghcr.io/open-telemetry/opentelemetry-java-instrumentation/smoke-test-fake-backend:20221127.3559314891") .withExposedPorts(8080) .waitingFor(Wait.forHttp("/health").forPort(8080)) .withNetwork(network) @@ -255,7 +255,7 @@ abstract class SmokeTest extends Specification { .withLogConsumer(new Slf4jLogConsumer(LoggerFactory.getLogger("smoke.tests.backend"))) backend.start() - collector = new GenericContainer<>("otel/opentelemetry-collector:0.21.0") + collector = new GenericContainer<>("otel/opentelemetry-collector:0.96.0") .dependsOn(backend) .withNetwork(network) .withNetworkAliases("collector") diff --git a/smoke-tests/src/test/groovy/org/hypertrace/agent/smoketest/TraceInspector.java b/smoke-tests/src/test/groovy/org/hypertrace/agent/smoketest/TraceInspector.java index d9b6ac022..254e6ff68 100644 --- a/smoke-tests/src/test/groovy/org/hypertrace/agent/smoketest/TraceInspector.java +++ b/smoke-tests/src/test/groovy/org/hypertrace/agent/smoketest/TraceInspector.java @@ -37,7 +37,7 @@ public TraceInspector(Collection traces) { public Stream getSpanStream() { return traces.stream() .flatMap(it -> it.getResourceSpansList().stream()) - .flatMap(it -> it.getInstrumentationLibrarySpansList().stream()) + .flatMap(it -> it.getScopeSpansList().stream()) .flatMap(it -> it.getSpansList().stream()); } diff --git a/smoke-tests/src/test/java/org/hypertrace/agent/smoketest/AbstractSmokeTest.java b/smoke-tests/src/test/java/org/hypertrace/agent/smoketest/AbstractSmokeTest.java index 70aaf4910..6360902cc 100644 --- a/smoke-tests/src/test/java/org/hypertrace/agent/smoketest/AbstractSmokeTest.java +++ b/smoke-tests/src/test/java/org/hypertrace/agent/smoketest/AbstractSmokeTest.java @@ -20,7 +20,7 @@ import com.google.protobuf.util.JsonFormat; import io.opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceRequest; import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest; -import io.opentelemetry.proto.trace.v1.InstrumentationLibrarySpans; +import io.opentelemetry.proto.trace.v1.ScopeSpans; import io.opentelemetry.proto.trace.v1.Span; import java.io.IOException; import java.time.Duration; @@ -54,9 +54,9 @@ public abstract class AbstractSmokeTest { private static final Logger log = LoggerFactory.getLogger(OpenTelemetryStorage.class); - private static final String OTEL_COLLECTOR_IMAGE = "otel/opentelemetry-collector:0.21.0"; + private static final String OTEL_COLLECTOR_IMAGE = "otel/opentelemetry-collector:0.96.0"; private static final String MOCK_BACKEND_IMAGE = - "ghcr.io/open-telemetry/opentelemetry-java-instrumentation/smoke-test-fake-backend:20210918.1248928123"; + "ghcr.io/open-telemetry/opentelemetry-java-instrumentation/smoke-test-fake-backend:20221127.3559314891"; private static final String NETWORK_ALIAS_OTEL_COLLECTOR = "collector"; private static final String NETWORK_ALIAS_OTEL_MOCK_STORAGE = "backend"; private static final String OTEL_EXPORTER_ENDPOINT = @@ -160,11 +160,11 @@ protected static Stream getSpanStream(Collection librarySpans.getSpansList().stream()); } - protected static Stream getInstrumentationLibSpanStream( + protected static Stream getInstrumentationLibSpanStream( Collection traceRequest) { return traceRequest.stream() .flatMap(request -> request.getResourceSpansList().stream()) - .flatMap(resourceSpans -> resourceSpans.getInstrumentationLibrarySpansList().stream()); + .flatMap(resourceSpans -> resourceSpans.getScopeSpansList().stream()); } protected Collection waitForTraces(final int count) { @@ -237,7 +237,7 @@ protected boolean hasMetricNamed( String metricName, Collection metricRequests) { return metricRequests.stream() .flatMap(metricRequest -> metricRequest.getResourceMetricsList().stream()) - .flatMap(resourceMetrics -> resourceMetrics.getInstrumentationLibraryMetricsList().stream()) + .flatMap(resourceMetrics -> resourceMetrics.getScopeMetricsList().stream()) .flatMap( instrumentationLibraryMetrics -> instrumentationLibraryMetrics.getMetricsList().stream()) diff --git a/smoke-tests/src/test/java/org/hypertrace/agent/smoketest/SpringBootDisabledBodyCaptureSmokeTest.java b/smoke-tests/src/test/java/org/hypertrace/agent/smoketest/SpringBootDisabledBodyCaptureSmokeTest.java index 0c77b9ee1..a88079d3f 100644 --- a/smoke-tests/src/test/java/org/hypertrace/agent/smoketest/SpringBootDisabledBodyCaptureSmokeTest.java +++ b/smoke-tests/src/test/java/org/hypertrace/agent/smoketest/SpringBootDisabledBodyCaptureSmokeTest.java @@ -77,16 +77,12 @@ public void get() throws IOException { .getMainAttributes() .get(OTEL_INSTRUMENTATION_VERSION_MANIFEST_PROP); - Assertions.assertEquals(1, countSpansByName(traces, "GET /greeting")); - Assertions.assertEquals(1, countSpansByName(traces, "WebController.greeting")); + Assertions.assertEquals(1, countSpansByName(traces, "GET /*")); + Assertions.assertEquals(0, countSpansByName(traces, "WebController.greeting")); Assertions.assertTrue( getInstrumentationLibSpanStream(traces) .anyMatch( - instLibSpan -> - instLibSpan - .getInstrumentationLibrary() - .getVersion() - .equals(currentAgentVersion))); + instLibSpan -> instLibSpan.getScope().getVersion().equals(currentAgentVersion))); Assertions.assertEquals( 0, getSpanStream(traces) diff --git a/smoke-tests/src/test/java/org/hypertrace/agent/smoketest/SpringBootSmokeTest.java b/smoke-tests/src/test/java/org/hypertrace/agent/smoketest/SpringBootSmokeTest.java index da19ca902..eb4c2f153 100644 --- a/smoke-tests/src/test/java/org/hypertrace/agent/smoketest/SpringBootSmokeTest.java +++ b/smoke-tests/src/test/java/org/hypertrace/agent/smoketest/SpringBootSmokeTest.java @@ -18,7 +18,7 @@ import io.opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceRequest; import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest; -import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; +import io.opentelemetry.semconv.ResourceAttributes; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; @@ -121,16 +121,12 @@ public void postJson() throws IOException, InterruptedException { .getValue() .getStringValue()); - Assertions.assertEquals(1, countSpansByName(traces, "POST /echo")); - Assertions.assertEquals(1, countSpansByName(traces, "WebController.echo")); + Assertions.assertEquals(1, countSpansByName(traces, "POST /*")); + Assertions.assertEquals(0, countSpansByName(traces, "WebController.echo")); Assertions.assertTrue( getInstrumentationLibSpanStream(traces) .anyMatch( - instLibSpan -> - instLibSpan - .getInstrumentationLibrary() - .getVersion() - .equals(currentAgentVersion))); + instLibSpan -> instLibSpan.getScope().getVersion().equals(currentAgentVersion))); Assertions.assertTrue( getSpanStream(traces) .flatMap(span -> span.getAttributesList().stream()) @@ -177,12 +173,7 @@ public void postJson() throws IOException, InterruptedException { Assertions.assertTrue(hasMetricNamed("otlp.exporter.exported", metrics)); Assertions.assertTrue(hasMetricNamed("processedSpans", metrics)); Assertions.assertTrue(hasMetricNamed("queueSize", metrics)); - Assertions.assertTrue(hasMetricNamed("http.server.request.size", metrics)); - Assertions.assertTrue(hasMetricNamed("http.server.response.size", metrics)); Assertions.assertTrue(hasMetricNamed("http.server.duration", metrics)); - Assertions.assertTrue(hasMetricNamed("process.runtime.jvm.memory.usage", metrics)); - Assertions.assertTrue(hasMetricNamed("process.runtime.jvm.memory.init", metrics)); - Assertions.assertTrue(hasMetricNamed("process.runtime.jvm.memory.committed", metrics)); } @Test diff --git a/smoke-tests/src/test/resources/otel.yaml b/smoke-tests/src/test/resources/otel.yaml index 0c6d258ca..af97a3442 100644 --- a/smoke-tests/src/test/resources/otel.yaml +++ b/smoke-tests/src/test/resources/otel.yaml @@ -22,7 +22,8 @@ exporters: loglevel: debug otlp: endpoint: backend:8080 - insecure: true + tls: + insecure: true service: pipelines: diff --git a/testing-bootstrap/build.gradle.kts b/testing-bootstrap/build.gradle.kts index 92065cd15..2b89f76a5 100644 --- a/testing-bootstrap/build.gradle.kts +++ b/testing-bootstrap/build.gradle.kts @@ -19,16 +19,3 @@ dependencies { implementation("ch.qos.logback:logback-classic:1.4.6") implementation("org.slf4j:slf4j-api:${versions["slf4j"]}") } - -tasks { - shadowJar { - archiveFileName.set("testing-bootstrap.jar") - - // need to exclude these logback classes from the bootstrap jar, otherwise tomcat will find them - // and try to load them from the bootstrap class loader, which will fail with NoClassDefFoundError - // since their super classes are servlet classes which are not in the bootstrap class loader - exclude("ch/qos/logback/classic/servlet/LogbackServletContainerInitializer.class") - exclude("ch/qos/logback/classic/servlet/LogbackServletContextListener.class") - exclude("META-INF/services/javax.servlet.ServletContainerInitializer") - } -} diff --git a/testing-common/build.gradle.kts b/testing-common/build.gradle.kts index a1ccb996c..eb5ad188b 100644 --- a/testing-common/build.gradle.kts +++ b/testing-common/build.gradle.kts @@ -1,44 +1,49 @@ +import com.google.protobuf.gradle.* + plugins { `java-library` - `java-test-fixtures` - id("net.bytebuddy.byte-buddy") + idea + id("com.google.protobuf") version "0.9.4" +} + +val protobufVersion = "3.19.6" + +protobuf { + protoc { + // The artifact spec for the Protobuf Compiler + artifact = "com.google.protobuf:protoc:$protobufVersion" + } + generateProtoTasks { + } +} + +idea { + module { + sourceDirs.add(file("${projectDir}/build/generated/source/proto/main/proto")) + } } val versions: Map by extra dependencies { - testFixturesApi(project(":otel-extensions")) + api(project(":otel-extensions")) - testFixturesCompileOnly("org.junit.jupiter:junit-jupiter-api:5.7.0") - testFixturesRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.7.0") - testFixturesImplementation("org.junit-pioneer:junit-pioneer:1.0.0") - testFixturesApi("io.opentelemetry:opentelemetry-api:${versions["opentelemetry"]}") - testFixturesApi("io.opentelemetry:opentelemetry-sdk:${versions["opentelemetry"]}") - testFixturesCompileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure:${versions["opentelemetry"]}-alpha") - testFixturesApi("com.squareup.okhttp3:okhttp:4.9.0") - testFixturesApi("com.squareup.okhttp3:logging-interceptor:4.9.0") - testFixturesImplementation("io.opentelemetry:opentelemetry-exporter-logging:${versions["opentelemetry"]}") - testFixturesImplementation("io.opentelemetry.javaagent:opentelemetry-javaagent-bootstrap:${versions["opentelemetry_java_agent"]}") - testFixturesImplementation("io.opentelemetry.javaagent:opentelemetry-javaagent-tooling:${versions["opentelemetry_java_agent-tooling"]}") { - constraints { - implementation("io.opentelemetry.javaagent:opentelemetry-javaagent-tooling-java9:1.7.2-alpha") { - attributes { - // this transitive dependency creates classes compatible with Java 9 and up, but is only referenced in safe ways for - // java 8 by the javaagent-tooling dependency - attribute(Attribute.of("org.gradle.jvm.version", Integer::class.java), 9 as Integer) - } - } - } - } - testFixturesImplementation("io.opentelemetry.javaagent:opentelemetry-javaagent-extension-api:${versions["opentelemetry_java_agent"]}") - testFixturesImplementation("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api:${versions["opentelemetry"]}") - testFixturesImplementation("ch.qos.logback:logback-classic:1.4.6") - testFixturesImplementation("org.slf4j:log4j-over-slf4j:${versions["slf4j"]}") - testFixturesImplementation("org.slf4j:jcl-over-slf4j:${versions["slf4j"]}") - testFixturesImplementation("org.slf4j:jul-to-slf4j:${versions["slf4j"]}") - testFixturesImplementation("net.bytebuddy:byte-buddy:${versions["byte_buddy"]}") - testFixturesImplementation("net.bytebuddy:byte-buddy-agent:${versions["byte_buddy"]}") - testFixturesCompileOnly("com.google.auto.service:auto-service-annotations:1.0") - testFixturesAnnotationProcessor("com.google.auto.service:auto-service:1.0") - testFixturesImplementation("org.eclipse.jetty:jetty-server:8.0.0.v20110901") + compileOnly("org.junit.jupiter:junit-jupiter-api:5.7.0") + runtimeOnly("org.junit.jupiter:junit-jupiter-engine:5.7.0") + implementation("org.junit-pioneer:junit-pioneer:1.0.0") + api("io.opentelemetry:opentelemetry-api:${versions["opentelemetry"]}") + api("io.opentelemetry:opentelemetry-sdk:${versions["opentelemetry"]}") + compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure:${versions["opentelemetry"]}") + api("com.squareup.okhttp3:okhttp:4.9.0") + api("com.squareup.okhttp3:logging-interceptor:4.9.0") + implementation("io.opentelemetry:opentelemetry-exporter-logging:${versions["opentelemetry"]}") + implementation("io.opentelemetry.javaagent:opentelemetry-javaagent-extension-api:${versions["opentelemetry_java_agent"]}") + implementation("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api:${versions["opentelemetry"]}") + implementation("ch.qos.logback:logback-classic:1.4.6") + implementation("org.slf4j:log4j-over-slf4j:${versions["slf4j"]}") + implementation("org.slf4j:jcl-over-slf4j:${versions["slf4j"]}") + implementation("org.slf4j:jul-to-slf4j:${versions["slf4j"]}") + compileOnly("com.google.auto.service:auto-service-annotations:1.0") + annotationProcessor("com.google.auto.service:auto-service:1.0") + implementation("org.eclipse.jetty:jetty-server:8.0.0.v20110901") } diff --git a/testing-common/src/testFixtures/java/org/hypertrace/agent/testing/AbstractHttpClientTest.java b/testing-common/src/main/java/org/hypertrace/agent/testing/AbstractHttpClientTest.java similarity index 57% rename from testing-common/src/testFixtures/java/org/hypertrace/agent/testing/AbstractHttpClientTest.java rename to testing-common/src/main/java/org/hypertrace/agent/testing/AbstractHttpClientTest.java index 51764b192..d71cdd21f 100644 --- a/testing-common/src/testFixtures/java/org/hypertrace/agent/testing/AbstractHttpClientTest.java +++ b/testing-common/src/main/java/org/hypertrace/agent/testing/AbstractHttpClientTest.java @@ -16,8 +16,7 @@ package org.hypertrace.agent.testing; -import io.opentelemetry.api.common.AttributeKey; -import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.proto.trace.v1.Span; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; @@ -30,7 +29,6 @@ import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; -import org.hypertrace.agent.core.instrumentation.HypertraceSemanticAttributes; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; @@ -42,7 +40,7 @@ public abstract class AbstractHttpClientTest extends AbstractInstrumenterTest { private static final String GET_NO_CONTENT_PATH_FORMAT = "http://localhost:%d/get_no_content"; private static final String GET_JSON_PATH_FORMAT = "http://localhost:%d/get_json"; - private static final String HEADER_NAME = "headerName"; + private static final String HEADER_NAME = "headername"; private static final String HEADER_VALUE = "headerValue"; private static final Map headers; @@ -56,9 +54,6 @@ public abstract class AbstractHttpClientTest extends AbstractInstrumenterTest { private final boolean hasResponseBodySpan; - private FakeTransformer fakeTransformer; - private String transformedClassDirName; - public AbstractHttpClientTest(boolean hasResponseBodySpan) { this.hasResponseBodySpan = hasResponseBodySpan; } @@ -108,46 +103,44 @@ public void postJson_echo() Assertions.assertEquals(body, response.body); TEST_WRITER.waitForTraces(1); + List> traces; if (hasResponseBodySpan) { - TEST_WRITER.waitForSpans(2); + // exclude server spans + traces = + TEST_WRITER.waitForSpans( + 2, span -> span.getKind().equals(Span.SpanKind.SPAN_KIND_SERVER)); + } else { + // exclude server spans + traces = + TEST_WRITER.waitForSpans( + 1, + span -> + !span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT) + || span.getAttributesList().stream() + .noneMatch( + keyValue -> + keyValue.getKey().equals("http.url") + && keyValue.getValue().getStringValue().contains("/echo"))); } - List> traces = TEST_WRITER.getTraces(); Assertions.assertEquals(1, traces.size()); - SpanData clientSpan = traces.get(0).get(0); + Span clientSpan = traces.get(0).get(0); if (hasResponseBodySpan) { Assertions.assertEquals(2, traces.get(0).size()); - SpanData responseBodySpan = traces.get(0).get(1); + Span responseBodySpan = traces.get(0).get(1); + if (traces.get(0).get(1).getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT)) { + clientSpan = traces.get(0).get(1); + responseBodySpan = traces.get(0).get(0); + } assertBodies(clientSpan, responseBodySpan, body, body); Assertions.assertNotNull( - responseBodySpan - .getAttributes() - .get(AttributeKey.stringKey("http.response.header.content-type"))); + TEST_WRITER.getAttributesMap(responseBodySpan).get("http.response.header.content-type")); } else { Assertions.assertEquals(1, traces.get(0).size()); assertRequestAndResponseBody(clientSpan, body, body); } } - /** - * For debugging unit tests, this function can be called to pass the class back through the OTEL - * Transformer, and write the resulting class bytes to an external file. - * - * @param className - */ - private void writeTransformedClass(String className) { - if (fakeTransformer == null) { - transformedClassDirName = System.getenv("TRANSFORMED_CLASS_DIR"); - if (transformedClassDirName != null && transformedClassDirName.length() > 0) { - fakeTransformer = new FakeTransformer(); - } - } - - if (fakeTransformer != null) { - fakeTransformer.writeTransformedClass(className, transformedClassDirName); - } - } - @Test public void postUrlEncoded_echo() throws TimeoutException, InterruptedException, IOException, ExecutionException { @@ -160,16 +153,33 @@ public void postUrlEncoded_echo() Assertions.assertEquals(body, response.body); TEST_WRITER.waitForTraces(1); + List> traces; if (hasResponseBodySpan) { - TEST_WRITER.waitForSpans(2); + traces = + TEST_WRITER.waitForSpans( + 2, span -> span.getKind().equals(Span.SpanKind.SPAN_KIND_SERVER)); + } else { + traces = + TEST_WRITER.waitForSpans( + 1, + span -> + !span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT) + || span.getAttributesList().stream() + .noneMatch( + keyValue -> + keyValue.getKey().equals("http.url") + && keyValue.getValue().getStringValue().contains("/echo"))); } - List> traces = TEST_WRITER.getTraces(); Assertions.assertEquals(1, traces.size()); - SpanData clientSpan = traces.get(0).get(0); + Span clientSpan = traces.get(0).get(0); if (hasResponseBodySpan) { Assertions.assertEquals(2, traces.get(0).size()); - SpanData responseBodySpan = traces.get(0).get(1); + Span responseBodySpan = traces.get(0).get(1); + if (traces.get(0).get(1).getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT)) { + clientSpan = traces.get(0).get(1); + responseBodySpan = traces.get(0).get(0); + } assertBodies(clientSpan, responseBodySpan, body, body); } else { Assertions.assertEquals(1, traces.get(0).size()); @@ -189,10 +199,19 @@ public void postPlainText_echo() Assertions.assertEquals(body, response.body); TEST_WRITER.waitForTraces(1); - List> traces = TEST_WRITER.getTraces(); + List> traces = + TEST_WRITER.waitForSpans( + 1, + span -> + !span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT) + || span.getAttributesList().stream() + .noneMatch( + keyValue -> + keyValue.getKey().equals("http.url") + && keyValue.getValue().getStringValue().contains("/echo"))); Assertions.assertEquals(1, traces.size()); Assertions.assertEquals(1, traces.get(0).size()); - SpanData clientSpan = traces.get(0).get(0); + Span clientSpan = traces.get(0).get(0); assertHeaders(clientSpan); assertNoBodies(clientSpan); @@ -209,10 +228,22 @@ public void getNoContent() Assertions.assertNull(response.body); TEST_WRITER.waitForTraces(1); - List> traces = TEST_WRITER.getTraces(); + List> traces = + TEST_WRITER.waitForSpans( + 1, + span -> + !span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT) + || span.getAttributesList().stream() + .noneMatch( + keyValue -> + keyValue.getKey().equals("http.url") + && keyValue + .getValue() + .getStringValue() + .contains("/get_no_content"))); Assertions.assertEquals(1, traces.size()); Assertions.assertEquals(1, traces.get(0).size()); - SpanData clientSpan = traces.get(0).get(0); + Span clientSpan = traces.get(0).get(0); assertHeaders(clientSpan); assertNoBodies(clientSpan); @@ -229,66 +260,90 @@ public void getJson() Assertions.assertEquals(TestHttpServer.GetJsonHandler.RESPONSE_BODY, response.body); TEST_WRITER.waitForTraces(1); + List> traces; if (hasResponseBodySpan) { - TEST_WRITER.waitForSpans(2); + traces = + TEST_WRITER.waitForSpans( + 2, span -> span.getKind().equals(Span.SpanKind.SPAN_KIND_SERVER)); + } else { + traces = + TEST_WRITER.waitForSpans( + 1, + span -> + !span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT) + || span.getAttributesList().stream() + .noneMatch( + keyValue -> + keyValue.getKey().equals("http.url") + && keyValue + .getValue() + .getStringValue() + .contains("/get_json"))); } - List> traces = TEST_WRITER.getTraces(); - Assertions.assertEquals(1, traces.size()); - SpanData clientSpan = traces.get(0).get(0); - Assertions.assertNull( - clientSpan.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); + Assertions.assertEquals(1, traces.size()); + Span clientSpan = traces.get(0).get(0); if (hasResponseBodySpan) { Assertions.assertEquals(2, traces.get(0).size()); - SpanData responseBodySpan = traces.get(0).get(1); + Span responseBodySpan = traces.get(0).get(1); + if (traces.get(0).get(1).getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT)) { + clientSpan = traces.get(0).get(1); + responseBodySpan = traces.get(0).get(0); + } + Assertions.assertNull(TEST_WRITER.getAttributesMap(clientSpan).get("http.request.body")); + Assertions.assertEquals( TestHttpServer.GetJsonHandler.RESPONSE_BODY, - responseBodySpan.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER + .getAttributesMap(responseBodySpan) + .get("http.response.body") + .getStringValue()); } else { + Assertions.assertNull(TEST_WRITER.getAttributesMap(clientSpan).get("http.request.body")); + Assertions.assertEquals(1, traces.get(0).size()); Assertions.assertEquals( TestHttpServer.GetJsonHandler.RESPONSE_BODY, - clientSpan.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER.getAttributesMap(clientSpan).get("http.response.body").getStringValue()); } } - private void assertHeaders(SpanData spanData) { + private void assertHeaders(Span span) { Assertions.assertEquals( TestHttpServer.RESPONSE_HEADER_VALUE, - spanData - .getAttributes() - .get( - HypertraceSemanticAttributes.httpResponseHeader( - TestHttpServer.RESPONSE_HEADER_NAME))); + TEST_WRITER + .getAttributesMap(span) + .get("http.response.header." + TestHttpServer.RESPONSE_HEADER_NAME) + .getStringValue()); Assertions.assertEquals( HEADER_VALUE, - spanData.getAttributes().get(HypertraceSemanticAttributes.httpRequestHeader(HEADER_NAME))); + TEST_WRITER + .getAttributesMap(span) + .get("http.request.header." + HEADER_NAME) + .getStringValue()); } - private void assertRequestAndResponseBody( - SpanData spanData, String requestBody, String responseBody) { + private void assertRequestAndResponseBody(Span span, String requestBody, String responseBody) { Assertions.assertEquals( - requestBody, spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); + requestBody, TEST_WRITER.getAttributesMap(span).get("http.request.body").getStringValue()); Assertions.assertEquals( responseBody, - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER.getAttributesMap(span).get("http.response.body").getStringValue()); } private void assertBodies( - SpanData clientSpan, SpanData responseBodySpan, String requestBody, String responseBody) { + Span clientSpan, Span responseBodySpan, String requestBody, String responseBody) { Assertions.assertEquals( requestBody, - clientSpan.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); + TEST_WRITER.getAttributesMap(clientSpan).get("http.request.body").getStringValue()); Assertions.assertEquals( responseBody, - responseBodySpan.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + TEST_WRITER.getAttributesMap(responseBodySpan).get("http.response.body").getStringValue()); } - private void assertNoBodies(SpanData spanData) { - Assertions.assertNull( - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); - Assertions.assertNull( - spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); + private void assertNoBodies(Span span) { + Assertions.assertNull(TEST_WRITER.getAttributesMap(span).get("http.response.body")); + Assertions.assertNull(TEST_WRITER.getAttributesMap(span).get("http.request.body")); } public static String readInputStream(InputStream inputStream) throws IOException { diff --git a/testing-common/src/testFixtures/java/org/hypertrace/agent/testing/AbstractInstrumenterTest.java b/testing-common/src/main/java/org/hypertrace/agent/testing/AbstractInstrumenterTest.java similarity index 52% rename from testing-common/src/testFixtures/java/org/hypertrace/agent/testing/AbstractInstrumenterTest.java rename to testing-common/src/main/java/org/hypertrace/agent/testing/AbstractInstrumenterTest.java index bb3cfefb5..80139df54 100644 --- a/testing-common/src/testFixtures/java/org/hypertrace/agent/testing/AbstractInstrumenterTest.java +++ b/testing-common/src/main/java/org/hypertrace/agent/testing/AbstractInstrumenterTest.java @@ -18,27 +18,20 @@ import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; -import io.opentelemetry.api.GlobalOpenTelemetry; -import io.opentelemetry.api.trace.Tracer; -import io.opentelemetry.javaagent.bootstrap.AgentInitializer; -import io.opentelemetry.javaagent.bootstrap.InstrumentationHolder; -import io.opentelemetry.javaagent.tooling.AgentInstaller; import java.io.IOException; -import java.lang.instrument.Instrumentation; -import java.lang.reflect.Field; import java.util.Arrays; import java.util.concurrent.TimeUnit; -import net.bytebuddy.agent.ByteBuddyAgent; import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.RequestBody; import okio.BufferedSink; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.slf4j.LoggerFactory; /** - * Abstract test class that tests {@link + * Abstract test class that tests {@link // * io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule} on the classpath. */ public abstract class AbstractInstrumenterTest { @@ -51,29 +44,14 @@ public abstract class AbstractInstrumenterTest { * *

Before the start of each test the reported traces will be reset. */ - public static final InMemoryExporter TEST_WRITER = new InMemoryExporter();; - - protected static Tracer TEST_TRACER; - private static final Instrumentation INSTRUMENTATION; + public static final TestOtlpReceiver TEST_WRITER = new TestOtlpReceiver(); static { - // always run with the thread propagation debugger to help track down sporadic test failures - System.setProperty("io.opentelemetry.context.contextStorageProvider", "default"); - System.setProperty("otel.threadPropagationDebugger", "true"); - System.setProperty("otel.internal.failOnContextLeak", "true"); - System.setProperty("io.opentelemetry.javaagent.slf4j.simpleLogger.log.muzzleMatcher", "warn"); - System.setProperty("otel.traces.exporter", "none"); - System.setProperty("otel.metrics.exporter", "none"); - - INSTRUMENTATION = ByteBuddyAgent.install(); - InstrumentationHolder.setInstrumentation(INSTRUMENTATION); - // TODO causes Caused by: java.lang.ClassCastException ((Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME)).setLevel(Level.WARN); ((Logger) LoggerFactory.getLogger("io.opentelemetry")).setLevel(Level.DEBUG); } - private static boolean INSTRUMENTED = false; private static final int DEFAULT_TIMEOUT_SECONDS = 30; protected OkHttpClient httpClient = new OkHttpClient.Builder() @@ -82,45 +60,14 @@ public abstract class AbstractInstrumenterTest { .readTimeout(DEFAULT_TIMEOUT_SECONDS, TimeUnit.SECONDS) .build(); + @AfterAll + public static void closeServer() throws Exception { + TEST_WRITER.close(); + } + @BeforeAll - public static void beforeAll() { - /* - * OpenTelemetry moved the initialization of some agent primitives to the OTEL class - * OpenTelemetryAgent which does not get used in the scope of these tests. To remove this - * workaround, we should adopt their testing pattern leveraging the agent-for-teseting artifact - * and the AgentInstrumentationExtension for JUnit. - */ - TestAgentStarter testAgentStarter = new TestAgentStarter(); - try { - Class agentInitializerClass = - ClassLoader.getSystemClassLoader() - .loadClass("io.opentelemetry.javaagent.bootstrap.AgentInitializer"); - Field agentClassLoaderField = agentInitializerClass.getDeclaredField("agentClassLoader"); - agentClassLoaderField.setAccessible(true); - ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); - agentClassLoaderField.set(null, systemClassLoader); - Field agentStarterField = agentInitializerClass.getDeclaredField("agentStarter"); - agentStarterField.setAccessible(true); - agentStarterField.set(null, testAgentStarter); - } catch (Throwable t) { - throw new AssertionError("Could not access agent classLoader", t); - } - if (!INSTRUMENTED) { - // --------------------------------------------------------------------- - // Set the otel.instrumentation.internal-reflection.enabled property to - // 'false'. This causes the OTEL agent to filter out virtual field - // accessor interfaces when it is verifying that a class can be - // transformed. Some of our unit tests will fail if this property - // is not set. - // --------------------------------------------------------------------- - System.setProperty("otel.instrumentation.internal-reflection.enabled", "false"); - AgentInstaller.installBytebuddyAgent( - INSTRUMENTATION, AgentInitializer.getExtensionsClassLoader()); - INSTRUMENTED = true; - } - if (TEST_TRACER == null) { - TEST_TRACER = GlobalOpenTelemetry.getTracer("io.opentelemetry.auto"); - } + public static void beforeAll() throws Exception { + TEST_WRITER.start(); } @BeforeEach diff --git a/testing-common/src/testFixtures/java/org/hypertrace/agent/testing/OkHttpUtils.java b/testing-common/src/main/java/org/hypertrace/agent/testing/OkHttpUtils.java similarity index 100% rename from testing-common/src/testFixtures/java/org/hypertrace/agent/testing/OkHttpUtils.java rename to testing-common/src/main/java/org/hypertrace/agent/testing/OkHttpUtils.java diff --git a/testing-common/src/testFixtures/java/org/hypertrace/agent/testing/TestHttpServer.java b/testing-common/src/main/java/org/hypertrace/agent/testing/TestHttpServer.java similarity index 100% rename from testing-common/src/testFixtures/java/org/hypertrace/agent/testing/TestHttpServer.java rename to testing-common/src/main/java/org/hypertrace/agent/testing/TestHttpServer.java diff --git a/testing-common/src/main/java/org/hypertrace/agent/testing/TestOtlpReceiver.java b/testing-common/src/main/java/org/hypertrace/agent/testing/TestOtlpReceiver.java new file mode 100644 index 000000000..bf69c8a2c --- /dev/null +++ b/testing-common/src/main/java/org/hypertrace/agent/testing/TestOtlpReceiver.java @@ -0,0 +1,299 @@ +/* + * Copyright The Hypertrace Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.hypertrace.agent.testing; + +import com.google.common.base.Stopwatch; +import io.opentelemetry.api.trace.SpanId; +import io.opentelemetry.proto.collector.trace.v1.ExportTracePartialSuccess; +import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest; +import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceResponse; +import io.opentelemetry.proto.common.v1.AnyValue; +import io.opentelemetry.proto.common.v1.KeyValue; +import io.opentelemetry.proto.trace.v1.ResourceSpans; +import io.opentelemetry.proto.trace.v1.ScopeSpans; +import io.opentelemetry.proto.trace.v1.Span; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.Predicate; +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.AbstractHandler; +import org.eclipse.jetty.server.handler.HandlerList; + +public class TestOtlpReceiver implements AutoCloseable { + + private final Server server = new Server(4318); + private final HandlerList handlerList = new HandlerList(); + + private static final List> traces = new ArrayList<>(); // guarded by tracesLock + private static final Object tracesLock = new Object(); + + public void start() throws Exception { + HandlerList handlerList = new HandlerList(); + server.stop(); + handlerList.addHandler(new OtlpTracesHandler()); + server.setHandler(handlerList); + System.out.println("Starting OTLP receiver"); + server.start(); + } + + public void addHandler(Handler handler) { + this.handlerList.addHandler(handler); + } + + @Override + public void close() throws Exception { + server.stop(); + } + + public int port() { + return server.getConnectors()[0].getLocalPort(); + } + + static class OtlpTracesHandler extends AbstractHandler { + @Override + public void handle( + String target, + Request baseRequest, + HttpServletRequest request, + HttpServletResponse response) + throws IOException { + + if (target.equals("/v1/traces") + && ("post".equalsIgnoreCase(request.getMethod()) + || "put".equalsIgnoreCase(request.getMethod()))) { + ServletInputStream inputStream = request.getInputStream(); + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + int nRead; + while ((nRead = inputStream.read()) != -1) { + buffer.write((byte) nRead); + } + + ExportTraceServiceRequest traceServiceRequest = + ExportTraceServiceRequest.newBuilder().mergeFrom(buffer.toByteArray()).build(); + + System.out.println("Received traces in OTLP Receiver:\n" + traceServiceRequest); + + for (ResourceSpans resourceSpans : traceServiceRequest.getResourceSpansList()) { + for (ScopeSpans scopeSpans : resourceSpans.getScopeSpansList()) { + for (Span span : scopeSpans.getSpansList()) { + synchronized (tracesLock) { + boolean found = false; + for (List trace : traces) { + if (trace.get(0).getTraceId().equals(span.getTraceId())) { + trace.add(span); + found = true; + break; + } + } + if (!found) { + List trace = new CopyOnWriteArrayList<>(); + trace.add(span); + traces.add(trace); + } + tracesLock.notifyAll(); + } + } + } + } + + response.setStatus(200); + response.setContentType(request.getContentType()); + response + .getWriter() + .print( + ExportTraceServiceResponse.newBuilder() + .setPartialSuccess( + ExportTracePartialSuccess.newBuilder().setRejectedSpans(0).build())); + baseRequest.setHandled(true); + } + } + } + + private List> getTraces() { + synchronized (tracesLock) { + // always return a copy so that future structural changes cannot cause race conditions during + // test verification + List> copy = new ArrayList<>(traces.size()); + for (List trace : traces) { + copy.add(new ArrayList<>(trace)); + } + return copy; + } + } + + public void waitForTraces(int number) throws InterruptedException, TimeoutException { + waitForTraces(number, spans -> false); + } + + public List> waitForTraces(int number, Predicate> excludes) + throws InterruptedException, TimeoutException { + synchronized (tracesLock) { + long remainingWaitMillis = TimeUnit.SECONDS.toMillis(31); + List> traces = getCompletedAndFilteredTraces(excludes, span -> false); + while (traces.size() < number && remainingWaitMillis > 0) { + Stopwatch stopwatch = Stopwatch.createStarted(); + tracesLock.wait(remainingWaitMillis); + remainingWaitMillis -= stopwatch.elapsed(TimeUnit.MILLISECONDS); + traces = getCompletedAndFilteredTraces(excludes, span -> false); + } + if (traces.size() < number) { + System.out.println( + "Timeout waiting for " + + number + + " completed/filtered trace(s), found " + + traces.size() + + " completed/filtered trace(s) and " + + traces.size() + + " total trace(s): " + + traces); + throw new TimeoutException( + "Timeout waiting for " + + number + + " completed/filtered trace(s), found " + + traces.size() + + " completed/filtered trace(s) and " + + traces.size() + + " total trace(s): " + + traces); + } + return traces; + } + } + + private int spansCount(List> traces) { + int count = 0; + for (List trace : traces) { + count += trace.size(); + } + return count; + } + + public List> waitForSpans(int number) throws InterruptedException, TimeoutException { + return waitForSpans(number, span -> false); + } + + public List> waitForSpans(int number, Predicate excludes) + throws InterruptedException, TimeoutException { + return waitForSpans(number, excludes, 20); + } + + public List> waitForSpans(int number, Predicate excludes, int timeoutSeconds) + throws InterruptedException, TimeoutException { + synchronized (tracesLock) { + long remainingWaitMillis = TimeUnit.SECONDS.toMillis(timeoutSeconds); + + List> traces = getCompletedAndFilteredTraces(spans -> false, excludes); + while (spansCount(traces) < number && remainingWaitMillis > 0) { + Stopwatch stopwatch = Stopwatch.createStarted(); + tracesLock.wait(remainingWaitMillis); + remainingWaitMillis -= stopwatch.elapsed(TimeUnit.MILLISECONDS); + traces = getCompletedAndFilteredTraces(spans -> false, excludes); + } + if (spansCount(traces) < number) { + System.out.println( + "Timeout waiting for " + + number + + " completed/filtered spans(s), found " + + spansCount(traces) + + " total spans(s): " + + traces); + throw new TimeoutException( + "Timeout waiting for " + + number + + " completed/filtered spans(s), found " + + spansCount(traces) + + " total spans(s): " + + traces); + } + return traces; + } + } + + private List getFilteredSpans(List trace, Predicate excludeSpanPredicate) { + List filteredSpans = new ArrayList<>(); + Predicate excludes = + span -> { + assert span != null; + AnyValue target = getAttributesMap(span).get("http.target"); + boolean excludeSpan = false; + if (target != null && target.getStringValue().contains("/v1/traces")) { + excludeSpan = true; + } + AnyValue url = getAttributesMap(span).get("http.url"); + if (url != null && url.getStringValue().contains("/v1/traces")) { + excludeSpan = true; + } + return excludeSpan; + }; + for (Span span : trace) { + if (!excludes.test(span) && !excludeSpanPredicate.test(span)) { + filteredSpans.add(span); + } + } + return filteredSpans; + } + + private List> getCompletedAndFilteredTraces( + Predicate> excludeTrace, Predicate excludeSpan) { + List> traces = new ArrayList<>(); + for (List trace : getTraces()) { + if (isCompleted(trace) && !excludeTrace.test(trace)) { + List filteredTrace = getFilteredSpans(trace, excludeSpan); + if (!filteredTrace.isEmpty()) { + traces.add(filteredTrace); + } + } + } + return traces; + } + + // trace is completed if root span is present + private static boolean isCompleted(List trace) { + for (Span span : trace) { + if (!SpanId.isValid(span.getParentSpanId().toString())) { + return true; + } + } + return false; + } + + public void clear() { + synchronized (tracesLock) { + traces.clear(); + } + } + + public Map getAttributesMap(Span span) { + Map attributesMap = new HashMap<>(); + for (KeyValue keyValue : span.getAttributesList()) { + attributesMap.put(keyValue.getKey(), keyValue.getValue()); + } + return attributesMap; + } +} diff --git a/testing-common/src/main/proto b/testing-common/src/main/proto new file mode 160000 index 000000000..9d139c87b --- /dev/null +++ b/testing-common/src/main/proto @@ -0,0 +1 @@ +Subproject commit 9d139c87b52669a3e2825b835dd828b57a455a55 diff --git a/testing-common/src/testFixtures/java/org/hypertrace/agent/testing/FakeTransformer.java b/testing-common/src/testFixtures/java/org/hypertrace/agent/testing/FakeTransformer.java deleted file mode 100644 index 505d8722b..000000000 --- a/testing-common/src/testFixtures/java/org/hypertrace/agent/testing/FakeTransformer.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright The Hypertrace Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.hypertrace.agent.testing; - -import io.opentelemetry.javaagent.bootstrap.ClassFileTransformerHolder; -import java.io.*; -import java.lang.instrument.ClassFileTransformer; - -/** - * This is a utility class which can be called within a unit test. It's purpose is to re-invoke the - * OTEL class transformer for a class, and then write the result class bytes to an external file, - * where it can be examined. - */ -public class FakeTransformer { - - private final ClassLoader classLoader; - private ClassFileTransformer transformer; - - public FakeTransformer() { - super(); - classLoader = FakeTransformer.class.getClassLoader(); - transformer = ClassFileTransformerHolder.getClassFileTransformer(); - } - - /** - * Re-transforms a class, and then writes the before and after images to a file system directory. - * - * @param className - * @param targetDir - */ - public void writeTransformedClass(String className, String targetDir) { - BufferedInputStream bis = null; - ByteArrayOutputStream baos = null; - - String resourceName = className.replace('.', '/') + ".class"; - - try { - InputStream is = classLoader.getResourceAsStream(resourceName); - - try { - baos = new ByteArrayOutputStream(); - bis = new BufferedInputStream(is); - - copy(bis, baos); - } finally { - if (baos != null) { - baos.close(); - } - - if (bis != null) { - bis.close(); - } - } - - File beforeFile = new File(new File(targetDir), "before" + File.separator + resourceName); - - beforeFile.getParentFile().mkdirs(); - - BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(beforeFile)); - bis = new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())); - - try { - copy(bis, bos); - } finally { - if (bos != null) { - bos.close(); - } - - if (bis != null) { - bis.close(); - } - } - - byte[] result = transformer.transform(classLoader, className, null, null, baos.toByteArray()); - - if (result != null) { - System.err.println("Received transformed bytes"); - - File outFile = new File(new File(targetDir), "after" + File.separator + resourceName); - - outFile.getParentFile().mkdirs(); - - bos = new BufferedOutputStream(new FileOutputStream(outFile)); - bis = new BufferedInputStream(new ByteArrayInputStream(result)); - - try { - copy(bis, bos); - } finally { - if (bos != null) { - bos.close(); - } - - if (bis != null) { - bis.close(); - } - } - } - } catch (Exception e) { - e.printStackTrace(System.err); - } - } - - private static void copy(InputStream is, OutputStream os) throws IOException { - - int available; - - while ((available = is.available()) > 0) { - byte[] buffer = new byte[available]; - - int bytesRead = is.read(buffer); - - if (bytesRead < 0) { - break; - } - - if (bytesRead > 0) { - os.write(buffer, 0, bytesRead); - } - } - } -} diff --git a/testing-common/src/testFixtures/java/org/hypertrace/agent/testing/InMemoryExporter.java b/testing-common/src/testFixtures/java/org/hypertrace/agent/testing/InMemoryExporter.java deleted file mode 100644 index ad2bd0141..000000000 --- a/testing-common/src/testFixtures/java/org/hypertrace/agent/testing/InMemoryExporter.java +++ /dev/null @@ -1,374 +0,0 @@ -/* - * Copyright The Hypertrace Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.hypertrace.agent.testing; - -import com.google.common.base.Predicate; -import com.google.common.base.Predicates; -import com.google.common.base.Stopwatch; -import com.google.common.collect.Iterables; -import com.google.common.collect.TreeTraverser; -import com.google.common.primitives.Ints; -import com.google.common.primitives.Longs; -import io.opentelemetry.api.trace.SpanId; -import io.opentelemetry.context.Context; -import io.opentelemetry.sdk.common.CompletableResultCode; -import io.opentelemetry.sdk.trace.ReadWriteSpan; -import io.opentelemetry.sdk.trace.ReadableSpan; -import io.opentelemetry.sdk.trace.SpanProcessor; -import io.opentelemetry.sdk.trace.data.SpanData; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicInteger; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class InMemoryExporter implements SpanProcessor { - - private static final Logger log = LoggerFactory.getLogger(InMemoryExporter.class); - - private final List> traces = new ArrayList<>(); // guarded by tracesLock - - private boolean needsTraceSorting; // guarded by tracesLock - private final Set needsSpanSorting = new HashSet<>(); // guarded by tracesLock - - private final Object tracesLock = new Object(); - - // not using span startEpochNanos since that is not strictly increasing so can lead to ties - private final Map spanOrders = new ConcurrentHashMap<>(); - private final AtomicInteger nextSpanOrder = new AtomicInteger(); - - private volatile boolean forceFlushCalled; - - @Override - public void onStart(Context context, ReadWriteSpan readWriteSpan) { - SpanData sd = readWriteSpan.toSpanData(); - log.debug( - ">>>{} SPAN START: {} id={} traceid={} parent={}, library={}", - sd.getStartEpochNanos(), - sd.getName(), - sd.getSpanId(), - sd.getTraceId(), - sd.getParentSpanId(), - sd.getInstrumentationLibraryInfo()); - synchronized (tracesLock) { - spanOrders.put(readWriteSpan.getSpanContext().getSpanId(), nextSpanOrder.getAndIncrement()); - } - } - - @Override - public boolean isStartRequired() { - return true; - } - - @Override - public void onEnd(ReadableSpan readableSpan) { - SpanData sd = readableSpan.toSpanData(); - log.debug( - "<<<{} SPAN END: {} id={} traceid={} parent={}, library={}, attributes={}", - sd.getEndEpochNanos(), - sd.getName(), - sd.getSpanId(), - sd.getTraceId(), - sd.getParentSpanId(), - sd.getInstrumentationLibraryInfo(), - printSpanAttributes(sd)); - SpanData span = readableSpan.toSpanData(); - synchronized (tracesLock) { - if (!spanOrders.containsKey(span.getSpanId())) { - // this happens on some tests where there are sporadic background traces, - // e.g. Elasticsearch "RefreshAction" - log.debug("span ended that was started prior to clear(): {}", span); - return; - } - boolean found = false; - for (List trace : traces) { - if (trace.get(0).getTraceId().equals(span.getTraceId())) { - trace.add(span); - found = true; - break; - } - } - if (!found) { - List trace = new CopyOnWriteArrayList<>(); - trace.add(span); - traces.add(trace); - needsTraceSorting = true; - } - needsSpanSorting.add(span.getTraceId()); - tracesLock.notifyAll(); - } - } - - private String printSpanAttributes(SpanData sd) { - final StringBuilder attributes = new StringBuilder(); - sd.getAttributes() - .forEach( - (key, value) -> { - attributes.append(String.format("Attribute %s=%s", key, value)); - }); - return attributes.toString(); - } - - @Override - public boolean isEndRequired() { - return true; - } - - public List> getTraces() { - synchronized (tracesLock) { - // important not to sort trace or span lists in place so that any tests that are currently - // iterating over them are not affected - if (needsTraceSorting) { - sortTraces(); - needsTraceSorting = false; - } - if (!needsSpanSorting.isEmpty()) { - for (int i = 0; i < traces.size(); i++) { - List trace = traces.get(i); - if (needsSpanSorting.contains(trace.get(0).getTraceId())) { - traces.set(i, sort(trace)); - } - } - needsSpanSorting.clear(); - } - // always return a copy so that future structural changes cannot cause race conditions during - // test verification - List> copy = new ArrayList<>(traces.size()); - for (List trace : traces) { - copy.add(new ArrayList<>(trace)); - } - return copy; - } - } - - public void waitForTraces(int number) throws InterruptedException, TimeoutException { - waitForTraces(number, Predicates.>alwaysFalse()); - } - - public List> waitForTraces(int number, Predicate> excludes) - throws InterruptedException, TimeoutException { - synchronized (tracesLock) { - long remainingWaitMillis = TimeUnit.SECONDS.toMillis(20); - List> traces = getCompletedAndFilteredTraces(excludes); - while (traces.size() < number && remainingWaitMillis > 0) { - Stopwatch stopwatch = Stopwatch.createStarted(); - tracesLock.wait(remainingWaitMillis); - remainingWaitMillis -= stopwatch.elapsed(TimeUnit.MILLISECONDS); - traces = getCompletedAndFilteredTraces(excludes); - } - if (traces.size() < number) { - throw new TimeoutException( - "Timeout waiting for " - + number - + " completed/filtered trace(s), found " - + traces.size() - + " completed/filtered trace(s) and " - + traces.size() - + " total trace(s): " - + traces); - } - return traces; - } - } - - private int spansCount(List> traces) { - int count = 0; - for (List trace : traces) { - count += trace.size(); - } - return count; - } - - public List> waitForSpans(int number) - throws InterruptedException, TimeoutException { - Predicate> excludes = Predicates.alwaysFalse(); - synchronized (tracesLock) { - long remainingWaitMillis = TimeUnit.SECONDS.toMillis(20); - List> traces = getCompletedAndFilteredTraces(excludes); - while (spansCount(traces) < number && remainingWaitMillis > 0) { - Stopwatch stopwatch = Stopwatch.createStarted(); - tracesLock.wait(remainingWaitMillis); - remainingWaitMillis -= stopwatch.elapsed(TimeUnit.MILLISECONDS); - traces = getCompletedAndFilteredTraces(excludes); - } - if (spansCount(traces) < number) { - throw new TimeoutException( - "Timeout waiting for " - + number - + " completed/filtered spans(s), found " - + spansCount(traces) - + " total spans(s): " - + traces); - } - return traces; - } - } - - private List> getCompletedAndFilteredTraces(Predicate> excludes) { - List> traces = new ArrayList<>(); - for (List trace : getTraces()) { - if (isCompleted(trace) && !excludes.apply(trace)) { - traces.add(trace); - } - } - return traces; - } - - public void clear() { - synchronized (tracesLock) { - traces.clear(); - spanOrders.clear(); - } - forceFlushCalled = false; - } - - @Override - public CompletableResultCode shutdown() { - return CompletableResultCode.ofSuccess(); - } - - @Override - public CompletableResultCode forceFlush() { - forceFlushCalled = true; - return CompletableResultCode.ofSuccess(); - } - - public boolean forceFlushCalled() { - return forceFlushCalled; - } - - // must be called under tracesLock - private void sortTraces() { - Collections.sort( - traces, - new Comparator>() { - @Override - public int compare(List trace1, List trace2) { - return Longs.compare(getMinSpanOrder(trace1), getMinSpanOrder(trace2)); - } - }); - } - - private long getMinSpanOrder(List spans) { - long min = Long.MAX_VALUE; - for (SpanData span : spans) { - min = Math.min(min, getSpanOrder(span)); - } - return min; - } - - private List sort(List trace) { - - Map lookup = new HashMap<>(); - for (SpanData span : trace) { - lookup.put(span.getSpanId(), new Node(span)); - } - - for (Node node : lookup.values()) { - String parentSpanId = node.span.getParentSpanId(); - if (SpanId.isValid(parentSpanId)) { - Node parentNode = lookup.get(parentSpanId); - if (parentNode != null) { - parentNode.childNodes.add(node); - node.root = false; - } - } - } - - List rootNodes = new ArrayList<>(); - for (Node node : lookup.values()) { - sortOneLevel(node.childNodes); - if (node.root) { - rootNodes.add(node); - } - } - sortOneLevel(rootNodes); - - TreeTraverser traverser = - new TreeTraverser() { - @Override - public Iterable children(Node node) { - return node.childNodes; - } - }; - - List orderedNodes = new ArrayList<>(); - for (Node rootNode : rootNodes) { - Iterables.addAll(orderedNodes, traverser.preOrderTraversal(rootNode)); - } - - List orderedSpans = new ArrayList<>(); - for (Node node : orderedNodes) { - orderedSpans.add(node.span); - } - return orderedSpans; - } - - private void sortOneLevel(List nodes) { - Collections.sort( - nodes, - new Comparator() { - @Override - public int compare(Node node1, Node node2) { - return Ints.compare(getSpanOrder(node1.span), getSpanOrder(node2.span)); - } - }); - } - - private int getSpanOrder(SpanData span) { - Integer order = spanOrders.get(span.getSpanId()); - if (order == null) { - throw new IllegalStateException("order not found for span: " + span); - } - return order; - } - - // trace is completed if root span is present - private static boolean isCompleted(List trace) { - for (SpanData span : trace) { - if (!SpanId.isValid(span.getParentSpanId())) { - return true; - } - if (span.getParentSpanId().equals("0000000000000456")) { - // this is a special parent id that some tests use - return true; - } - } - return false; - } - - private static class Node { - - private final SpanData span; - private final List childNodes = new ArrayList<>(); - private boolean root = true; - - private Node(SpanData span) { - this.span = span; - } - } -} diff --git a/testing-common/src/testFixtures/java/org/hypertrace/agent/testing/TestSdkTracerProviderConfigurer.java b/testing-common/src/testFixtures/java/org/hypertrace/agent/testing/TestSdkTracerProviderConfigurer.java deleted file mode 100644 index 09dddebea..000000000 --- a/testing-common/src/testFixtures/java/org/hypertrace/agent/testing/TestSdkTracerProviderConfigurer.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright The Hypertrace Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.hypertrace.agent.testing; - -import com.google.auto.service.AutoService; -import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer; -import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; - -@AutoService(AutoConfigurationCustomizerProvider.class) -public final class TestSdkTracerProviderConfigurer implements AutoConfigurationCustomizerProvider { - - @Override - public void customize(AutoConfigurationCustomizer autoConfiguration) { - autoConfiguration.addTracerProviderCustomizer( - (sdkTracerProviderBuilder, configProperties) -> - sdkTracerProviderBuilder.addSpanProcessor(AbstractInstrumenterTest.TEST_WRITER)); - } -} diff --git a/tests-extension/build.gradle.kts b/tests-extension/build.gradle.kts new file mode 100644 index 000000000..1288e5ced --- /dev/null +++ b/tests-extension/build.gradle.kts @@ -0,0 +1,68 @@ +plugins { + `java-library` + id("com.github.johnrengelman.shadow") +} + +val versions: Map by extra + +repositories { + mavenCentral() +} + +dependencies { + api(project(":filter-api")) + compileOnly(project(":javaagent-bootstrap")) + compileOnly("io.opentelemetry:opentelemetry-sdk") + compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi") + compileOnly("io.opentelemetry.javaagent:opentelemetry-javaagent-extension-api:${versions["opentelemetry_java_agent"]}") + compileOnly("io.opentelemetry.javaagent:opentelemetry-javaagent-tooling:${versions["opentelemetry_java_agent-tooling"]}") + compileOnly("com.google.auto.service:auto-service-annotations:1.0") + annotationProcessor("com.google.auto.service:auto-service:1.0") +} + +tasks{ + shadowJar{ + dependencies{ + // exclude packages that live in the bootstrap classloader + exclude("io/opentelemetry/semconv/**") + exclude("io/opentelemetry/context/**") + exclude(dependency("io.opentelemetry:opentelemetry-api")) + exclude(dependency("org.hypertrace.agent:filter-api")) + exclude(dependency("org.hypertrace.agent:javaagent-core")) + exclude("io/opentelemetry/instrumentation/api/**") + // exclude bootstrap part of javaagent-extension-api + exclude("io/opentelemetry/javaagent/bootstrap/**") + } + + // relocate these in sync with + // https://github.com/hypertrace/javaagent/blob/main/instrumentation/build.gradle.kts#L56-L82 + relocate("com.fasterxml.jackson", "io.opentelemetry.javaagent.shaded.org.hypertrace.shaded.com.fasterxml.jackson") + relocate("com.google", "io.opentelemetry.javaagent.shaded.org.hypertrace.shaded.com.google") + relocate("google.protobuf", "io.opentelemetry.javaagent.shaded.org.hypertrace.shaded.google.protobuf") + relocate("org.checkerframework", "io.opentelemetry.javaagent.shaded.org.hypertrace.shaded.com.checkerframework") // transitive dependency form ht-filter-api + relocate("org.yaml", "io.opentelemetry.javaagent.shaded.org.hypertrace.shaded.org.yaml") // transitive dependency form ht-filter-api + relocate("com.blogspot.mydailyjava.weaklockfree", "io.opentelemetry.instrumentation.api.internal.shaded.weaklockfree") // transitive dependency from ht-filter-api + relocate("org.slf4j", "io.opentelemetry.javaagent.slf4j") + relocate("java.util.logging.Logger", "io.opentelemetry.javaagent.bootstrap.PatchLogger") + + relocate("okhttp3", "org.hypertrace.javaagent.filter.com.squareup.okhttp3") + relocate("okio", "org.hypertrace.javaagent.filter.com.squareup.okio") // transitive dependency from okhttp + relocate("org.codehaus", "org.hypertrace.javaagent.filter.org.codehaus") // transitive dependency from ht-filter-api + + // relocate OpenTelemetry API in sync with + // https://github.com/hypertrace/javaagent/blob/main/javaagent/build.gradle.kts#L58-L63 + relocate("io.opentelemetry.api", "io.opentelemetry.javaagent.shaded.io.opentelemetry.api") + relocate("io.opentelemetry.semconv", "io.opentelemetry.javaagent.shaded.io.opentelemetry.semconv") + relocate("io.opentelemetry.spi", "io.opentelemetry.javaagent.shaded.io.opentelemetry.spi") + relocate("io.opentelemetry.context", "io.opentelemetry.javaagent.shaded.io.opentelemetry.context") + relocate("io.opentelemetry.extension.kotlin", "io.opentelemetry.javaagent.shaded.io.opentelemetry.extension.kotlin") + relocate("io.opentelemetry.extension.aws", "io.opentelemetry.javaagent.shaded.io.opentelemetry.extension.aws") + + + manifest { + attributes.put("Implementation-Title", "test-filter-impl") + attributes.put("Implementation-Version", project.version) + attributes.put("Implementation-Vendor", "Hypertrace") + } + } +} diff --git a/testing-common/src/testFixtures/java/org/hypertrace/agent/testing/mockfilter/MockFilter.java b/tests-extension/src/main/java/org/hypertrace/javaagent/mockfilter/MockFilter.java similarity index 97% rename from testing-common/src/testFixtures/java/org/hypertrace/agent/testing/mockfilter/MockFilter.java rename to tests-extension/src/main/java/org/hypertrace/javaagent/mockfilter/MockFilter.java index 626a88942..bc90ab640 100644 --- a/testing-common/src/testFixtures/java/org/hypertrace/agent/testing/mockfilter/MockFilter.java +++ b/tests-extension/src/main/java/org/hypertrace/javaagent/mockfilter/MockFilter.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.hypertrace.agent.testing.mockfilter; +package org.hypertrace.javaagent.mockfilter; import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.trace.Span; diff --git a/testing-common/src/testFixtures/java/org/hypertrace/agent/testing/mockfilter/MockFilterProvider.java b/tests-extension/src/main/java/org/hypertrace/javaagent/mockfilter/MockFilterProvider.java similarity index 81% rename from testing-common/src/testFixtures/java/org/hypertrace/agent/testing/mockfilter/MockFilterProvider.java rename to tests-extension/src/main/java/org/hypertrace/javaagent/mockfilter/MockFilterProvider.java index 7124b2d9f..281ebb157 100644 --- a/testing-common/src/testFixtures/java/org/hypertrace/agent/testing/mockfilter/MockFilterProvider.java +++ b/tests-extension/src/main/java/org/hypertrace/javaagent/mockfilter/MockFilterProvider.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.hypertrace.agent.testing.mockfilter; +package org.hypertrace.javaagent.mockfilter; import com.google.auto.service.AutoService; import org.hypertrace.agent.filter.api.Filter; @@ -24,6 +24,13 @@ @AutoService(FilterProvider.class) public class MockFilterProvider implements FilterProvider { + /** + * This no-args constructor must be available for instantiation by the {@link + * java.util.ServiceLoader} API + */ + @SuppressWarnings("unused") + public MockFilterProvider() {} + @Override public Filter create(FilterProviderConfig config) { return new MockFilter();