diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerRoute.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerRoute.java index 5e652ba9c2c0..267fbdbf4428 100644 --- a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerRoute.java +++ b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerRoute.java @@ -31,13 +31,16 @@ public final class HttpServerRoute { */ public static ContextCustomizer create( HttpServerAttributesGetter getter) { - return (context, request, startAttributes) -> { - if (HttpRouteState.fromContextOrNull(context) != null) { - return context; - } - String method = getter.getHttpRequestMethod(request); - return context.with(HttpRouteState.create(method, null, 0)); - }; + return builder(getter).build(); + } + + /** + * Returns a new {@link HttpServerRouteBuilder} that can be used to configure the {@link + * HttpServerRoute}. + */ + public static HttpServerRouteBuilder builder( + HttpServerAttributesGetter getter) { + return new HttpServerRouteBuilder<>(getter); } private HttpServerRoute() {} @@ -147,11 +150,8 @@ private static boolean isBetterRoute(HttpRouteState httpRouteState, String name) private static void updateSpanName(Span serverSpan, HttpRouteState httpRouteState, String route) { String method = httpRouteState.getMethod(); - // method should never really be null - but in case it for some reason is, we'll rely on the - // span name extractor behavior - if (method != null) { - serverSpan.updateName(method + " " + route); - } + // method should never really be null + serverSpan.updateName(method + " " + route); } /** diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerRouteBuilder.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerRouteBuilder.java new file mode 100644 index 000000000000..4608dc9d5252 --- /dev/null +++ b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerRouteBuilder.java @@ -0,0 +1,65 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.api.instrumenter.http; + +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.api.instrumenter.ContextCustomizer; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.api.internal.HttpConstants; +import io.opentelemetry.instrumentation.api.internal.HttpRouteState; +import java.util.HashSet; +import java.util.Set; + +/** A builder of {@link HttpSpanNameExtractor}. */ +public final class HttpServerRouteBuilder { + + final HttpServerAttributesGetter getter; + Set knownMethods = HttpConstants.KNOWN_METHODS; + + HttpServerRouteBuilder(HttpServerAttributesGetter getter) { + this.getter = getter; + } + + /** + * Configures the customizer to recognize an alternative set of HTTP request methods. + * + *

By default, this customizer defines "known" methods as the ones listed in RFC9110 and the PATCH + * method defined in RFC5789. If an + * unknown method is encountered, the customizer will use the value {@value HttpConstants#_OTHER} + * instead. + * + *

Note: calling this method overrides the default known method sets completely; it does + * not supplement it. + * + * @param knownMethods A set of recognized HTTP request methods. + */ + @CanIgnoreReturnValue + public HttpServerRouteBuilder setKnownMethods(Set knownMethods) { + this.knownMethods = new HashSet<>(knownMethods); + return this; + } + + /** + * Returns a {@link ContextCustomizer} that initializes an {@link HttpServerRoute} in the {@link + * Context} returned from {@link Instrumenter#start(Context, Object)}. The returned customizer is + * configured with the settings of this {@link HttpServerRouteBuilder}. + */ + public ContextCustomizer build() { + Set knownMethods = new HashSet<>(this.knownMethods); + return (context, request, startAttributes) -> { + if (HttpRouteState.fromContextOrNull(context) != null) { + return context; + } + String method = getter.getHttpRequestMethod(request); + if (method == null || !knownMethods.contains(method)) { + method = "HTTP"; + } + return context.with(HttpRouteState.create(method, null, 0)); + }; + } +} diff --git a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerRouteTest.java b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerRouteTest.java index f252b59c90fa..4df036ad27cb 100644 --- a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerRouteTest.java +++ b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerRouteTest.java @@ -6,6 +6,7 @@ package io.opentelemetry.instrumentation.api.instrumenter.http; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static java.util.Collections.singletonList; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.Mockito.when; @@ -14,6 +15,7 @@ import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.sdk.testing.junit5.OpenTelemetryExtension; +import java.util.HashSet; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -33,7 +35,10 @@ class HttpServerRouteTest { void setUp() { instrumenter = Instrumenter.builder(testing.getOpenTelemetry(), "test", s -> s) - .addContextCustomizer(HttpServerRoute.create(getter)) + .addContextCustomizer( + HttpServerRoute.builder(getter) + .setKnownMethods(new HashSet<>(singletonList("GET"))) + .build()) .buildInstrumenter(); } @@ -158,7 +163,7 @@ void shouldNotUpdateRoute_worseMatch() { } @Test - void shouldNotUpdateSpanName_noMethod() { + void shouldUseHttp_noMethod() { when(getter.getHttpRequestMethod("test")).thenReturn(null); Context context = instrumenter.start(Context.root(), "test"); @@ -169,6 +174,23 @@ void shouldNotUpdateSpanName_noMethod() { instrumenter.end(context, "test", null, null); assertEquals("/get/:id", HttpServerRoute.get(context)); - assertThat(testing.getSpans()).satisfiesExactly(span -> assertThat(span).hasName("test")); + assertThat(testing.getSpans()) + .satisfiesExactly(span -> assertThat(span).hasName("HTTP /get/:id")); + } + + @Test + void shouldUseHttp_unknownMethod() { + when(getter.getHttpRequestMethod("test")).thenReturn("POST"); + + Context context = instrumenter.start(Context.root(), "test"); + assertNull(HttpServerRoute.get(context)); + + HttpServerRoute.update(context, HttpServerRouteSource.SERVER, "/get/:id"); + + instrumenter.end(context, "test", null, null); + + assertEquals("/get/:id", HttpServerRoute.get(context)); + assertThat(testing.getSpans()) + .satisfiesExactly(span -> assertThat(span).hasName("HTTP /get/:id")); } } diff --git a/instrumentation/akka/akka-http-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkahttp/server/AkkaHttpServerSingletons.java b/instrumentation/akka/akka-http-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkahttp/server/AkkaHttpServerSingletons.java index 07e93a9e2481..8a1ede91276b 100644 --- a/instrumentation/akka/akka-http-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkahttp/server/AkkaHttpServerSingletons.java +++ b/instrumentation/akka/akka-http-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkahttp/server/AkkaHttpServerSingletons.java @@ -40,7 +40,10 @@ public final class AkkaHttpServerSingletons { .setKnownMethods(CommonConfig.get().getKnownHttpRequestMethods()) .build()) .addOperationMetrics(HttpServerMetrics.get()) - .addContextCustomizer(HttpServerRoute.create(httpAttributesGetter)); + .addContextCustomizer( + HttpServerRoute.builder(httpAttributesGetter) + .setKnownMethods(CommonConfig.get().getKnownHttpRequestMethods()) + .build()); if (CommonConfig.get().shouldEmitExperimentalHttpServerMetrics()) { builder.addOperationMetrics(HttpServerExperimentalMetrics.get()); } diff --git a/instrumentation/akka/akka-http-10.0/javaagent/src/test/scala/io/opentelemetry/javaagent/instrumentation/akkahttp/AbstractHttpServerInstrumentationTest.scala b/instrumentation/akka/akka-http-10.0/javaagent/src/test/scala/io/opentelemetry/javaagent/instrumentation/akkahttp/AbstractHttpServerInstrumentationTest.scala index 58b98f31d16b..8cbba92195ac 100644 --- a/instrumentation/akka/akka-http-10.0/javaagent/src/test/scala/io/opentelemetry/javaagent/instrumentation/akkahttp/AbstractHttpServerInstrumentationTest.scala +++ b/instrumentation/akka/akka-http-10.0/javaagent/src/test/scala/io/opentelemetry/javaagent/instrumentation/akkahttp/AbstractHttpServerInstrumentationTest.scala @@ -35,5 +35,7 @@ abstract class AbstractHttpServerInstrumentationTest t != ServerEndpoint.EXCEPTION } ) + // instrumentation does not create a span at all + options.disableTestNonStandardHttpMethod } } diff --git a/instrumentation/armeria-1.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/armeria/v1_3/ArmeriaHttpServerTest.java b/instrumentation/armeria-1.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/armeria/v1_3/ArmeriaHttpServerTest.java index 862bda592832..930e42b217b6 100644 --- a/instrumentation/armeria-1.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/armeria/v1_3/ArmeriaHttpServerTest.java +++ b/instrumentation/armeria-1.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/armeria/v1_3/ArmeriaHttpServerTest.java @@ -29,5 +29,6 @@ protected void configure(HttpServerTestOptions options) { options.setHasResponseCustomizer( endpoint -> ServerEndpoint.NOT_FOUND != endpoint && ServerEndpoint.EXCEPTION != endpoint); options.setTestHttpPipelining(false); + options.setResponseCodeOnNonStandardHttpMethod(405); } } diff --git a/instrumentation/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/ArmeriaTelemetryBuilder.java b/instrumentation/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/ArmeriaTelemetryBuilder.java index f9706afd5009..11e916b99f13 100644 --- a/instrumentation/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/ArmeriaTelemetryBuilder.java +++ b/instrumentation/armeria-1.3/library/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/ArmeriaTelemetryBuilder.java @@ -24,6 +24,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerExperimentalMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerRoute; +import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerRouteBuilder; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractorBuilder; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; @@ -62,6 +63,9 @@ public final class ArmeriaTelemetryBuilder { private final HttpSpanNameExtractorBuilder httpServerSpanNameExtractorBuilder = HttpSpanNameExtractor.builder(ArmeriaHttpServerAttributesGetter.INSTANCE); + private final HttpServerRouteBuilder httpServerRouteBuilder = + HttpServerRoute.builder(ArmeriaHttpServerAttributesGetter.INSTANCE); + private Function< SpanStatusExtractor, ? extends SpanStatusExtractor> @@ -175,6 +179,7 @@ public ArmeriaTelemetryBuilder setKnownMethods(Set knownMethods) { httpServerAttributesExtractorBuilder.setKnownMethods(knownMethods); httpClientSpanNameExtractorBuilder.setKnownMethods(knownMethods); httpServerSpanNameExtractorBuilder.setKnownMethods(knownMethods); + httpServerRouteBuilder.setKnownMethods(knownMethods); return this; } @@ -233,7 +238,7 @@ public ArmeriaTelemetry build() { HttpSpanStatusExtractor.create(serverAttributesGetter))) .addAttributesExtractor(httpServerAttributesExtractorBuilder.build()) .addOperationMetrics(HttpServerMetrics.get()) - .addContextCustomizer(HttpServerRoute.create(serverAttributesGetter)); + .addContextCustomizer(httpServerRouteBuilder.build()); if (peerService != null) { clientInstrumenterBuilder.addAttributesExtractor( diff --git a/instrumentation/armeria-1.3/library/src/test/java/io/opentelemetry/instrumentation/armeria/v1_3/ArmeriaHttpServerTest.java b/instrumentation/armeria-1.3/library/src/test/java/io/opentelemetry/instrumentation/armeria/v1_3/ArmeriaHttpServerTest.java index 0faf2f0ec62f..283261595fa8 100644 --- a/instrumentation/armeria-1.3/library/src/test/java/io/opentelemetry/instrumentation/armeria/v1_3/ArmeriaHttpServerTest.java +++ b/instrumentation/armeria-1.3/library/src/test/java/io/opentelemetry/instrumentation/armeria/v1_3/ArmeriaHttpServerTest.java @@ -9,6 +9,7 @@ import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpServerTest; import io.opentelemetry.instrumentation.testing.junit.http.HttpServerInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions; import java.util.Collections; import org.junit.jupiter.api.extension.RegisterExtension; @@ -28,4 +29,11 @@ protected ServerBuilder configureServer(ServerBuilder sb) { .build() .newServiceDecorator()); } + + @Override + protected void configure(HttpServerTestOptions options) { + super.configure(options); + // library instrumentation does not create a span at all + options.disableTestNonStandardHttpMethod(); + } } diff --git a/instrumentation/armeria-1.3/testing/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/AbstractArmeriaHttpServerTest.java b/instrumentation/armeria-1.3/testing/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/AbstractArmeriaHttpServerTest.java index 7c684f4e8040..4f8e008ba6ef 100644 --- a/instrumentation/armeria-1.3/testing/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/AbstractArmeriaHttpServerTest.java +++ b/instrumentation/armeria-1.3/testing/src/main/java/io/opentelemetry/instrumentation/armeria/v1_3/AbstractArmeriaHttpServerTest.java @@ -178,13 +178,13 @@ protected void stopServer(Server server) { @Override protected void configure(HttpServerTestOptions options) { options.setExpectedHttpRoute( - endpoint -> { + (endpoint, method) -> { if (endpoint == ServerEndpoint.NOT_FOUND) { // TODO(anuraaga): Revisit this when applying instrumenters to more libraries, Armeria // currently reports '/*' which is a fallback route. return "/*"; } - return expectedHttpRoute(endpoint); + return expectedHttpRoute(endpoint, method); }); options.setTestPathParam(true); diff --git a/instrumentation/dropwizard/dropwizard-testing/src/test/groovy/DropwizardTest.groovy b/instrumentation/dropwizard/dropwizard-testing/src/test/groovy/DropwizardTest.groovy index a121252656d3..a5c099d99088 100644 --- a/instrumentation/dropwizard/dropwizard-testing/src/test/groovy/DropwizardTest.groovy +++ b/instrumentation/dropwizard/dropwizard-testing/src/test/groovy/DropwizardTest.groovy @@ -83,14 +83,14 @@ class DropwizardTest extends HttpServerTest implements Ag } @Override - String expectedHttpRoute(ServerEndpoint endpoint) { + String expectedHttpRoute(ServerEndpoint endpoint, String method) { switch (endpoint) { case NOT_FOUND: return getContextPath() + "/*" case PATH_PARAM: return getContextPath() + "/path/{id}/param" default: - return super.expectedHttpRoute(endpoint) + return super.expectedHttpRoute(endpoint, method) } } diff --git a/instrumentation/finatra-2.9/javaagent/src/test/scala/io/opentelemetry/javaagent/instrumentation/finatra/FinatraServerTest.scala b/instrumentation/finatra-2.9/javaagent/src/test/scala/io/opentelemetry/javaagent/instrumentation/finatra/FinatraServerTest.scala index 55ef9ac83291..1ecd06dd8c36 100644 --- a/instrumentation/finatra-2.9/javaagent/src/test/scala/io/opentelemetry/javaagent/instrumentation/finatra/FinatraServerTest.scala +++ b/instrumentation/finatra-2.9/javaagent/src/test/scala/io/opentelemetry/javaagent/instrumentation/finatra/FinatraServerTest.scala @@ -56,6 +56,7 @@ class FinatraServerTest extends AbstractHttpServerTest[HttpServer] { override def test(endpoint: ServerEndpoint): Boolean = endpoint != ServerEndpoint.NOT_FOUND }) + options.setResponseCodeOnNonStandardHttpMethod(400) } override protected def assertHandlerSpan( diff --git a/instrumentation/grails-3.0/javaagent/build.gradle.kts b/instrumentation/grails-3.0/javaagent/build.gradle.kts index d9eaba9ffb0e..c741ff5d6952 100644 --- a/instrumentation/grails-3.0/javaagent/build.gradle.kts +++ b/instrumentation/grails-3.0/javaagent/build.gradle.kts @@ -65,12 +65,16 @@ configurations.testRuntimeClasspath { } } +val latestDepTest = findProperty("testLatestDeps") as Boolean + tasks { val testStableSemconv by registering(Test::class) { jvmArgs("-Dotel.semconv-stability.opt-in=http") } withType().configureEach { + systemProperty("testLatestDeps", latestDepTest) + // required on jdk17 jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") diff --git a/instrumentation/grails-3.0/javaagent/src/test/java/test/GrailsTest.java b/instrumentation/grails-3.0/javaagent/src/test/java/test/GrailsTest.java index 94941605f9a7..85d91c7501db 100644 --- a/instrumentation/grails-3.0/javaagent/src/test/java/test/GrailsTest.java +++ b/instrumentation/grails-3.0/javaagent/src/test/java/test/GrailsTest.java @@ -19,6 +19,7 @@ import grails.boot.config.GrailsAutoConfiguration; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.api.internal.HttpConstants; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpServerTest; import io.opentelemetry.instrumentation.testing.junit.http.HttpServerInstrumentationExtension; @@ -42,6 +43,8 @@ public class GrailsTest extends AbstractHttpServerTest { + static final boolean testLatestDeps = Boolean.getBoolean("testLatestDeps"); + @RegisterExtension static final InstrumentationExtension testing = HttpServerInstrumentationExtension.forAgent(); @@ -64,6 +67,7 @@ protected void configure(HttpServerTestOptions options) { options.setHasErrorPageSpans( endpoint -> endpoint == ERROR || endpoint == EXCEPTION || endpoint == NOT_FOUND); options.setTestPathParam(true); + options.setResponseCodeOnNonStandardHttpMethod(testLatestDeps ? 200 : 501); } @SpringBootApplication @@ -106,7 +110,12 @@ private static Class load(String name) { } @Override - public String expectedHttpRoute(ServerEndpoint endpoint) { + public String expectedHttpRoute(ServerEndpoint endpoint, String method) { + if (HttpConstants._OTHER.equals(method)) { + return testLatestDeps + ? getContextPath() + "/test" + endpoint.getPath() + : getContextPath() + "/*"; + } if (PATH_PARAM.equals(endpoint)) { return getContextPath() + "/test/path"; } else if (QUERY_PARAM.equals(endpoint)) { diff --git a/instrumentation/grizzly-2.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grizzly/GrizzlySingletons.java b/instrumentation/grizzly-2.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grizzly/GrizzlySingletons.java index 8bab9d6430ed..807d1f9907e5 100644 --- a/instrumentation/grizzly-2.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grizzly/GrizzlySingletons.java +++ b/instrumentation/grizzly-2.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grizzly/GrizzlySingletons.java @@ -54,7 +54,10 @@ public final class GrizzlySingletons { .init(context)) .addContextCustomizer( (context, httpRequestPacket, startAttributes) -> GrizzlyErrorHolder.init(context)) - .addContextCustomizer(HttpServerRoute.create(httpAttributesGetter)) + .addContextCustomizer( + HttpServerRoute.builder(httpAttributesGetter) + .setKnownMethods(CommonConfig.get().getKnownHttpRequestMethods()) + .build()) .buildServerInstrumenter(HttpRequestHeadersGetter.INSTANCE); } diff --git a/instrumentation/jaxrs/jaxrs-common/testing/src/main/groovy/AbstractJaxRsHttpServerTest.groovy b/instrumentation/jaxrs/jaxrs-common/testing/src/main/groovy/AbstractJaxRsHttpServerTest.groovy index a2a6c2c44aaf..234c7ee9bede 100644 --- a/instrumentation/jaxrs/jaxrs-common/testing/src/main/groovy/AbstractJaxRsHttpServerTest.groovy +++ b/instrumentation/jaxrs/jaxrs-common/testing/src/main/groovy/AbstractJaxRsHttpServerTest.groovy @@ -229,7 +229,6 @@ abstract class AbstractJaxRsHttpServerTest extends HttpServerTest implemen String traceID = null, String parentID = null, String method = "GET", - Long responseContentLength = null, ServerEndpoint endpoint = SUCCESS, String spanID = null) { serverSpan(trace, index, traceID, parentID, spanID, method, diff --git a/instrumentation/ktor/ktor-1.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/KtorServerTracing.kt b/instrumentation/ktor/ktor-1.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/KtorServerTracing.kt index f3073fc22303..1a9c896f4844 100644 --- a/instrumentation/ktor/ktor-1.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/KtorServerTracing.kt +++ b/instrumentation/ktor/ktor-1.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/KtorServerTracing.kt @@ -40,6 +40,8 @@ class KtorServerTracing private constructor( internal val httpSpanNameExtractorBuilder = HttpSpanNameExtractor.builder(KtorHttpServerAttributesGetter.INSTANCE) + internal val httpServerRouteBuilder = HttpServerRoute.builder(KtorHttpServerAttributesGetter.INSTANCE) + internal var statusExtractor: (SpanStatusExtractor) -> SpanStatusExtractor = { a -> a } @@ -77,6 +79,7 @@ class KtorServerTracing private constructor( fun setKnownMethods(knownMethods: Set) { httpAttributesExtractorBuilder.setKnownMethods(knownMethods) httpSpanNameExtractorBuilder.setKnownMethods(knownMethods) + httpServerRouteBuilder.setKnownMethods(knownMethods) } internal fun isOpenTelemetryInitialized(): Boolean = this::openTelemetry.isInitialized @@ -124,7 +127,7 @@ class KtorServerTracing private constructor( setSpanStatusExtractor(configuration.statusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter))) addAttributesExtractor(configuration.httpAttributesExtractorBuilder.build()) addOperationMetrics(HttpServerMetrics.get()) - addContextCustomizer(HttpServerRoute.create(httpAttributesGetter)) + addContextCustomizer(configuration.httpServerRouteBuilder.build()) } val instrumenter = InstrumenterUtil.buildUpstreamInstrumenter( diff --git a/instrumentation/ktor/ktor-1.0/library/src/test/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/KtorHttpServerTest.kt b/instrumentation/ktor/ktor-1.0/library/src/test/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/KtorHttpServerTest.kt index 9c2b4a84761f..336b2db73758 100644 --- a/instrumentation/ktor/ktor-1.0/library/src/test/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/KtorHttpServerTest.kt +++ b/instrumentation/ktor/ktor-1.0/library/src/test/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/KtorHttpServerTest.kt @@ -129,10 +129,10 @@ class KtorHttpServerTest : AbstractHttpServerTest() { HttpServerTestOptions.DEFAULT_HTTP_ATTRIBUTES - SemanticAttributes.NET_PEER_PORT } - options.setExpectedHttpRoute { - when (it) { + options.setExpectedHttpRoute { endpoint, method -> + when (endpoint) { ServerEndpoint.PATH_PARAM -> "/path/{id}/param" - else -> expectedHttpRoute(it) + else -> expectedHttpRoute(endpoint, method) } } // ktor does not have a controller lifecycle so the server span ends immediately when the diff --git a/instrumentation/ktor/ktor-2.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/server/KtorServerTracing.kt b/instrumentation/ktor/ktor-2.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/server/KtorServerTracing.kt index 18d28944dff8..86c765c93847 100644 --- a/instrumentation/ktor/ktor-2.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/server/KtorServerTracing.kt +++ b/instrumentation/ktor/ktor-2.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/server/KtorServerTracing.kt @@ -41,6 +41,8 @@ class KtorServerTracing private constructor( internal val httpSpanNameExtractorBuilder = HttpSpanNameExtractor.builder(KtorHttpServerAttributesGetter.INSTANCE) + internal val httpServerRouteBuilder = HttpServerRoute.builder(KtorHttpServerAttributesGetter.INSTANCE) + internal var statusExtractor: (SpanStatusExtractor) -> SpanStatusExtractor = { a -> a } @@ -78,6 +80,7 @@ class KtorServerTracing private constructor( fun setKnownMethods(knownMethods: Set) { httpAttributesExtractorBuilder.setKnownMethods(knownMethods) httpSpanNameExtractorBuilder.setKnownMethods(knownMethods) + httpServerRouteBuilder.setKnownMethods(knownMethods) } internal fun isOpenTelemetryInitialized(): Boolean = this::openTelemetry.isInitialized @@ -124,7 +127,7 @@ class KtorServerTracing private constructor( setSpanStatusExtractor(configuration.statusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter))) addAttributesExtractor(configuration.httpAttributesExtractorBuilder.build()) addOperationMetrics(HttpServerMetrics.get()) - addContextCustomizer(HttpServerRoute.create(httpAttributesGetter)) + addContextCustomizer(configuration.httpServerRouteBuilder.build()) } val instrumenter = InstrumenterUtil.buildUpstreamInstrumenter( diff --git a/instrumentation/ktor/ktor-2.0/testing/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/server/AbstractKtorHttpServerTest.kt b/instrumentation/ktor/ktor-2.0/testing/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/server/AbstractKtorHttpServerTest.kt index 3fff8bea2ebb..42eb2007a7c8 100644 --- a/instrumentation/ktor/ktor-2.0/testing/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/server/AbstractKtorHttpServerTest.kt +++ b/instrumentation/ktor/ktor-2.0/testing/src/main/kotlin/io/opentelemetry/instrumentation/ktor/v2_0/server/AbstractKtorHttpServerTest.kt @@ -126,10 +126,10 @@ abstract class AbstractKtorHttpServerTest : AbstractHttpServerTest + when (endpoint) { ServerEndpoint.PATH_PARAM -> "/path/{id}/param" - else -> expectedHttpRoute(it) + else -> expectedHttpRoute(endpoint, method) } } diff --git a/instrumentation/liberty/liberty-dispatcher-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyDispatcherSingletons.java b/instrumentation/liberty/liberty-dispatcher-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyDispatcherSingletons.java index b53f52b9006d..44879bc1fa65 100644 --- a/instrumentation/liberty/liberty-dispatcher-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyDispatcherSingletons.java +++ b/instrumentation/liberty/liberty-dispatcher-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/liberty/dispatcher/LibertyDispatcherSingletons.java @@ -39,7 +39,10 @@ public final class LibertyDispatcherSingletons { .setCapturedResponseHeaders(CommonConfig.get().getServerResponseHeaders()) .setKnownMethods(CommonConfig.get().getKnownHttpRequestMethods()) .build()) - .addContextCustomizer(HttpServerRoute.create(httpAttributesGetter)) + .addContextCustomizer( + HttpServerRoute.builder(httpAttributesGetter) + .setKnownMethods(CommonConfig.get().getKnownHttpRequestMethods()) + .build()) .addOperationMetrics(HttpServerMetrics.get()); if (CommonConfig.get().shouldEmitExperimentalHttpServerMetrics()) { builder.addOperationMetrics(HttpServerExperimentalMetrics.get()); diff --git a/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/server/NettyServerSingletons.java b/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/server/NettyServerSingletons.java index 0f251ae3e32d..51d4134fca62 100644 --- a/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/server/NettyServerSingletons.java +++ b/instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/server/NettyServerSingletons.java @@ -49,7 +49,10 @@ final class NettyServerSingletons { builder .addContextCustomizer( (context, requestAndChannel, startAttributes) -> NettyErrorHolder.init(context)) - .addContextCustomizer(HttpServerRoute.create(httpServerAttributesGetter)) + .addContextCustomizer( + HttpServerRoute.builder(httpServerAttributesGetter) + .setKnownMethods(CommonConfig.get().getKnownHttpRequestMethods()) + .build()) .buildServerInstrumenter(NettyHeadersGetter.INSTANCE); } diff --git a/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/server/NettyServerInstrumenterFactory.java b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/server/NettyServerInstrumenterFactory.java index 28ab52bb86ee..a0029c3d72e8 100644 --- a/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/server/NettyServerInstrumenterFactory.java +++ b/instrumentation/netty/netty-4-common/library/src/main/java/io/opentelemetry/instrumentation/netty/v4/common/internal/server/NettyServerInstrumenterFactory.java @@ -14,6 +14,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerExperimentalMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerRoute; +import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerRouteBuilder; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractorBuilder; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; @@ -33,6 +34,7 @@ public static Instrumenter create( Consumer> extractorConfigurer, Consumer> spanNameExtractorConfigurer, + Consumer> httpServerRouteConfigurer, boolean emitExperimentalHttpServerMetrics) { NettyHttpServerAttributesGetter httpAttributesGetter = new NettyHttpServerAttributesGetter(); @@ -55,9 +57,13 @@ public static Instrumenter create( builder.addOperationMetrics(HttpServerExperimentalMetrics.get()); } + HttpServerRouteBuilder httpServerRouteBuilder = + HttpServerRoute.builder(httpAttributesGetter); + httpServerRouteConfigurer.accept(httpServerRouteBuilder); + return builder .addContextCustomizer((context, request, attributes) -> NettyErrorHolder.init(context)) - .addContextCustomizer(HttpServerRoute.create(httpAttributesGetter)) + .addContextCustomizer(httpServerRouteBuilder.build()) .buildServerInstrumenter(HttpRequestHeadersGetter.INSTANCE); } diff --git a/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/server/NettyServerSingletons.java b/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/server/NettyServerSingletons.java index 054735d0e90a..3cb0a536d38c 100644 --- a/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/server/NettyServerSingletons.java +++ b/instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/server/NettyServerSingletons.java @@ -24,6 +24,7 @@ public final class NettyServerSingletons { .setCapturedResponseHeaders(CommonConfig.get().getServerResponseHeaders()) .setKnownMethods(CommonConfig.get().getKnownHttpRequestMethods()), builder -> builder.setKnownMethods(CommonConfig.get().getKnownHttpRequestMethods()), + builder -> builder.setKnownMethods(CommonConfig.get().getKnownHttpRequestMethods()), CommonConfig.get().shouldEmitExperimentalHttpServerMetrics()); public static Instrumenter instrumenter() { diff --git a/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/NettyServerSingletons.java b/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/NettyServerSingletons.java index 3675e720a503..0e4ed3436da4 100644 --- a/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/NettyServerSingletons.java +++ b/instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/NettyServerSingletons.java @@ -24,6 +24,7 @@ public final class NettyServerSingletons { .setCapturedResponseHeaders(CommonConfig.get().getServerResponseHeaders()) .setKnownMethods(CommonConfig.get().getKnownHttpRequestMethods()), builder -> builder.setKnownMethods(CommonConfig.get().getKnownHttpRequestMethods()), + builder -> builder.setKnownMethods(CommonConfig.get().getKnownHttpRequestMethods()), CommonConfig.get().shouldEmitExperimentalHttpServerMetrics()); public static Instrumenter instrumenter() { diff --git a/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/NettyServerTelemetryBuilder.java b/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/NettyServerTelemetryBuilder.java index 7912b9bd5a81..f1c0d4f8aeb2 100644 --- a/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/NettyServerTelemetryBuilder.java +++ b/instrumentation/netty/netty-4.1/library/src/main/java/io/opentelemetry/instrumentation/netty/v4_1/NettyServerTelemetryBuilder.java @@ -9,6 +9,7 @@ import io.netty.handler.codec.http.HttpResponse; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerAttributesExtractorBuilder; +import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerRouteBuilder; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractorBuilder; import io.opentelemetry.instrumentation.netty.v4.common.HttpRequestAndChannel; import io.opentelemetry.instrumentation.netty.v4.common.internal.server.NettyServerInstrumenterFactory; @@ -25,6 +26,8 @@ public final class NettyServerTelemetryBuilder { extractorConfigurer = builder -> {}; private Consumer> spanNameExtractorConfigurer = builder -> {}; + private Consumer> httpServerRouteConfigurer = + builder -> {}; private boolean emitExperimentalHttpServerMetrics = false; NettyServerTelemetryBuilder(OpenTelemetry openTelemetry) { @@ -78,6 +81,8 @@ public NettyServerTelemetryBuilder setKnownMethods(Set knownMethods) { extractorConfigurer.andThen(builder -> builder.setKnownMethods(knownMethods)); spanNameExtractorConfigurer = spanNameExtractorConfigurer.andThen(builder -> builder.setKnownMethods(knownMethods)); + httpServerRouteConfigurer = + httpServerRouteConfigurer.andThen(builder -> builder.setKnownMethods(knownMethods)); return this; } @@ -102,6 +107,7 @@ public NettyServerTelemetry build() { "io.opentelemetry.netty-4.1", extractorConfigurer, spanNameExtractorConfigurer, + httpServerRouteConfigurer, emitExperimentalHttpServerMetrics)); } } diff --git a/instrumentation/play/play-mvc/play-mvc-2.4/javaagent/src/test/groovy/server/PlayServerTest.groovy b/instrumentation/play/play-mvc/play-mvc-2.4/javaagent/src/test/groovy/server/PlayServerTest.groovy index 50accc3447f4..60896ca8cb07 100644 --- a/instrumentation/play/play-mvc/play-mvc-2.4/javaagent/src/test/groovy/server/PlayServerTest.groovy +++ b/instrumentation/play/play-mvc/play-mvc-2.4/javaagent/src/test/groovy/server/PlayServerTest.groovy @@ -115,4 +115,9 @@ class PlayServerTest extends HttpServerTest implements AgentTestTrait { attributes.remove(SemanticAttributes.HTTP_ROUTE) attributes } + + @Override + int getResponseCodeOnNonStandardHttpMethod() { + 404 + } } diff --git a/instrumentation/play/play-mvc/play-mvc-2.6/javaagent/src/test/groovy/server/PlayServerTest.groovy b/instrumentation/play/play-mvc/play-mvc-2.6/javaagent/src/test/groovy/server/PlayServerTest.groovy index 0bc7c3802b41..9f3ab3cb7759 100644 --- a/instrumentation/play/play-mvc/play-mvc-2.6/javaagent/src/test/groovy/server/PlayServerTest.groovy +++ b/instrumentation/play/play-mvc/play-mvc-2.6/javaagent/src/test/groovy/server/PlayServerTest.groovy @@ -105,4 +105,10 @@ class PlayServerTest extends HttpServerTest implements AgentTestTrait { Set> httpAttributes(ServerEndpoint endpoint) { [] } + + @Override + boolean testNonStandardHttpMethod() { + // instrumentation does not create a server span at all + false + } } diff --git a/instrumentation/ratpack/ratpack-1.4/testing/src/main/groovy/io/opentelemetry/instrumentation/ratpack/server/AbstractRatpackHttpServerTest.groovy b/instrumentation/ratpack/ratpack-1.4/testing/src/main/groovy/io/opentelemetry/instrumentation/ratpack/server/AbstractRatpackHttpServerTest.groovy index 67b73a52c652..61735d098984 100644 --- a/instrumentation/ratpack/ratpack-1.4/testing/src/main/groovy/io/opentelemetry/instrumentation/ratpack/server/AbstractRatpackHttpServerTest.groovy +++ b/instrumentation/ratpack/ratpack-1.4/testing/src/main/groovy/io/opentelemetry/instrumentation/ratpack/server/AbstractRatpackHttpServerTest.groovy @@ -153,7 +153,7 @@ abstract class AbstractRatpackHttpServerTest extends HttpServerTest httpServerSpanNameExtractorBuilder = HttpSpanNameExtractor.builder(RatpackHttpAttributesGetter.INSTANCE); + private final HttpServerRouteBuilder httpServerRouteBuilder = + HttpServerRoute.builder(RatpackHttpAttributesGetter.INSTANCE); + private final List> additionalHttpClientExtractors = new ArrayList<>(); @@ -143,6 +147,7 @@ public RatpackTelemetryBuilder setKnownMethods(Set knownMethods) { httpServerAttributesExtractorBuilder.setKnownMethods(knownMethods); httpClientSpanNameExtractorBuilder.setKnownMethods(knownMethods); httpServerSpanNameExtractorBuilder.setKnownMethods(knownMethods); + httpServerRouteBuilder.setKnownMethods(knownMethods); return this; } @@ -187,7 +192,7 @@ private Instrumenter buildServerInstrumenter() { .addAttributesExtractor(httpServerAttributesExtractorBuilder.build()) .addAttributesExtractors(additionalExtractors) .addOperationMetrics(HttpServerMetrics.get()) - .addContextCustomizer(HttpServerRoute.create(httpAttributes)); + .addContextCustomizer(httpServerRouteBuilder.build()); if (emitExperimentalHttpServerMetrics) { builder.addOperationMetrics(HttpServerExperimentalMetrics.get()); } diff --git a/instrumentation/restlet/restlet-1.1/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/restlet/v1_1/RestletServerTest.groovy b/instrumentation/restlet/restlet-1.1/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/restlet/v1_1/RestletServerTest.groovy index d6d738354297..76aff28f054b 100644 --- a/instrumentation/restlet/restlet-1.1/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/restlet/v1_1/RestletServerTest.groovy +++ b/instrumentation/restlet/restlet-1.1/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/restlet/v1_1/RestletServerTest.groovy @@ -14,12 +14,12 @@ import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint class RestletServerTest extends AbstractRestletServerTest implements AgentTestTrait { @Override - String expectedHttpRoute(ServerEndpoint endpoint) { + String expectedHttpRoute(ServerEndpoint endpoint, String method) { switch (endpoint) { case NOT_FOUND: return getContextPath() + "/" default: - return super.expectedHttpRoute(endpoint) + return super.expectedHttpRoute(endpoint, method) } } diff --git a/instrumentation/restlet/restlet-1.1/library/src/main/java/io/opentelemetry/instrumentation/restlet/v1_1/RestletTelemetryBuilder.java b/instrumentation/restlet/restlet-1.1/library/src/main/java/io/opentelemetry/instrumentation/restlet/v1_1/RestletTelemetryBuilder.java index a0e4f81975ed..72f4a578cbc9 100644 --- a/instrumentation/restlet/restlet-1.1/library/src/main/java/io/opentelemetry/instrumentation/restlet/v1_1/RestletTelemetryBuilder.java +++ b/instrumentation/restlet/restlet-1.1/library/src/main/java/io/opentelemetry/instrumentation/restlet/v1_1/RestletTelemetryBuilder.java @@ -15,6 +15,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerExperimentalMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerRoute; +import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerRouteBuilder; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractorBuilder; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; @@ -37,6 +38,8 @@ public final class RestletTelemetryBuilder { HttpServerAttributesExtractor.builder(RestletHttpAttributesGetter.INSTANCE); private final HttpSpanNameExtractorBuilder httpSpanNameExtractorBuilder = HttpSpanNameExtractor.builder(RestletHttpAttributesGetter.INSTANCE); + private final HttpServerRouteBuilder httpServerRouteBuilder = + HttpServerRoute.builder(RestletHttpAttributesGetter.INSTANCE); private boolean emitExperimentalHttpServerMetrics = false; RestletTelemetryBuilder(OpenTelemetry openTelemetry) { @@ -93,6 +96,7 @@ public RestletTelemetryBuilder setCapturedResponseHeaders(List responseH public RestletTelemetryBuilder setKnownMethods(Set knownMethods) { httpAttributesExtractorBuilder.setKnownMethods(knownMethods); httpSpanNameExtractorBuilder.setKnownMethods(knownMethods); + httpServerRouteBuilder.setKnownMethods(knownMethods); return this; } @@ -122,7 +126,7 @@ public RestletTelemetry build() { .setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter)) .addAttributesExtractor(httpAttributesExtractorBuilder.build()) .addAttributesExtractors(additionalExtractors) - .addContextCustomizer(HttpServerRoute.create(httpAttributesGetter)) + .addContextCustomizer(httpServerRouteBuilder.build()) .addOperationMetrics(HttpServerMetrics.get()); if (emitExperimentalHttpServerMetrics) { builder.addOperationMetrics(HttpServerExperimentalMetrics.get()); diff --git a/instrumentation/restlet/restlet-1.1/testing/src/main/groovy/io/opentelemetry/instrumentation/restlet/v1_1/AbstractRestletServerTest.groovy b/instrumentation/restlet/restlet-1.1/testing/src/main/groovy/io/opentelemetry/instrumentation/restlet/v1_1/AbstractRestletServerTest.groovy index 158ecb7a4269..c72f63a81092 100644 --- a/instrumentation/restlet/restlet-1.1/testing/src/main/groovy/io/opentelemetry/instrumentation/restlet/v1_1/AbstractRestletServerTest.groovy +++ b/instrumentation/restlet/restlet-1.1/testing/src/main/groovy/io/opentelemetry/instrumentation/restlet/v1_1/AbstractRestletServerTest.groovy @@ -168,14 +168,14 @@ abstract class AbstractRestletServerTest extends HttpServerTest { } @Override - String expectedHttpRoute(ServerEndpoint endpoint) { + String expectedHttpRoute(ServerEndpoint endpoint, String method) { switch (endpoint) { case PATH_PARAM: return getContextPath() + "/path/{id}/param" case NOT_FOUND: return getContextPath() + "/*" default: - return super.expectedHttpRoute(endpoint) + return super.expectedHttpRoute(endpoint, method) } } diff --git a/instrumentation/restlet/restlet-1.1/testing/src/main/groovy/io/opentelemetry/instrumentation/restlet/v1_1/AbstractServletServerTest.groovy b/instrumentation/restlet/restlet-1.1/testing/src/main/groovy/io/opentelemetry/instrumentation/restlet/v1_1/AbstractServletServerTest.groovy index 3c764ae8e065..6f695ec6da71 100644 --- a/instrumentation/restlet/restlet-1.1/testing/src/main/groovy/io/opentelemetry/instrumentation/restlet/v1_1/AbstractServletServerTest.groovy +++ b/instrumentation/restlet/restlet-1.1/testing/src/main/groovy/io/opentelemetry/instrumentation/restlet/v1_1/AbstractServletServerTest.groovy @@ -63,14 +63,14 @@ abstract class AbstractServletServerTest extends HttpServerTest { } @Override - String expectedHttpRoute(ServerEndpoint endpoint) { + String expectedHttpRoute(ServerEndpoint endpoint, String method) { switch (endpoint) { case PATH_PARAM: return getContextPath() + "/path/{id}/param" case NOT_FOUND: return getContextPath() + "/*" default: - return super.expectedHttpRoute(endpoint) + return super.expectedHttpRoute(endpoint, method) } } diff --git a/instrumentation/restlet/restlet-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v2_0/RestletSingletons.java b/instrumentation/restlet/restlet-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v2_0/RestletSingletons.java index 5a811253e03b..2c7a7ca70d6c 100644 --- a/instrumentation/restlet/restlet-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v2_0/RestletSingletons.java +++ b/instrumentation/restlet/restlet-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v2_0/RestletSingletons.java @@ -8,6 +8,7 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerAttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerRoute; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerRouteGetter; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; import io.opentelemetry.instrumentation.restlet.v2_0.internal.RestletHttpAttributesGetter; @@ -31,6 +32,9 @@ public final class RestletSingletons { HttpSpanNameExtractor.builder(RestletHttpAttributesGetter.INSTANCE) .setKnownMethods(CommonConfig.get().getKnownHttpRequestMethods()) .build(), + HttpServerRoute.builder(RestletHttpAttributesGetter.INSTANCE) + .setKnownMethods(CommonConfig.get().getKnownHttpRequestMethods()) + .build(), Collections.emptyList(), CommonConfig.get().shouldEmitExperimentalHttpServerMetrics()); diff --git a/instrumentation/restlet/restlet-2.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/restlet/v2_0/RestletServerTest.groovy b/instrumentation/restlet/restlet-2.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/restlet/v2_0/RestletServerTest.groovy index f3cefae657ab..3fe0f9a93c6f 100644 --- a/instrumentation/restlet/restlet-2.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/restlet/v2_0/RestletServerTest.groovy +++ b/instrumentation/restlet/restlet-2.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/restlet/v2_0/RestletServerTest.groovy @@ -14,12 +14,12 @@ import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint class RestletServerTest extends AbstractRestletServerTest implements AgentTestTrait { @Override - String expectedHttpRoute(ServerEndpoint endpoint) { + String expectedHttpRoute(ServerEndpoint endpoint, String method) { switch (endpoint) { case NOT_FOUND: return getContextPath() + "/" default: - return super.expectedHttpRoute(endpoint) + return super.expectedHttpRoute(endpoint, method) } } diff --git a/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletTelemetryBuilder.java b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletTelemetryBuilder.java index e32145ab0657..57868dff18b8 100644 --- a/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletTelemetryBuilder.java +++ b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletTelemetryBuilder.java @@ -11,6 +11,8 @@ import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerAttributesExtractorBuilder; +import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerRoute; +import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerRouteBuilder; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractorBuilder; import io.opentelemetry.instrumentation.restlet.v2_0.internal.RestletHttpAttributesGetter; @@ -32,6 +34,9 @@ public final class RestletTelemetryBuilder { HttpServerAttributesExtractor.builder(RestletHttpAttributesGetter.INSTANCE); private final HttpSpanNameExtractorBuilder httpSpanNameExtractorBuilder = HttpSpanNameExtractor.builder(RestletHttpAttributesGetter.INSTANCE); + private final HttpServerRouteBuilder httpServerRouteBuilder = + HttpServerRoute.builder(RestletHttpAttributesGetter.INSTANCE); + private boolean emitExperimentalHttpServerMetrics = false; RestletTelemetryBuilder(OpenTelemetry openTelemetry) { @@ -88,6 +93,7 @@ public RestletTelemetryBuilder setCapturedResponseHeaders(List responseH public RestletTelemetryBuilder setKnownMethods(Set knownMethods) { httpAttributesExtractorBuilder.setKnownMethods(knownMethods); httpSpanNameExtractorBuilder.setKnownMethods(knownMethods); + httpServerRouteBuilder.setKnownMethods(knownMethods); return this; } @@ -114,6 +120,7 @@ public RestletTelemetry build() { openTelemetry, httpAttributesExtractorBuilder.build(), httpSpanNameExtractorBuilder.build(), + httpServerRouteBuilder.build(), additionalExtractors, emitExperimentalHttpServerMetrics); diff --git a/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/internal/RestletInstrumenterFactory.java b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/internal/RestletInstrumenterFactory.java index 3d156a67b53d..daa063a16920 100644 --- a/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/internal/RestletInstrumenterFactory.java +++ b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/internal/RestletInstrumenterFactory.java @@ -7,12 +7,12 @@ import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.ContextCustomizer; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder; import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerExperimentalMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerMetrics; -import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerRoute; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; import java.util.List; import org.restlet.Request; @@ -30,6 +30,7 @@ public static Instrumenter newServerInstrumenter( OpenTelemetry openTelemetry, AttributesExtractor httpServerAttributesExtractor, SpanNameExtractor httpServerSpanNameExtractor, + ContextCustomizer httpServerRoute, List> additionalExtractors, boolean emitExperimentalHttpServerMetrics) { @@ -41,7 +42,7 @@ public static Instrumenter newServerInstrumenter( .setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter)) .addAttributesExtractor(httpServerAttributesExtractor) .addAttributesExtractors(additionalExtractors) - .addContextCustomizer(HttpServerRoute.create(httpAttributesGetter)) + .addContextCustomizer(httpServerRoute) .addOperationMetrics(HttpServerMetrics.get()); if (emitExperimentalHttpServerMetrics) { builder.addOperationMetrics(HttpServerExperimentalMetrics.get()); diff --git a/instrumentation/restlet/restlet-2.0/testing/src/main/groovy/io/opentelemetry/instrumentation/restlet/v2_0/AbstractRestletServerTest.groovy b/instrumentation/restlet/restlet-2.0/testing/src/main/groovy/io/opentelemetry/instrumentation/restlet/v2_0/AbstractRestletServerTest.groovy index baad1e390e7c..6ed486bf27bd 100644 --- a/instrumentation/restlet/restlet-2.0/testing/src/main/groovy/io/opentelemetry/instrumentation/restlet/v2_0/AbstractRestletServerTest.groovy +++ b/instrumentation/restlet/restlet-2.0/testing/src/main/groovy/io/opentelemetry/instrumentation/restlet/v2_0/AbstractRestletServerTest.groovy @@ -176,14 +176,14 @@ abstract class AbstractRestletServerTest extends HttpServerTest { } @Override - String expectedHttpRoute(ServerEndpoint endpoint) { + String expectedHttpRoute(ServerEndpoint endpoint, String method) { switch (endpoint) { case PATH_PARAM: return getContextPath() + "/path/{id}/param" case NOT_FOUND: return getContextPath() + "/*" default: - return super.expectedHttpRoute(endpoint) + return super.expectedHttpRoute(endpoint, method) } } diff --git a/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/Servlet2SpanNameExtractor.java b/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/Servlet2SpanNameExtractor.java index 751a4629ee58..ee6ab3a0f31a 100644 --- a/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/Servlet2SpanNameExtractor.java +++ b/instrumentation/servlet/servlet-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v2_2/Servlet2SpanNameExtractor.java @@ -6,12 +6,16 @@ package io.opentelemetry.javaagent.instrumentation.servlet.v2_2; import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; import io.opentelemetry.javaagent.instrumentation.servlet.ServletAccessor; import io.opentelemetry.javaagent.instrumentation.servlet.ServletRequestContext; +import java.util.Set; public class Servlet2SpanNameExtractor implements SpanNameExtractor> { + private final ServletAccessor accessor; + private final Set knownMethods = CommonConfig.get().getKnownHttpRequestMethods(); public Servlet2SpanNameExtractor(ServletAccessor accessor) { this.accessor = accessor; @@ -22,16 +26,19 @@ public String extract(ServletRequestContext requestContext) { REQUEST request = requestContext.request(); String method = accessor.getRequestMethod(request); String servletPath = accessor.getRequestServletPath(request); - if (method != null) { - if (servletPath.isEmpty()) { - return method; - } - String contextPath = accessor.getRequestContextPath(request); - if (contextPath == null || contextPath.isEmpty() || contextPath.equals("/")) { - return method + " " + servletPath; - } - return method + " " + contextPath + servletPath; + if (method == null) { + return "HTTP"; + } + if (!knownMethods.contains(method)) { + method = "HTTP"; + } + if (servletPath.isEmpty()) { + return method; + } + String contextPath = accessor.getRequestContextPath(request); + if (contextPath == null || contextPath.isEmpty() || contextPath.equals("/")) { + return method + " " + servletPath; } - return "HTTP request"; + return method + " " + contextPath + servletPath; } } diff --git a/instrumentation/servlet/servlet-2.2/javaagent/src/test/groovy/JettyServlet2Test.groovy b/instrumentation/servlet/servlet-2.2/javaagent/src/test/groovy/JettyServlet2Test.groovy index 1431a9814e6a..dfdb773473bb 100644 --- a/instrumentation/servlet/servlet-2.2/javaagent/src/test/groovy/JettyServlet2Test.groovy +++ b/instrumentation/servlet/servlet-2.2/javaagent/src/test/groovy/JettyServlet2Test.groovy @@ -4,6 +4,7 @@ */ import io.opentelemetry.api.common.AttributeKey +import io.opentelemetry.instrumentation.api.internal.HttpConstants import io.opentelemetry.instrumentation.test.AgentTestTrait import io.opentelemetry.instrumentation.test.asserts.TraceAssert import io.opentelemetry.instrumentation.test.base.HttpServerTest @@ -83,6 +84,9 @@ class JettyServlet2Test extends HttpServerTest implements AgentTestTrait @Override String expectedServerSpanName(ServerEndpoint endpoint, String method, @Nullable String route) { + if (method == HttpConstants._OTHER) { + return "HTTP " + endpoint.resolvePath(address).path + } switch (endpoint) { case NOT_FOUND: return method diff --git a/instrumentation/servlet/servlet-3.0/javaagent/src/test/groovy/AbstractServlet3Test.groovy b/instrumentation/servlet/servlet-3.0/javaagent/src/test/groovy/AbstractServlet3Test.groovy index 723b6430c4a0..40e780135f57 100644 --- a/instrumentation/servlet/servlet-3.0/javaagent/src/test/groovy/AbstractServlet3Test.groovy +++ b/instrumentation/servlet/servlet-3.0/javaagent/src/test/groovy/AbstractServlet3Test.groovy @@ -4,11 +4,13 @@ */ import io.opentelemetry.api.trace.SpanKind +import io.opentelemetry.instrumentation.api.internal.HttpConstants import io.opentelemetry.instrumentation.test.AgentTestTrait import io.opentelemetry.instrumentation.test.asserts.TraceAssert import io.opentelemetry.instrumentation.test.base.HttpServerTest import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint import io.opentelemetry.javaagent.bootstrap.servlet.ExperimentalSnippetHolder +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes import javax.servlet.Servlet @@ -82,12 +84,19 @@ abstract class AbstractServlet3Test extends HttpServerTest extends HttpServerTest extends HttpServerTest (0..count - 1).each { trace(it, 2) { - serverSpan(it, 0, null, null, "GET", ACCESS_LOG_SUCCESS.body.length(), ACCESS_LOG_SUCCESS) + serverSpan(it, 0, null, null, "GET", ACCESS_LOG_SUCCESS) controllerSpan(it, 1, span(0)) } @@ -182,7 +182,7 @@ abstract class TomcatServlet3Test extends AbstractServlet3Test } assertTraces(1) { trace(0, spanCount) { - serverSpan(it, 0, null, null, method, response.content().length(), ACCESS_LOG_ERROR) + serverSpan(it, 0, null, null, method, ACCESS_LOG_ERROR) def spanIndex = 1 controllerSpan(it, spanIndex, span(spanIndex - 1)) spanIndex++ diff --git a/instrumentation/servlet/servlet-5.0/javaagent/src/test/groovy/AbstractServlet5Test.groovy b/instrumentation/servlet/servlet-5.0/javaagent/src/test/groovy/AbstractServlet5Test.groovy index 77291b2e1bc8..ea8957d49628 100644 --- a/instrumentation/servlet/servlet-5.0/javaagent/src/test/groovy/AbstractServlet5Test.groovy +++ b/instrumentation/servlet/servlet-5.0/javaagent/src/test/groovy/AbstractServlet5Test.groovy @@ -4,11 +4,13 @@ */ import io.opentelemetry.api.trace.SpanKind +import io.opentelemetry.instrumentation.api.internal.HttpConstants import io.opentelemetry.instrumentation.test.AgentTestTrait import io.opentelemetry.instrumentation.test.asserts.TraceAssert import io.opentelemetry.instrumentation.test.base.HttpServerTest import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint import io.opentelemetry.javaagent.bootstrap.servlet.ExperimentalSnippetHolder +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes import jakarta.servlet.Servlet import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.AUTH_REQUIRED @@ -91,12 +93,19 @@ abstract class AbstractServlet5Test extends HttpServerTest extends HttpServerTest extends HttpServerTest (0..count - 1).each { trace(it, 2) { - serverSpan(it, 0, null, null, "GET", ACCESS_LOG_SUCCESS.body.length(), ACCESS_LOG_SUCCESS) + serverSpan(it, 0, null, null, "GET", ACCESS_LOG_SUCCESS) controllerSpan(it, 1, span(0)) } @@ -182,7 +182,7 @@ abstract class TomcatServlet5Test extends AbstractServlet5Test } assertTraces(1) { trace(0, spanCount) { - serverSpan(it, 0, null, null, method, response.content().length(), ACCESS_LOG_ERROR) + serverSpan(it, 0, null, null, method, ACCESS_LOG_ERROR) def spanIndex = 1 controllerSpan(it, spanIndex, span(spanIndex - 1)) spanIndex++ diff --git a/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletInstrumenterBuilder.java b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletInstrumenterBuilder.java index c18bdee30579..3bf2357bee2e 100644 --- a/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletInstrumenterBuilder.java +++ b/instrumentation/servlet/servlet-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/ServletInstrumenterBuilder.java @@ -66,7 +66,10 @@ public Instrumenter, ServletResponseContext, ServletResponseContext> requestParametersExtractor = new ServletRequestParametersExtractor<>(accessor); diff --git a/instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/java/server/base/ImmediateHandlerSpringWebFluxServerTest.java b/instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/java/server/base/ImmediateHandlerSpringWebFluxServerTest.java index 6a0e7483748a..94d515e62e18 100644 --- a/instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/java/server/base/ImmediateHandlerSpringWebFluxServerTest.java +++ b/instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/java/server/base/ImmediateHandlerSpringWebFluxServerTest.java @@ -72,6 +72,6 @@ void nestedPath() { assertThat(response.contentUtf8()).isEqualTo(NESTED_PATH.getBody()); assertResponseHasCustomizedHeaders(response, NESTED_PATH, null); - assertTheTraces(1, null, null, null, method, NESTED_PATH, response); + assertTheTraces(1, null, null, null, method, NESTED_PATH); } } diff --git a/instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/java/server/base/SpringWebFluxServerTest.java b/instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/java/server/base/SpringWebFluxServerTest.java index 343be82d9a18..9efe2589b978 100644 --- a/instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/java/server/base/SpringWebFluxServerTest.java +++ b/instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/java/server/base/SpringWebFluxServerTest.java @@ -49,7 +49,7 @@ public void stopServer(ConfigurableApplicationContext configurableApplicationCon } @Override - public String expectedHttpRoute(ServerEndpoint endpoint) { + public String expectedHttpRoute(ServerEndpoint endpoint, String method) { if (endpoint.equals(PATH_PARAM)) { return getContextPath() + "/path/{id}/param"; } else if (endpoint.equals(NOT_FOUND)) { @@ -57,7 +57,7 @@ public String expectedHttpRoute(ServerEndpoint endpoint) { } else if (endpoint.equals(NESTED_PATH)) { return "/nestedPath/hello/world"; } - return super.expectedHttpRoute(endpoint); + return super.expectedHttpRoute(endpoint, method); } @Override diff --git a/instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/SpringWebfluxTelemetryBuilder.java b/instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/SpringWebfluxTelemetryBuilder.java index ae2c4fdcff68..64abe6f6011f 100644 --- a/instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/SpringWebfluxTelemetryBuilder.java +++ b/instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/SpringWebfluxTelemetryBuilder.java @@ -16,6 +16,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerExperimentalMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerRoute; +import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerRouteBuilder; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractorBuilder; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; @@ -44,6 +45,8 @@ public final class SpringWebfluxTelemetryBuilder { HttpServerAttributesExtractor.builder(WebfluxServerHttpAttributesGetter.INSTANCE); private final HttpSpanNameExtractorBuilder httpServerSpanNameExtractorBuilder = HttpSpanNameExtractor.builder(WebfluxServerHttpAttributesGetter.INSTANCE); + private final HttpServerRouteBuilder httpServerRouteBuilder = + HttpServerRoute.builder(WebfluxServerHttpAttributesGetter.INSTANCE); private Consumer> clientExtractorConfigurer = builder -> {}; @@ -167,6 +170,7 @@ public SpringWebfluxTelemetryBuilder setKnownMethods(Set knownMethods) { clientSpanNameExtractorConfigurer.andThen(builder -> builder.setKnownMethods(knownMethods)); httpServerAttributesExtractorBuilder.setKnownMethods(knownMethods); httpServerSpanNameExtractorBuilder.setKnownMethods(knownMethods); + httpServerRouteBuilder.setKnownMethods(knownMethods); return this; } @@ -227,7 +231,7 @@ private Instrumenter buildServerInstrument .setSpanStatusExtractor(HttpSpanStatusExtractor.create(getter)) .addAttributesExtractor(httpServerAttributesExtractorBuilder.build()) .addAttributesExtractors(serverAdditionalExtractors) - .addContextCustomizer(HttpServerRoute.create(getter)) + .addContextCustomizer(httpServerRouteBuilder.build()) .addOperationMetrics(HttpServerMetrics.get()); if (emitExperimentalHttpServerMetrics) { builder.addOperationMetrics(HttpServerExperimentalMetrics.get()); diff --git a/instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/test/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/SpringWebfluxServerInstrumentationTest.java b/instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/test/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/SpringWebfluxServerInstrumentationTest.java index 0adf05481a5a..58c5d08062e5 100644 --- a/instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/test/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/SpringWebfluxServerInstrumentationTest.java +++ b/instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/test/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/SpringWebfluxServerInstrumentationTest.java @@ -38,11 +38,11 @@ protected void configure(HttpServerTestOptions options) { options.setExpectedException(new RuntimeException(ServerEndpoint.EXCEPTION.getBody())); options.setExpectedHttpRoute( - endpoint -> { + (endpoint, method) -> { if (endpoint == ServerEndpoint.PATH_PARAM) { return CONTEXT_PATH + "/path/{id}/param"; } - return expectedHttpRoute(endpoint); + return expectedHttpRoute(endpoint, method); }); } } diff --git a/instrumentation/spring/spring-webmvc/spring-webmvc-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/SpringWebMvcTelemetryBuilder.java b/instrumentation/spring/spring-webmvc/spring-webmvc-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/SpringWebMvcTelemetryBuilder.java index c00f9fd1730a..f34f4fd772bb 100644 --- a/instrumentation/spring/spring-webmvc/spring-webmvc-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/SpringWebMvcTelemetryBuilder.java +++ b/instrumentation/spring/spring-webmvc/spring-webmvc-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/SpringWebMvcTelemetryBuilder.java @@ -16,6 +16,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerExperimentalMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerRoute; +import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerRouteBuilder; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractorBuilder; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; @@ -40,6 +41,8 @@ public final class SpringWebMvcTelemetryBuilder { HttpServerAttributesExtractor.builder(SpringWebMvcHttpAttributesGetter.INSTANCE); private final HttpSpanNameExtractorBuilder httpSpanNameExtractorBuilder = HttpSpanNameExtractor.builder(SpringWebMvcHttpAttributesGetter.INSTANCE); + private final HttpServerRouteBuilder httpServerRouteBuilder = + HttpServerRoute.builder(SpringWebMvcHttpAttributesGetter.INSTANCE); @Nullable private Function< @@ -114,6 +117,7 @@ public SpringWebMvcTelemetryBuilder setSpanNameExtractor( public SpringWebMvcTelemetryBuilder setKnownMethods(Set knownMethods) { httpAttributesExtractorBuilder.setKnownMethods(knownMethods); httpSpanNameExtractorBuilder.setKnownMethods(knownMethods); + httpServerRouteBuilder.setKnownMethods(knownMethods); return this; } @@ -151,7 +155,7 @@ public SpringWebMvcTelemetry build() { .setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter)) .addAttributesExtractor(httpAttributesExtractorBuilder.build()) .addAttributesExtractors(additionalExtractors) - .addContextCustomizer(HttpServerRoute.create(httpAttributesGetter)) + .addContextCustomizer(httpServerRouteBuilder.build()) .addOperationMetrics(HttpServerMetrics.get()); if (emitExperimentalHttpServerMetrics) { builder.addOperationMetrics(HttpServerExperimentalMetrics.get()); diff --git a/instrumentation/spring/spring-webmvc/spring-webmvc-5.3/library/src/test/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/WebMvcHttpServerTest.java b/instrumentation/spring/spring-webmvc/spring-webmvc-5.3/library/src/test/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/WebMvcHttpServerTest.java index 9283a640d125..040d74e1457d 100644 --- a/instrumentation/spring/spring-webmvc/spring-webmvc-5.3/library/src/test/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/WebMvcHttpServerTest.java +++ b/instrumentation/spring/spring-webmvc/spring-webmvc-5.3/library/src/test/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/WebMvcHttpServerTest.java @@ -38,11 +38,11 @@ protected void configure(HttpServerTestOptions options) { options.setTestException(false); options.setExpectedHttpRoute( - endpoint -> { + (endpoint, method) -> { if (endpoint == ServerEndpoint.PATH_PARAM) { return CONTEXT_PATH + "/path/{id}/param"; } - return expectedHttpRoute(endpoint); + return expectedHttpRoute(endpoint, method); }); } } diff --git a/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v6_0/SpringWebMvcTelemetryBuilder.java b/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v6_0/SpringWebMvcTelemetryBuilder.java index 364429ccd7b5..136a89fed59b 100644 --- a/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v6_0/SpringWebMvcTelemetryBuilder.java +++ b/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v6_0/SpringWebMvcTelemetryBuilder.java @@ -16,6 +16,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerExperimentalMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerMetrics; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerRoute; +import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerRouteBuilder; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractorBuilder; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; @@ -40,6 +41,8 @@ public final class SpringWebMvcTelemetryBuilder { HttpServerAttributesExtractor.builder(SpringWebMvcHttpAttributesGetter.INSTANCE); private final HttpSpanNameExtractorBuilder httpSpanNameExtractorBuilder = HttpSpanNameExtractor.builder(SpringWebMvcHttpAttributesGetter.INSTANCE); + private final HttpServerRouteBuilder httpServerRouteBuilder = + HttpServerRoute.builder(SpringWebMvcHttpAttributesGetter.INSTANCE); @Nullable private Function< @@ -114,6 +117,7 @@ public SpringWebMvcTelemetryBuilder setSpanNameExtractor( public SpringWebMvcTelemetryBuilder setKnownMethods(Set knownMethods) { httpAttributesExtractorBuilder.setKnownMethods(knownMethods); httpSpanNameExtractorBuilder.setKnownMethods(knownMethods); + httpServerRouteBuilder.setKnownMethods(knownMethods); return this; } @@ -151,7 +155,7 @@ public SpringWebMvcTelemetry build() { .setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter)) .addAttributesExtractor(httpAttributesExtractorBuilder.build()) .addAttributesExtractors(additionalExtractors) - .addContextCustomizer(HttpServerRoute.create(httpAttributesGetter)) + .addContextCustomizer(httpServerRouteBuilder.build()) .addOperationMetrics(HttpServerMetrics.get()); if (emitExperimentalHttpServerMetrics) { builder.addOperationMetrics(HttpServerExperimentalMetrics.get()); diff --git a/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/library/src/test/java/io/opentelemetry/instrumentation/spring/webmvc/v6_0/WebMvcHttpServerTest.java b/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/library/src/test/java/io/opentelemetry/instrumentation/spring/webmvc/v6_0/WebMvcHttpServerTest.java index 3f76d4a99496..dec3c378d93b 100644 --- a/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/library/src/test/java/io/opentelemetry/instrumentation/spring/webmvc/v6_0/WebMvcHttpServerTest.java +++ b/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/library/src/test/java/io/opentelemetry/instrumentation/spring/webmvc/v6_0/WebMvcHttpServerTest.java @@ -38,11 +38,11 @@ protected void configure(HttpServerTestOptions options) { options.setTestException(false); options.setExpectedHttpRoute( - endpoint -> { + (endpoint, method) -> { if (endpoint == ServerEndpoint.PATH_PARAM) { return CONTEXT_PATH + "/path/{id}/param"; } - return expectedHttpRoute(endpoint); + return expectedHttpRoute(endpoint, method); }); } } diff --git a/instrumentation/spring/spring-webmvc/spring-webmvc-common/testing/src/main/groovy/boot/AbstractSpringBootBasedTest.groovy b/instrumentation/spring/spring-webmvc/spring-webmvc-common/testing/src/main/groovy/boot/AbstractSpringBootBasedTest.groovy index a69cc7694654..324e24605f3d 100644 --- a/instrumentation/spring/spring-webmvc/spring-webmvc-common/testing/src/main/groovy/boot/AbstractSpringBootBasedTest.groovy +++ b/instrumentation/spring/spring-webmvc/spring-webmvc-common/testing/src/main/groovy/boot/AbstractSpringBootBasedTest.groovy @@ -85,7 +85,7 @@ abstract class AbstractSpringBootBasedTest extends HttpServerTest implements AgentTestT } } - String expectedHttpRoute(ServerEndpoint endpoint) { + String expectedHttpRoute(ServerEndpoint endpoint, String method) { switch (endpoint) { case PATH_PARAM: return getContextPath() + "/path/{id}/param" case NOT_FOUND: return getContextPath() + "/*" default: - return super.expectedHttpRoute(endpoint) + return super.expectedHttpRoute(endpoint, method) } } diff --git a/instrumentation/tomcat/tomcat-10.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v10_0/TomcatAsyncTest.groovy b/instrumentation/tomcat/tomcat-10.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v10_0/TomcatAsyncTest.groovy index 01495f495de1..ff0e941dfe34 100644 --- a/instrumentation/tomcat/tomcat-10.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v10_0/TomcatAsyncTest.groovy +++ b/instrumentation/tomcat/tomcat-10.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v10_0/TomcatAsyncTest.groovy @@ -5,7 +5,7 @@ package io.opentelemetry.javaagent.instrumentation.tomcat.v10_0 - +import io.opentelemetry.instrumentation.api.internal.HttpConstants import io.opentelemetry.instrumentation.test.AgentTestTrait import io.opentelemetry.instrumentation.test.asserts.TraceAssert import io.opentelemetry.instrumentation.test.base.HttpServerTest @@ -107,12 +107,15 @@ class TomcatAsyncTest extends HttpServerTest implements AgentTestTrait { } @Override - String expectedHttpRoute(ServerEndpoint endpoint) { + String expectedHttpRoute(ServerEndpoint endpoint, String method) { + if (method == HttpConstants._OTHER) { + return getContextPath() + endpoint.path + } switch (endpoint) { case NOT_FOUND: return getContextPath() + "/*" default: - return super.expectedHttpRoute(endpoint) + return super.expectedHttpRoute(endpoint, method) } } diff --git a/instrumentation/tomcat/tomcat-10.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v10_0/TomcatHandlerTest.groovy b/instrumentation/tomcat/tomcat-10.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v10_0/TomcatHandlerTest.groovy index 8fca022e1768..2041d6d8eeb1 100644 --- a/instrumentation/tomcat/tomcat-10.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v10_0/TomcatHandlerTest.groovy +++ b/instrumentation/tomcat/tomcat-10.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v10_0/TomcatHandlerTest.groovy @@ -5,7 +5,7 @@ package io.opentelemetry.javaagent.instrumentation.tomcat.v10_0 - +import io.opentelemetry.instrumentation.api.internal.HttpConstants import io.opentelemetry.instrumentation.test.AgentTestTrait import io.opentelemetry.instrumentation.test.asserts.TraceAssert import io.opentelemetry.instrumentation.test.base.HttpServerTest @@ -55,6 +55,14 @@ class TomcatHandlerTest extends HttpServerTest implements AgentTestTrait true } + @Override + String expectedHttpRoute(ServerEndpoint endpoint, String method) { + if (method == HttpConstants._OTHER) { + return getContextPath() + endpoint.path + } + return super.expectedHttpRoute(endpoint, method) + } + @Override Tomcat startServer(int port) { Tomcat tomcat = new Tomcat() diff --git a/instrumentation/tomcat/tomcat-7.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/TomcatAsyncTest.groovy b/instrumentation/tomcat/tomcat-7.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/TomcatAsyncTest.groovy index b7519f9d43e3..1db9939430ef 100644 --- a/instrumentation/tomcat/tomcat-7.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/TomcatAsyncTest.groovy +++ b/instrumentation/tomcat/tomcat-7.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/TomcatAsyncTest.groovy @@ -5,7 +5,7 @@ package io.opentelemetry.javaagent.instrumentation.tomcat.v7_0 - +import io.opentelemetry.instrumentation.api.internal.HttpConstants import io.opentelemetry.instrumentation.test.AgentTestTrait import io.opentelemetry.instrumentation.test.asserts.TraceAssert import io.opentelemetry.instrumentation.test.base.HttpServerTest @@ -107,12 +107,15 @@ class TomcatAsyncTest extends HttpServerTest implements AgentTestTrait { } @Override - String expectedHttpRoute(ServerEndpoint endpoint) { + String expectedHttpRoute(ServerEndpoint endpoint, String method) { + if (method == HttpConstants._OTHER) { + return getContextPath() + endpoint.path + } switch (endpoint) { case NOT_FOUND: return getContextPath() + "/*" default: - return super.expectedHttpRoute(endpoint) + return super.expectedHttpRoute(endpoint, method) } } diff --git a/instrumentation/tomcat/tomcat-7.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/TomcatHandlerTest.groovy b/instrumentation/tomcat/tomcat-7.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/TomcatHandlerTest.groovy index fa84544f20e7..586ae411d771 100644 --- a/instrumentation/tomcat/tomcat-7.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/TomcatHandlerTest.groovy +++ b/instrumentation/tomcat/tomcat-7.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/TomcatHandlerTest.groovy @@ -5,6 +5,7 @@ package io.opentelemetry.javaagent.instrumentation.tomcat.v7_0 +import io.opentelemetry.instrumentation.api.internal.HttpConstants import io.opentelemetry.instrumentation.test.AgentTestTrait import io.opentelemetry.instrumentation.test.asserts.TraceAssert import io.opentelemetry.instrumentation.test.base.HttpServerTest @@ -54,6 +55,14 @@ class TomcatHandlerTest extends HttpServerTest implements AgentTestTrait true } + @Override + String expectedHttpRoute(ServerEndpoint endpoint, String method) { + if (method == HttpConstants._OTHER) { + return getContextPath() + endpoint.path + } + return super.expectedHttpRoute(endpoint, method) + } + @Override Tomcat startServer(int port) { Tomcat tomcat = new Tomcat() diff --git a/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatInstrumenterFactory.java b/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatInstrumenterFactory.java index bd2ee855f781..1c5febafdddd 100644 --- a/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatInstrumenterFactory.java +++ b/instrumentation/tomcat/tomcat-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/common/TomcatInstrumenterFactory.java @@ -44,7 +44,10 @@ public static Instrumenter create( .setCapturedResponseHeaders(CommonConfig.get().getServerResponseHeaders()) .setKnownMethods(CommonConfig.get().getKnownHttpRequestMethods()) .build()) - .addContextCustomizer(HttpServerRoute.create(httpAttributesGetter)) + .addContextCustomizer( + HttpServerRoute.builder(httpAttributesGetter) + .setKnownMethods(CommonConfig.get().getKnownHttpRequestMethods()) + .build()) .addContextCustomizer( (context, request, attributes) -> new AppServerBridge.Builder() diff --git a/instrumentation/undertow-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/undertow/UndertowSingletons.java b/instrumentation/undertow-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/undertow/UndertowSingletons.java index 3811f413197a..85a5bd20e10c 100644 --- a/instrumentation/undertow-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/undertow/UndertowSingletons.java +++ b/instrumentation/undertow-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/undertow/UndertowSingletons.java @@ -41,7 +41,10 @@ public final class UndertowSingletons { .setCapturedResponseHeaders(CommonConfig.get().getServerResponseHeaders()) .setKnownMethods(CommonConfig.get().getKnownHttpRequestMethods()) .build()) - .addContextCustomizer(HttpServerRoute.create(httpAttributesGetter)) + .addContextCustomizer( + HttpServerRoute.builder(httpAttributesGetter) + .setKnownMethods(CommonConfig.get().getKnownHttpRequestMethods()) + .build()) .addContextCustomizer( (context, request, attributes) -> { // span is ended when counter reaches 0, we start from 2 which accounts for the diff --git a/testing-common/src/main/groovy/io/opentelemetry/instrumentation/test/base/HttpServerTest.groovy b/testing-common/src/main/groovy/io/opentelemetry/instrumentation/test/base/HttpServerTest.groovy index 0a5a07c93154..73b54a5e440a 100644 --- a/testing-common/src/main/groovy/io/opentelemetry/instrumentation/test/base/HttpServerTest.groovy +++ b/testing-common/src/main/groovy/io/opentelemetry/instrumentation/test/base/HttpServerTest.groovy @@ -9,6 +9,8 @@ import io.opentelemetry.api.common.AttributeKey import io.opentelemetry.api.trace.Span import io.opentelemetry.api.trace.SpanId import io.opentelemetry.api.trace.SpanKind +import io.opentelemetry.instrumentation.api.internal.HttpConstants +import io.opentelemetry.instrumentation.api.internal.SemconvStability import io.opentelemetry.instrumentation.test.InstrumentationSpecification import io.opentelemetry.instrumentation.test.asserts.TraceAssert import io.opentelemetry.instrumentation.testing.GlobalTraceUtil @@ -19,7 +21,6 @@ import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions import io.opentelemetry.sdk.trace.data.SpanData import io.opentelemetry.semconv.SemanticAttributes import io.opentelemetry.testing.internal.armeria.common.AggregatedHttpRequest -import io.opentelemetry.testing.internal.armeria.common.AggregatedHttpResponse import io.opentelemetry.testing.internal.armeria.common.HttpMethod import spock.lang.Shared import spock.lang.Unroll @@ -49,15 +50,22 @@ abstract class HttpServerTest extends InstrumentationSpecification imple } String expectedServerSpanName(ServerEndpoint endpoint, String method, @Nullable String route) { + if (method == HttpConstants._OTHER) { + method = "HTTP" + } return route == null ? method : method + " " + route } - String expectedHttpRoute(ServerEndpoint endpoint) { + String expectedHttpRoute(ServerEndpoint endpoint, String method) { // no need to compute route if we're not expecting it if (!httpAttributes(endpoint).contains(SemanticAttributes.HTTP_ROUTE)) { return null } + if (method == HttpConstants._OTHER) { + return null + } + switch (endpoint) { case NOT_FOUND: return null @@ -96,6 +104,10 @@ abstract class HttpServerTest extends InstrumentationSpecification imple 1 } + int getResponseCodeOnNonStandardHttpMethod() { + SUCCESS.status + } + boolean hasErrorPageSpans(ServerEndpoint endpoint) { false } @@ -148,6 +160,10 @@ abstract class HttpServerTest extends InstrumentationSpecification imple true } + boolean testNonStandardHttpMethod() { + true + } + boolean verifyServerSpanEndTime() { return true } @@ -199,8 +215,8 @@ abstract class HttpServerTest extends InstrumentationSpecification imple options.expectedServerSpanNameMapper = { endpoint, method, route -> HttpServerTest.this.expectedServerSpanName(endpoint, method, route) } - options.expectedHttpRoute = { endpoint -> - HttpServerTest.this.expectedHttpRoute(endpoint) + options.expectedHttpRoute = { endpoint, method -> + HttpServerTest.this.expectedHttpRoute(endpoint, method) } options.contextPath = getContextPath() options.httpAttributes = { endpoint -> @@ -219,6 +235,7 @@ abstract class HttpServerTest extends InstrumentationSpecification imple options.metricsInstrumentationName = { HttpServerTest.this.getMetricsInstrumentationName() } + options.responseCodeOnNonStandardHttpMethod = getResponseCodeOnNonStandardHttpMethod() options.testRedirect = testRedirect() options.testError = testError() @@ -229,6 +246,9 @@ abstract class HttpServerTest extends InstrumentationSpecification imple options.testCaptureHttpHeaders = testCapturedHttpHeaders() options.testCaptureRequestParameters = testCapturedRequestParameters() options.testHttpPipelining = testHttpPipelining() + if (!testNonStandardHttpMethod()) { + options.disableTestNonStandardHttpMethod() + } } // Override trace assertion method. We can call java assertions from groovy but not the other @@ -241,9 +261,8 @@ abstract class HttpServerTest extends InstrumentationSpecification imple String parentId, String spanId, String method, - ServerEndpoint endpoint, - AggregatedHttpResponse response) { - HttpServerTest.this.assertTheTraces(size, traceId, parentId, spanId, method, endpoint, response) + ServerEndpoint endpoint) { + HttpServerTest.this.assertTheTraces(size, traceId, parentId, spanId, method, endpoint) } @Override @@ -337,6 +356,13 @@ abstract class HttpServerTest extends InstrumentationSpecification imple junitTest.httpPipelining() } + def "non standard http method"() { + assumeTrue(SemconvStability.emitStableHttpSemconv()) + assumeTrue(testNonStandardHttpMethod()) + expect: + junitTest.requestWithNonStandardHttpMethod() + } + void assertHighConcurrency(int count) { def endpoint = INDEXED_CHILD assertTraces(count) { @@ -372,7 +398,7 @@ abstract class HttpServerTest extends InstrumentationSpecification imple //FIXME: add tests for POST with large/chunked data - void assertTheTraces(int size, String traceID = null, String parentID = null, String spanID = null, String method = "GET", ServerEndpoint endpoint = SUCCESS, AggregatedHttpResponse response = null) { + void assertTheTraces(int size, String traceID = null, String parentID = null, String spanID = null, String method = "GET", ServerEndpoint endpoint = SUCCESS) { def spanCount = 1 // server span if (hasResponseSpan(endpoint)) { spanCount++ @@ -398,7 +424,7 @@ abstract class HttpServerTest extends InstrumentationSpecification imple assert it.span(0).endEpochNanos - it.span(index).endEpochNanos >= 0 } } - serverSpan(it, spanIndex++, traceID, parentID, method, response?.content()?.length(), endpoint, spanID) + this.serverSpan(it, spanIndex++, traceID, parentID, method, endpoint, spanID) if (hasHandlerSpan(endpoint)) { handlerSpan(it, spanIndex++, span(0), method, endpoint) } @@ -471,10 +497,10 @@ abstract class HttpServerTest extends InstrumentationSpecification imple } } - void serverSpan(TraceAssert trace, int index, String traceID = null, String parentID = null, String method = "GET", Long responseContentLength = null, ServerEndpoint endpoint = SUCCESS, String spanID = null) { + void serverSpan(TraceAssert trace, int index, String traceID = null, String parentID = null, String method = "GET", ServerEndpoint endpoint = SUCCESS, String spanID = null) { trace.assertedIndexes.add(index) def spanData = trace.span(index) - def assertion = junitTest.assertServerSpan(OpenTelemetryAssertions.assertThat(spanData), method, endpoint) + def assertion = junitTest.assertServerSpan(OpenTelemetryAssertions.assertThat(spanData), method, endpoint, endpoint.status) if (parentID == null) { assertion.hasParentSpanId(SpanId.invalid) } else { diff --git a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/AbstractHttpServerTest.java b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/AbstractHttpServerTest.java index 87d9568f003a..c3dba18bff42 100644 --- a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/AbstractHttpServerTest.java +++ b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/AbstractHttpServerTest.java @@ -27,8 +27,10 @@ import io.opentelemetry.context.Context; import io.opentelemetry.context.propagation.TextMapPropagator; import io.opentelemetry.context.propagation.TextMapSetter; +import io.opentelemetry.instrumentation.api.instrumenter.http.internal.HttpAttributes; import io.opentelemetry.instrumentation.api.instrumenter.network.internal.NetworkAttributes; import io.opentelemetry.instrumentation.api.instrumenter.url.internal.UrlAttributes; +import io.opentelemetry.instrumentation.api.internal.HttpConstants; import io.opentelemetry.instrumentation.api.internal.SemconvStability; import io.opentelemetry.instrumentation.testing.GlobalTraceUtil; import io.opentelemetry.sdk.testing.assertj.SpanDataAssert; @@ -139,7 +141,7 @@ void successfulGetRequest(int count) { assertResponseHasCustomizedHeaders(response, SUCCESS, null); } - assertTheTraces(count, null, null, null, method, SUCCESS, responses.get(0)); + assertTheTraces(count, null, null, null, method, SUCCESS); } @Test @@ -160,7 +162,7 @@ void successfulGetRequestWithParent() { assertThat(response.contentUtf8()).isEqualTo(SUCCESS.getBody()); String spanId = assertResponseHasCustomizedHeaders(response, SUCCESS, traceId); - assertTheTraces(1, traceId, parentId, spanId, "GET", SUCCESS, response); + assertTheTraces(1, traceId, parentId, spanId, "GET", SUCCESS); } @Test @@ -179,7 +181,7 @@ void tracingHeaderIsCaseInsensitive() { assertThat(response.contentUtf8()).isEqualTo(SUCCESS.getBody()); String spanId = assertResponseHasCustomizedHeaders(response, SUCCESS, traceId); - assertTheTraces(1, traceId, parentId, spanId, "GET", SUCCESS, response); + assertTheTraces(1, traceId, parentId, spanId, "GET", SUCCESS); } @ParameterizedTest @@ -193,7 +195,7 @@ void requestWithQueryString(ServerEndpoint endpoint) { assertThat(response.contentUtf8()).isEqualTo(endpoint.getBody()); String spanId = assertResponseHasCustomizedHeaders(response, endpoint, null); - assertTheTraces(1, null, null, spanId, method, endpoint, response); + assertTheTraces(1, null, null, spanId, method, endpoint); } private static Stream provideServerEndpoints() { @@ -217,7 +219,7 @@ void requestWithRedirect() { .isEqualTo(address.resolve(REDIRECT.getBody()).toString())); String spanId = assertResponseHasCustomizedHeaders(response, REDIRECT, null); - assertTheTraces(1, null, null, spanId, method, REDIRECT, response); + assertTheTraces(1, null, null, spanId, method, REDIRECT); } @Test @@ -234,7 +236,7 @@ void requestWithError() { } String spanId = assertResponseHasCustomizedHeaders(response, ERROR, null); - assertTheTraces(1, null, null, spanId, method, ERROR, response); + assertTheTraces(1, null, null, spanId, method, ERROR); } @Test @@ -252,7 +254,7 @@ void requestWithException() { assertThat(response.status().code()).isEqualTo(EXCEPTION.getStatus()); String spanId = assertResponseHasCustomizedHeaders(response, EXCEPTION, null); - assertTheTraces(1, null, null, spanId, method, EXCEPTION, response); + assertTheTraces(1, null, null, spanId, method, EXCEPTION); } finally { Awaitility.reset(); } @@ -269,7 +271,7 @@ void requestForNotFound() { assertThat(response.status().code()).isEqualTo(NOT_FOUND.getStatus()); String spanId = assertResponseHasCustomizedHeaders(response, NOT_FOUND, null); - assertTheTraces(1, null, null, spanId, method, NOT_FOUND, response); + assertTheTraces(1, null, null, spanId, method, NOT_FOUND); } @Test @@ -284,7 +286,7 @@ void requestWithPathParameter() { assertThat(response.contentUtf8()).isEqualTo(PATH_PARAM.getBody()); String spanId = assertResponseHasCustomizedHeaders(response, PATH_PARAM, null); - assertTheTraces(1, null, null, spanId, method, PATH_PARAM, response); + assertTheTraces(1, null, null, spanId, method, PATH_PARAM); } @Test @@ -303,7 +305,7 @@ void captureHttpHeaders() { assertThat(response.headers().get("X-Test-Response")).isEqualTo("test"); String spanId = assertResponseHasCustomizedHeaders(response, CAPTURE_HEADERS, null); - assertTheTraces(1, null, null, spanId, "GET", CAPTURE_HEADERS, response); + assertTheTraces(1, null, null, spanId, "GET", CAPTURE_HEADERS); } @Test @@ -323,7 +325,7 @@ void captureRequestParameters() { assertThat(response.contentUtf8()).isEqualTo(CAPTURE_PARAMETERS.getBody()); String spanId = assertResponseHasCustomizedHeaders(response, CAPTURE_PARAMETERS, null); - assertTheTraces(1, null, null, spanId, "POST", CAPTURE_PARAMETERS, response); + assertTheTraces(1, null, null, spanId, "POST", CAPTURE_PARAMETERS); } @Test @@ -339,7 +341,8 @@ void httpServerMetrics() { testing.waitAndAssertTraces( trace -> { instrumentationName.set(trace.getSpan(0).getInstrumentationScopeInfo().getName()); - trace.anySatisfy(spanData -> assertServerSpan(assertThat(spanData), method, SUCCESS)); + trace.anySatisfy( + spanData -> assertServerSpan(assertThat(spanData), method, SUCCESS, SUCCESS.status)); }); String durationInstrumentName = @@ -478,6 +481,54 @@ protected void channelRead0( } } + @Test + void requestWithNonStandardHttpMethod() throws InterruptedException { + assumeTrue(SemconvStability.emitStableHttpSemconv()); + assumeTrue(options.testNonStandardHttpMethod); + + EventLoopGroup eventLoopGroup = new NioEventLoopGroup(); + try { + Bootstrap bootstrap = buildBootstrap(eventLoopGroup); + Channel channel = bootstrap.connect(address.getHost(), port).sync().channel(); + + String method = "TEST"; + DefaultFullHttpRequest request = + new DefaultFullHttpRequest( + HttpVersion.HTTP_1_1, + new io.opentelemetry.testing.internal.io.netty.handler.codec.http.HttpMethod(method), + SUCCESS.resolvePath(address).getPath(), + Unpooled.EMPTY_BUFFER); + request.headers().set(HttpHeaderNames.HOST, address.getHost() + ":" + port); + request.headers().set(HttpHeaderNames.USER_AGENT, TEST_USER_AGENT); + request.headers().set(HttpHeaderNames.X_FORWARDED_FOR, TEST_CLIENT_IP); + + testing + .getOpenTelemetry() + .getPropagators() + .getTextMapPropagator() + .inject( + Context.current(), + request, + (carrier, key, value) -> carrier.headers().set(key, value)); + channel.writeAndFlush(request); + + // TODO: add stricter assertions; could be difficult with the groovy code still in place + // though + testing.waitAndAssertTraces( + trace -> + trace.anySatisfy( + span -> + assertServerSpan( + assertThat(span), + HttpConstants._OTHER, + SUCCESS, + options.responseCodeOnNonStandardHttpMethod) + .hasAttribute(HttpAttributes.HTTP_REQUEST_METHOD_ORIGINAL, method))); + } finally { + eventLoopGroup.shutdownGracefully().await(10, TimeUnit.SECONDS); + } + } + private static Bootstrap buildBootstrap(EventLoopGroup eventLoopGroup) { Bootstrap bootstrap = new Bootstrap(); bootstrap @@ -566,8 +617,7 @@ protected void assertTheTraces( String parentId, String spanId, String method, - ServerEndpoint endpoint, - AggregatedHttpResponse response) { + ServerEndpoint endpoint) { List> assertions = new ArrayList<>(); for (int i = 0; i < size; i++) { assertions.add( @@ -575,7 +625,7 @@ protected void assertTheTraces( List> spanAssertions = new ArrayList<>(); spanAssertions.add( span -> { - assertServerSpan(span, method, endpoint); + assertServerSpan(span, method, endpoint, endpoint.status); if (traceId != null) { span.hasTraceId(traceId); } @@ -674,14 +724,14 @@ protected List> errorPageSpanAssertions( @CanIgnoreReturnValue @SuppressWarnings("deprecation") // until old http semconv are dropped in 2.0 protected SpanDataAssert assertServerSpan( - SpanDataAssert span, String method, ServerEndpoint endpoint) { + SpanDataAssert span, String method, ServerEndpoint endpoint, int statusCode) { Set> httpAttributes = options.httpAttributes.apply(endpoint); - String expectedRoute = options.expectedHttpRoute.apply(endpoint); - String name = getString(method, endpoint, expectedRoute); + String expectedRoute = options.expectedHttpRoute.apply(endpoint, method); + String name = options.expectedServerSpanNameMapper.apply(endpoint, method, expectedRoute); span.hasName(name).hasKind(SpanKind.SERVER); - if (endpoint.status >= 500) { + if (statusCode >= 500) { span.hasStatus(StatusData.error()); } @@ -751,7 +801,7 @@ protected SpanDataAssert assertServerSpan( } assertThat(attrs).containsEntry(getAttributeKey(SemanticAttributes.HTTP_METHOD), method); assertThat(attrs) - .containsEntry(getAttributeKey(SemanticAttributes.HTTP_STATUS_CODE), endpoint.status); + .containsEntry(getAttributeKey(SemanticAttributes.HTTP_STATUS_CODE), statusCode); AttributeKey netProtocolKey = getAttributeKey(SemanticAttributes.NET_PROTOCOL_NAME); @@ -823,17 +873,12 @@ protected static AttributeKey getAttributeKey(AttributeKey oldKey) { return SemconvStabilityUtil.getAttributeKey(oldKey); } - private String getString(String method, ServerEndpoint endpoint, String expectedRoute) { - String name = options.expectedServerSpanNameMapper.apply(endpoint, method, expectedRoute); - return name; - } - @CanIgnoreReturnValue @SuppressWarnings("deprecation") // until old http semconv are dropped in 2.0 protected SpanDataAssert assertIndexedServerSpan(SpanDataAssert span, int requestId) { ServerEndpoint endpoint = INDEXED_CHILD; String method = "GET"; - assertServerSpan(span, method, endpoint); + assertServerSpan(span, method, endpoint, endpoint.status); if (SemconvStability.emitOldHttpSemconv()) { span.hasAttributesSatisfying( @@ -865,12 +910,16 @@ public String expectedServerSpanName( endpoint, method, route); } - public String expectedHttpRoute(ServerEndpoint endpoint) { + public String expectedHttpRoute(ServerEndpoint endpoint, String method) { // no need to compute route if we're not expecting it if (!options.httpAttributes.apply(endpoint).contains(SemanticAttributes.HTTP_ROUTE)) { return null; } + if (HttpConstants._OTHER.equals(method)) { + return null; + } + if (NOT_FOUND.equals(endpoint)) { return null; } else if (PATH_PARAM.equals(endpoint)) { diff --git a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/HttpServerTestOptions.java b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/HttpServerTestOptions.java index 4b7e9b48118b..2eca91162865 100644 --- a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/HttpServerTestOptions.java +++ b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/HttpServerTestOptions.java @@ -9,11 +9,13 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.instrumentation.api.internal.HttpConstants; import io.opentelemetry.semconv.SemanticAttributes; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Set; +import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; @@ -30,15 +32,22 @@ public final class HttpServerTestOptions { SemconvStabilityUtil.getAttributeKey(SemanticAttributes.NET_PEER_PORT)))); public static final SpanNameMapper DEFAULT_EXPECTED_SERVER_SPAN_NAME_MAPPER = - (uri, method, route) -> route == null ? method : method + " " + route; + (uri, method, route) -> { + if (HttpConstants._OTHER.equals(method)) { + method = "HTTP"; + } + return route == null ? method : method + " " + route; + }; Function>> httpAttributes = unused -> DEFAULT_HTTP_ATTRIBUTES; SpanNameMapper expectedServerSpanNameMapper = DEFAULT_EXPECTED_SERVER_SPAN_NAME_MAPPER; - Function expectedHttpRoute = unused -> null; + BiFunction expectedHttpRoute = (endpoint, method) -> null; Function sockPeerAddr = unused -> "127.0.0.1"; String contextPath = ""; Throwable expectedException = new Exception(EXCEPTION.body); Supplier metricsInstrumentationName = () -> null; + // we're calling /success in the test, and most servers respond with 200 anyway + int responseCodeOnNonStandardHttpMethod = ServerEndpoint.SUCCESS.status; Predicate hasHandlerSpan = unused -> false; Predicate hasResponseSpan = unused -> false; @@ -57,6 +66,7 @@ public final class HttpServerTestOptions { boolean testCaptureHttpHeaders = true; boolean testCaptureRequestParameters = false; boolean testHttpPipelining = true; + boolean testNonStandardHttpMethod = true; boolean verifyServerSpanEndTime = true; HttpServerTestOptions() {} @@ -77,7 +87,7 @@ public HttpServerTestOptions setExpectedServerSpanNameMapper( @CanIgnoreReturnValue public HttpServerTestOptions setExpectedHttpRoute( - Function expectedHttpRoute) { + BiFunction expectedHttpRoute) { this.expectedHttpRoute = expectedHttpRoute; return this; } @@ -107,6 +117,13 @@ public HttpServerTestOptions setMetricsInstrumentationName( return this; } + @CanIgnoreReturnValue + public HttpServerTestOptions setResponseCodeOnNonStandardHttpMethod( + int responseCodeOnNonStandardHttpMethod) { + this.responseCodeOnNonStandardHttpMethod = responseCodeOnNonStandardHttpMethod; + return this; + } + @CanIgnoreReturnValue public HttpServerTestOptions setHasHandlerSpan(Predicate hasHandlerSpan) { this.hasHandlerSpan = hasHandlerSpan; @@ -201,6 +218,13 @@ public HttpServerTestOptions setTestHttpPipelining(boolean testHttpPipelining) { return this; } + // TODO: convert make this class follow the same pattern as HttpClientTestOptions + @CanIgnoreReturnValue + public HttpServerTestOptions disableTestNonStandardHttpMethod() { + this.testNonStandardHttpMethod = false; + return this; + } + @CanIgnoreReturnValue public HttpServerTestOptions setVerifyServerSpanEndTime(boolean verifyServerSpanEndTime) { this.verifyServerSpanEndTime = verifyServerSpanEndTime;