Add HTTP server tests for non standard methods (#9446)
This commit is contained in:
parent
2b2c4ca2d2
commit
3136916749
|
|
@ -31,13 +31,16 @@ public final class HttpServerRoute {
|
|||
*/
|
||||
public static <REQUEST> ContextCustomizer<REQUEST> create(
|
||||
HttpServerAttributesGetter<REQUEST, ?> 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 <REQUEST> HttpServerRouteBuilder<REQUEST> builder(
|
||||
HttpServerAttributesGetter<REQUEST, ?> getter) {
|
||||
return new HttpServerRouteBuilder<>(getter);
|
||||
}
|
||||
|
||||
private HttpServerRoute() {}
|
||||
|
|
@ -147,11 +150,8 @@ public final class HttpServerRoute {
|
|||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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<REQUEST> {
|
||||
|
||||
final HttpServerAttributesGetter<REQUEST, ?> getter;
|
||||
Set<String> knownMethods = HttpConstants.KNOWN_METHODS;
|
||||
|
||||
HttpServerRouteBuilder(HttpServerAttributesGetter<REQUEST, ?> getter) {
|
||||
this.getter = getter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the customizer to recognize an alternative set of HTTP request methods.
|
||||
*
|
||||
* <p>By default, this customizer defines "known" methods as the ones listed in <a
|
||||
* href="https://www.rfc-editor.org/rfc/rfc9110.html#name-methods">RFC9110</a> and the PATCH
|
||||
* method defined in <a href="https://www.rfc-editor.org/rfc/rfc5789.html">RFC5789</a>. If an
|
||||
* unknown method is encountered, the customizer will use the value {@value HttpConstants#_OTHER}
|
||||
* instead.
|
||||
*
|
||||
* <p>Note: calling this method <b>overrides</b> the default known method sets completely; it does
|
||||
* not supplement it.
|
||||
*
|
||||
* @param knownMethods A set of recognized HTTP request methods.
|
||||
*/
|
||||
@CanIgnoreReturnValue
|
||||
public HttpServerRouteBuilder<REQUEST> setKnownMethods(Set<String> 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<REQUEST> build() {
|
||||
Set<String> 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));
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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.api.trace.Span;
|
|||
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.<String, Void>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 @@ class HttpServerRouteTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
void shouldNotUpdateSpanName_noMethod() {
|
||||
void shouldUseHttp_noMethod() {
|
||||
when(getter.getHttpRequestMethod("test")).thenReturn(null);
|
||||
|
||||
Context context = instrumenter.start(Context.root(), "test");
|
||||
|
|
@ -169,6 +174,23 @@ class HttpServerRouteTest {
|
|||
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"));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,5 +35,7 @@ abstract class AbstractHttpServerInstrumentationTest
|
|||
t != ServerEndpoint.EXCEPTION
|
||||
}
|
||||
)
|
||||
// instrumentation does not create a span at all
|
||||
options.disableTestNonStandardHttpMethod
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,5 +29,6 @@ class ArmeriaHttpServerTest extends AbstractArmeriaHttpServerTest {
|
|||
options.setHasResponseCustomizer(
|
||||
endpoint -> ServerEndpoint.NOT_FOUND != endpoint && ServerEndpoint.EXCEPTION != endpoint);
|
||||
options.setTestHttpPipelining(false);
|
||||
options.setResponseCodeOnNonStandardHttpMethod(405);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerAttribut
|
|||
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<RequestContext> httpServerSpanNameExtractorBuilder =
|
||||
HttpSpanNameExtractor.builder(ArmeriaHttpServerAttributesGetter.INSTANCE);
|
||||
|
||||
private final HttpServerRouteBuilder<RequestContext> httpServerRouteBuilder =
|
||||
HttpServerRoute.builder(ArmeriaHttpServerAttributesGetter.INSTANCE);
|
||||
|
||||
private Function<
|
||||
SpanStatusExtractor<RequestContext, RequestLog>,
|
||||
? extends SpanStatusExtractor<? super RequestContext, ? super RequestLog>>
|
||||
|
|
@ -175,6 +179,7 @@ public final class ArmeriaTelemetryBuilder {
|
|||
httpServerAttributesExtractorBuilder.setKnownMethods(knownMethods);
|
||||
httpClientSpanNameExtractorBuilder.setKnownMethods(knownMethods);
|
||||
httpServerSpanNameExtractorBuilder.setKnownMethods(knownMethods);
|
||||
httpServerRouteBuilder.setKnownMethods(knownMethods);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
@ -233,7 +238,7 @@ public final class ArmeriaTelemetryBuilder {
|
|||
HttpSpanStatusExtractor.create(serverAttributesGetter)))
|
||||
.addAttributesExtractor(httpServerAttributesExtractorBuilder.build())
|
||||
.addOperationMetrics(HttpServerMetrics.get())
|
||||
.addContextCustomizer(HttpServerRoute.create(serverAttributesGetter));
|
||||
.addContextCustomizer(httpServerRouteBuilder.build());
|
||||
|
||||
if (peerService != null) {
|
||||
clientInstrumenterBuilder.addAttributesExtractor(
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import com.linecorp.armeria.server.ServerBuilder;
|
|||
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 @@ class ArmeriaHttpServerTest extends AbstractArmeriaHttpServerTest {
|
|||
.build()
|
||||
.newServiceDecorator());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpServerTestOptions options) {
|
||||
super.configure(options);
|
||||
// library instrumentation does not create a span at all
|
||||
options.disableTestNonStandardHttpMethod();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -178,13 +178,13 @@ public abstract class AbstractArmeriaHttpServerTest extends AbstractHttpServerTe
|
|||
@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);
|
||||
|
|
|
|||
|
|
@ -83,14 +83,14 @@ class DropwizardTest extends HttpServerTest<DropwizardTestSupport> 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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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<Test>().configureEach {
|
||||
systemProperty("testLatestDeps", latestDepTest)
|
||||
|
||||
// required on jdk17
|
||||
jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED")
|
||||
jvmArgs("-XX:+IgnoreUnrecognizedVMOptions")
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import grails.boot.GrailsApp;
|
|||
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 @@ import org.springframework.context.ConfigurableApplicationContext;
|
|||
|
||||
public class GrailsTest extends AbstractHttpServerTest<ConfigurableApplicationContext> {
|
||||
|
||||
static final boolean testLatestDeps = Boolean.getBoolean("testLatestDeps");
|
||||
|
||||
@RegisterExtension
|
||||
static final InstrumentationExtension testing = HttpServerInstrumentationExtension.forAgent();
|
||||
|
||||
|
|
@ -64,6 +67,7 @@ public class GrailsTest extends AbstractHttpServerTest<ConfigurableApplicationCo
|
|||
options.setHasErrorPageSpans(
|
||||
endpoint -> endpoint == ERROR || endpoint == EXCEPTION || endpoint == NOT_FOUND);
|
||||
options.setTestPathParam(true);
|
||||
options.setResponseCodeOnNonStandardHttpMethod(testLatestDeps ? 200 : 501);
|
||||
}
|
||||
|
||||
@SpringBootApplication
|
||||
|
|
@ -106,7 +110,12 @@ public class GrailsTest extends AbstractHttpServerTest<ConfigurableApplicationCo
|
|||
}
|
||||
|
||||
@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)) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -229,7 +229,6 @@ abstract class AbstractJaxRsHttpServerTest<S> extends HttpServerTest<S> 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,
|
||||
|
|
|
|||
|
|
@ -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<ApplicationRequest, ApplicationResponse>) -> SpanStatusExtractor<in ApplicationRequest, in ApplicationResponse> = { a -> a }
|
||||
|
||||
|
|
@ -77,6 +79,7 @@ class KtorServerTracing private constructor(
|
|||
fun setKnownMethods(knownMethods: Set<String>) {
|
||||
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(
|
||||
|
|
|
|||
|
|
@ -129,10 +129,10 @@ class KtorHttpServerTest : AbstractHttpServerTest<ApplicationEngine>() {
|
|||
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
|
||||
|
|
|
|||
|
|
@ -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<ApplicationRequest, ApplicationResponse>) -> SpanStatusExtractor<in ApplicationRequest, in ApplicationResponse> = { a -> a }
|
||||
|
||||
|
|
@ -78,6 +80,7 @@ class KtorServerTracing private constructor(
|
|||
fun setKnownMethods(knownMethods: Set<String>) {
|
||||
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(
|
||||
|
|
|
|||
|
|
@ -126,10 +126,10 @@ abstract class AbstractKtorHttpServerTest : AbstractHttpServerTest<ApplicationEn
|
|||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerAttribut
|
|||
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 final class NettyServerInstrumenterFactory {
|
|||
Consumer<HttpServerAttributesExtractorBuilder<HttpRequestAndChannel, HttpResponse>>
|
||||
extractorConfigurer,
|
||||
Consumer<HttpSpanNameExtractorBuilder<HttpRequestAndChannel>> spanNameExtractorConfigurer,
|
||||
Consumer<HttpServerRouteBuilder<HttpRequestAndChannel>> httpServerRouteConfigurer,
|
||||
boolean emitExperimentalHttpServerMetrics) {
|
||||
|
||||
NettyHttpServerAttributesGetter httpAttributesGetter = new NettyHttpServerAttributesGetter();
|
||||
|
|
@ -55,9 +57,13 @@ public final class NettyServerInstrumenterFactory {
|
|||
builder.addOperationMetrics(HttpServerExperimentalMetrics.get());
|
||||
}
|
||||
|
||||
HttpServerRouteBuilder<HttpRequestAndChannel> 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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<HttpRequestAndChannel, HttpResponse> instrumenter() {
|
||||
|
|
|
|||
|
|
@ -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<HttpRequestAndChannel, HttpResponse> instrumenter() {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
|||
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<HttpSpanNameExtractorBuilder<HttpRequestAndChannel>>
|
||||
spanNameExtractorConfigurer = builder -> {};
|
||||
private Consumer<HttpServerRouteBuilder<HttpRequestAndChannel>> httpServerRouteConfigurer =
|
||||
builder -> {};
|
||||
private boolean emitExperimentalHttpServerMetrics = false;
|
||||
|
||||
NettyServerTelemetryBuilder(OpenTelemetry openTelemetry) {
|
||||
|
|
@ -78,6 +81,8 @@ public final class NettyServerTelemetryBuilder {
|
|||
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 final class NettyServerTelemetryBuilder {
|
|||
"io.opentelemetry.netty-4.1",
|
||||
extractorConfigurer,
|
||||
spanNameExtractorConfigurer,
|
||||
httpServerRouteConfigurer,
|
||||
emitExperimentalHttpServerMetrics));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -115,4 +115,9 @@ class PlayServerTest extends HttpServerTest<Server> implements AgentTestTrait {
|
|||
attributes.remove(SemanticAttributes.HTTP_ROUTE)
|
||||
attributes
|
||||
}
|
||||
|
||||
@Override
|
||||
int getResponseCodeOnNonStandardHttpMethod() {
|
||||
404
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,4 +105,10 @@ class PlayServerTest extends HttpServerTest<Server> implements AgentTestTrait {
|
|||
Set<AttributeKey<?>> httpAttributes(ServerEndpoint endpoint) {
|
||||
[]
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean testNonStandardHttpMethod() {
|
||||
// instrumentation does not create a server span at all
|
||||
false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ abstract class AbstractRatpackHttpServerTest extends HttpServerTest<RatpackServe
|
|||
}
|
||||
|
||||
@Override
|
||||
String expectedHttpRoute(ServerEndpoint endpoint) {
|
||||
String expectedHttpRoute(ServerEndpoint endpoint, String method) {
|
||||
return endpoint.status == 404 ? "/" : endpoint == PATH_PARAM ? "/path/:id/param" : endpoint.path
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerAttribut
|
|||
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;
|
||||
|
|
@ -51,6 +52,9 @@ public final class RatpackTelemetryBuilder {
|
|||
private final HttpSpanNameExtractorBuilder<Request> httpServerSpanNameExtractorBuilder =
|
||||
HttpSpanNameExtractor.builder(RatpackHttpAttributesGetter.INSTANCE);
|
||||
|
||||
private final HttpServerRouteBuilder<Request> httpServerRouteBuilder =
|
||||
HttpServerRoute.builder(RatpackHttpAttributesGetter.INSTANCE);
|
||||
|
||||
private final List<AttributesExtractor<? super RequestSpec, ? super HttpResponse>>
|
||||
additionalHttpClientExtractors = new ArrayList<>();
|
||||
|
||||
|
|
@ -143,6 +147,7 @@ public final class RatpackTelemetryBuilder {
|
|||
httpServerAttributesExtractorBuilder.setKnownMethods(knownMethods);
|
||||
httpClientSpanNameExtractorBuilder.setKnownMethods(knownMethods);
|
||||
httpServerSpanNameExtractorBuilder.setKnownMethods(knownMethods);
|
||||
httpServerRouteBuilder.setKnownMethods(knownMethods);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
@ -187,7 +192,7 @@ public final class RatpackTelemetryBuilder {
|
|||
.addAttributesExtractor(httpServerAttributesExtractorBuilder.build())
|
||||
.addAttributesExtractors(additionalExtractors)
|
||||
.addOperationMetrics(HttpServerMetrics.get())
|
||||
.addContextCustomizer(HttpServerRoute.create(httpAttributes));
|
||||
.addContextCustomizer(httpServerRouteBuilder.build());
|
||||
if (emitExperimentalHttpServerMetrics) {
|
||||
builder.addOperationMetrics(HttpServerExperimentalMetrics.get());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerAttribut
|
|||
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<Request> httpSpanNameExtractorBuilder =
|
||||
HttpSpanNameExtractor.builder(RestletHttpAttributesGetter.INSTANCE);
|
||||
private final HttpServerRouteBuilder<Request> httpServerRouteBuilder =
|
||||
HttpServerRoute.builder(RestletHttpAttributesGetter.INSTANCE);
|
||||
private boolean emitExperimentalHttpServerMetrics = false;
|
||||
|
||||
RestletTelemetryBuilder(OpenTelemetry openTelemetry) {
|
||||
|
|
@ -93,6 +96,7 @@ public final class RestletTelemetryBuilder {
|
|||
public RestletTelemetryBuilder setKnownMethods(Set<String> knownMethods) {
|
||||
httpAttributesExtractorBuilder.setKnownMethods(knownMethods);
|
||||
httpSpanNameExtractorBuilder.setKnownMethods(knownMethods);
|
||||
httpServerRouteBuilder.setKnownMethods(knownMethods);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
@ -122,7 +126,7 @@ public final class RestletTelemetryBuilder {
|
|||
.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());
|
||||
|
|
|
|||
|
|
@ -168,14 +168,14 @@ abstract class AbstractRestletServerTest extends HttpServerTest<Server> {
|
|||
}
|
||||
|
||||
@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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -63,14 +63,14 @@ abstract class AbstractServletServerTest extends HttpServerTest<Server> {
|
|||
}
|
||||
|
||||
@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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ package io.opentelemetry.javaagent.instrumentation.restlet.v2_0;
|
|||
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());
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
|
|||
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<Request> httpSpanNameExtractorBuilder =
|
||||
HttpSpanNameExtractor.builder(RestletHttpAttributesGetter.INSTANCE);
|
||||
private final HttpServerRouteBuilder<Request> httpServerRouteBuilder =
|
||||
HttpServerRoute.builder(RestletHttpAttributesGetter.INSTANCE);
|
||||
|
||||
private boolean emitExperimentalHttpServerMetrics = false;
|
||||
|
||||
RestletTelemetryBuilder(OpenTelemetry openTelemetry) {
|
||||
|
|
@ -88,6 +93,7 @@ public final class RestletTelemetryBuilder {
|
|||
public RestletTelemetryBuilder setKnownMethods(Set<String> knownMethods) {
|
||||
httpAttributesExtractorBuilder.setKnownMethods(knownMethods);
|
||||
httpSpanNameExtractorBuilder.setKnownMethods(knownMethods);
|
||||
httpServerRouteBuilder.setKnownMethods(knownMethods);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
@ -114,6 +120,7 @@ public final class RestletTelemetryBuilder {
|
|||
openTelemetry,
|
||||
httpAttributesExtractorBuilder.build(),
|
||||
httpSpanNameExtractorBuilder.build(),
|
||||
httpServerRouteBuilder.build(),
|
||||
additionalExtractors,
|
||||
emitExperimentalHttpServerMetrics);
|
||||
|
||||
|
|
|
|||
|
|
@ -7,12 +7,12 @@ package io.opentelemetry.instrumentation.restlet.v2_0.internal;
|
|||
|
||||
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 final class RestletInstrumenterFactory {
|
|||
OpenTelemetry openTelemetry,
|
||||
AttributesExtractor<Request, Response> httpServerAttributesExtractor,
|
||||
SpanNameExtractor<Request> httpServerSpanNameExtractor,
|
||||
ContextCustomizer<Request> httpServerRoute,
|
||||
List<AttributesExtractor<Request, Response>> additionalExtractors,
|
||||
boolean emitExperimentalHttpServerMetrics) {
|
||||
|
||||
|
|
@ -41,7 +42,7 @@ public final class RestletInstrumenterFactory {
|
|||
.setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter))
|
||||
.addAttributesExtractor(httpServerAttributesExtractor)
|
||||
.addAttributesExtractors(additionalExtractors)
|
||||
.addContextCustomizer(HttpServerRoute.create(httpAttributesGetter))
|
||||
.addContextCustomizer(httpServerRoute)
|
||||
.addOperationMetrics(HttpServerMetrics.get());
|
||||
if (emitExperimentalHttpServerMetrics) {
|
||||
builder.addOperationMetrics(HttpServerExperimentalMetrics.get());
|
||||
|
|
|
|||
|
|
@ -176,14 +176,14 @@ abstract class AbstractRestletServerTest extends HttpServerTest<Server> {
|
|||
}
|
||||
|
||||
@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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<REQUEST, RESPONSE>
|
||||
implements SpanNameExtractor<ServletRequestContext<REQUEST>> {
|
||||
|
||||
private final ServletAccessor<REQUEST, RESPONSE> accessor;
|
||||
private final Set<String> knownMethods = CommonConfig.get().getKnownHttpRequestMethods();
|
||||
|
||||
public Servlet2SpanNameExtractor(ServletAccessor<REQUEST, RESPONSE> accessor) {
|
||||
this.accessor = accessor;
|
||||
|
|
@ -22,16 +26,19 @@ public class Servlet2SpanNameExtractor<REQUEST, RESPONSE>
|
|||
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";
|
||||
}
|
||||
return "HTTP request";
|
||||
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 method + " " + contextPath + servletPath;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Server> 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
|
||||
|
|
|
|||
|
|
@ -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<SERVER, CONTEXT> extends HttpServerTest<SERV
|
|||
}
|
||||
|
||||
@Override
|
||||
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 endpoint.resolvePath(address).path
|
||||
}
|
||||
switch (endpoint) {
|
||||
case NOT_FOUND:
|
||||
return getContextPath() + "/*"
|
||||
default:
|
||||
return super.expectedHttpRoute(endpoint)
|
||||
return super.expectedHttpRoute(endpoint, method)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -147,7 +156,7 @@ abstract class AbstractServlet3Test<SERVER, CONTEXT> extends HttpServerTest<SERV
|
|||
cleanup:
|
||||
ExperimentalSnippetHolder.setSnippet("")
|
||||
|
||||
def expectedRoute = expectedHttpRoute(HTML_SERVLET_OUTPUT_STREAM)
|
||||
def expectedRoute = expectedHttpRoute(HTML_SERVLET_OUTPUT_STREAM, "GET")
|
||||
assertTraces(1) {
|
||||
trace(0, 2) {
|
||||
span(0) {
|
||||
|
|
@ -190,7 +199,7 @@ abstract class AbstractServlet3Test<SERVER, CONTEXT> extends HttpServerTest<SERV
|
|||
cleanup:
|
||||
ExperimentalSnippetHolder.setSnippet("")
|
||||
|
||||
def expectedRoute = expectedHttpRoute(HTML_PRINT_WRITER)
|
||||
def expectedRoute = expectedHttpRoute(HTML_PRINT_WRITER, "GET")
|
||||
assertTraces(1) {
|
||||
trace(0, 2) {
|
||||
span(0) {
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ abstract class TomcatServlet3Test extends AbstractServlet3Test<Tomcat, Context>
|
|||
|
||||
(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<Tomcat, Context>
|
|||
}
|
||||
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++
|
||||
|
|
|
|||
|
|
@ -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<SERVER, CONTEXT> extends HttpServerTest<SERV
|
|||
}
|
||||
|
||||
@Override
|
||||
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 endpoint.resolvePath(address).path
|
||||
}
|
||||
switch (endpoint) {
|
||||
case NOT_FOUND:
|
||||
return getContextPath() + "/*"
|
||||
default:
|
||||
return super.expectedHttpRoute(endpoint)
|
||||
return super.expectedHttpRoute(endpoint, method)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -147,7 +156,7 @@ abstract class AbstractServlet5Test<SERVER, CONTEXT> extends HttpServerTest<SERV
|
|||
cleanup:
|
||||
ExperimentalSnippetHolder.setSnippet("")
|
||||
|
||||
def expectedRoute = expectedHttpRoute(HTML_SERVLET_OUTPUT_STREAM)
|
||||
def expectedRoute = expectedHttpRoute(HTML_SERVLET_OUTPUT_STREAM, "GET")
|
||||
assertTraces(1) {
|
||||
trace(0, 2) {
|
||||
span(0) {
|
||||
|
|
@ -190,7 +199,7 @@ abstract class AbstractServlet5Test<SERVER, CONTEXT> extends HttpServerTest<SERV
|
|||
cleanup:
|
||||
ExperimentalSnippetHolder.setSnippet("")
|
||||
|
||||
def expectedRoute = expectedHttpRoute(HTML_PRINT_WRITER)
|
||||
def expectedRoute = expectedHttpRoute(HTML_PRINT_WRITER, "GET")
|
||||
assertTraces(1) {
|
||||
trace(0, 2) {
|
||||
span(0) {
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ abstract class TomcatServlet5Test extends AbstractServlet5Test<Tomcat, Context>
|
|||
|
||||
(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<Tomcat, Context>
|
|||
}
|
||||
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++
|
||||
|
|
|
|||
|
|
@ -66,7 +66,10 @@ public final class ServletInstrumenterBuilder<REQUEST, RESPONSE> {
|
|||
.build())
|
||||
.addAttributesExtractor(additionalAttributesExtractor)
|
||||
.addOperationMetrics(HttpServerMetrics.get())
|
||||
.addContextCustomizer(HttpServerRoute.create(httpAttributesGetter));
|
||||
.addContextCustomizer(
|
||||
HttpServerRoute.builder(httpAttributesGetter)
|
||||
.setKnownMethods(CommonConfig.get().getKnownHttpRequestMethods())
|
||||
.build());
|
||||
if (ServletRequestParametersExtractor.enabled()) {
|
||||
AttributesExtractor<ServletRequestContext<REQUEST>, ServletResponseContext<RESPONSE>>
|
||||
requestParametersExtractor = new ServletRequestParametersExtractor<>(accessor);
|
||||
|
|
|
|||
|
|
@ -72,6 +72,6 @@ public class ImmediateHandlerSpringWebFluxServerTest extends HandlerSpringWebFlu
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ public abstract class SpringWebFluxServerTest
|
|||
}
|
||||
|
||||
@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 abstract class SpringWebFluxServerTest
|
|||
} else if (endpoint.equals(NESTED_PATH)) {
|
||||
return "/nestedPath/hello/world";
|
||||
}
|
||||
return super.expectedHttpRoute(endpoint);
|
||||
return super.expectedHttpRoute(endpoint, method);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerAttribut
|
|||
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<ServerWebExchange> httpServerSpanNameExtractorBuilder =
|
||||
HttpSpanNameExtractor.builder(WebfluxServerHttpAttributesGetter.INSTANCE);
|
||||
private final HttpServerRouteBuilder<ServerWebExchange> httpServerRouteBuilder =
|
||||
HttpServerRoute.builder(WebfluxServerHttpAttributesGetter.INSTANCE);
|
||||
|
||||
private Consumer<HttpClientAttributesExtractorBuilder<ClientRequest, ClientResponse>>
|
||||
clientExtractorConfigurer = builder -> {};
|
||||
|
|
@ -167,6 +170,7 @@ public final class SpringWebfluxTelemetryBuilder {
|
|||
clientSpanNameExtractorConfigurer.andThen(builder -> builder.setKnownMethods(knownMethods));
|
||||
httpServerAttributesExtractorBuilder.setKnownMethods(knownMethods);
|
||||
httpServerSpanNameExtractorBuilder.setKnownMethods(knownMethods);
|
||||
httpServerRouteBuilder.setKnownMethods(knownMethods);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
@ -227,7 +231,7 @@ public final class SpringWebfluxTelemetryBuilder {
|
|||
.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());
|
||||
|
|
|
|||
|
|
@ -38,11 +38,11 @@ public final class SpringWebfluxServerInstrumentationTest
|
|||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerAttribut
|
|||
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<HttpServletRequest> httpSpanNameExtractorBuilder =
|
||||
HttpSpanNameExtractor.builder(SpringWebMvcHttpAttributesGetter.INSTANCE);
|
||||
private final HttpServerRouteBuilder<HttpServletRequest> httpServerRouteBuilder =
|
||||
HttpServerRoute.builder(SpringWebMvcHttpAttributesGetter.INSTANCE);
|
||||
|
||||
@Nullable
|
||||
private Function<
|
||||
|
|
@ -114,6 +117,7 @@ public final class SpringWebMvcTelemetryBuilder {
|
|||
public SpringWebMvcTelemetryBuilder setKnownMethods(Set<String> knownMethods) {
|
||||
httpAttributesExtractorBuilder.setKnownMethods(knownMethods);
|
||||
httpSpanNameExtractorBuilder.setKnownMethods(knownMethods);
|
||||
httpServerRouteBuilder.setKnownMethods(knownMethods);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
@ -151,7 +155,7 @@ public final class SpringWebMvcTelemetryBuilder {
|
|||
.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());
|
||||
|
|
|
|||
|
|
@ -38,11 +38,11 @@ class WebMvcHttpServerTest extends AbstractHttpServerTest<ConfigurableApplicatio
|
|||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerAttribut
|
|||
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<HttpServletRequest> httpSpanNameExtractorBuilder =
|
||||
HttpSpanNameExtractor.builder(SpringWebMvcHttpAttributesGetter.INSTANCE);
|
||||
private final HttpServerRouteBuilder<HttpServletRequest> httpServerRouteBuilder =
|
||||
HttpServerRoute.builder(SpringWebMvcHttpAttributesGetter.INSTANCE);
|
||||
|
||||
@Nullable
|
||||
private Function<
|
||||
|
|
@ -114,6 +117,7 @@ public final class SpringWebMvcTelemetryBuilder {
|
|||
public SpringWebMvcTelemetryBuilder setKnownMethods(Set<String> knownMethods) {
|
||||
httpAttributesExtractorBuilder.setKnownMethods(knownMethods);
|
||||
httpSpanNameExtractorBuilder.setKnownMethods(knownMethods);
|
||||
httpServerRouteBuilder.setKnownMethods(knownMethods);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
@ -151,7 +155,7 @@ public final class SpringWebMvcTelemetryBuilder {
|
|||
.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());
|
||||
|
|
|
|||
|
|
@ -38,11 +38,11 @@ class WebMvcHttpServerTest extends AbstractHttpServerTest<ConfigurableApplicatio
|
|||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ abstract class AbstractSpringBootBasedTest extends HttpServerTest<ConfigurableAp
|
|||
}
|
||||
|
||||
@Override
|
||||
String expectedHttpRoute(ServerEndpoint endpoint) {
|
||||
String expectedHttpRoute(ServerEndpoint endpoint, String method) {
|
||||
switch (endpoint) {
|
||||
case PATH_PARAM:
|
||||
return getContextPath() + "/path/{id}/param"
|
||||
|
|
@ -94,7 +94,7 @@ abstract class AbstractSpringBootBasedTest extends HttpServerTest<ConfigurableAp
|
|||
case LOGIN:
|
||||
return getContextPath() + "/*"
|
||||
default:
|
||||
return super.expectedHttpRoute(endpoint)
|
||||
return super.expectedHttpRoute(endpoint, method)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -113,7 +113,7 @@ abstract class AbstractSpringBootBasedTest extends HttpServerTest<ConfigurableAp
|
|||
and:
|
||||
assertTraces(1) {
|
||||
trace(0, 3) {
|
||||
serverSpan(it, 0, null, null, "GET", null, AUTH_ERROR)
|
||||
serverSpan(it, 0, null, null, "GET", AUTH_ERROR)
|
||||
sendErrorSpan(it, 1, span(0))
|
||||
errorPageSpans(it, 2, null)
|
||||
}
|
||||
|
|
@ -140,7 +140,7 @@ abstract class AbstractSpringBootBasedTest extends HttpServerTest<ConfigurableAp
|
|||
and:
|
||||
assertTraces(1) {
|
||||
trace(0, 2) {
|
||||
serverSpan(it, 0, null, null, "POST", response.contentUtf8().length(), LOGIN)
|
||||
serverSpan(it, 0, null, null, "POST", LOGIN)
|
||||
redirectSpan(it, 1, span(0))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,14 +83,14 @@ abstract class AbstractServletFilterTest extends HttpServerTest<ConfigurableAppl
|
|||
}
|
||||
|
||||
@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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -65,14 +65,14 @@ class Struts2ActionSpanTest extends HttpServerTest<Server> 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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<Tomcat> 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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<Tomcat> 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()
|
||||
|
|
|
|||
|
|
@ -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<Tomcat> 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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<Tomcat> 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()
|
||||
|
|
|
|||
|
|
@ -44,7 +44,10 @@ public final class TomcatInstrumenterFactory {
|
|||
.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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<SERVER> 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<SERVER> extends InstrumentationSpecification imple
|
|||
1
|
||||
}
|
||||
|
||||
int getResponseCodeOnNonStandardHttpMethod() {
|
||||
SUCCESS.status
|
||||
}
|
||||
|
||||
boolean hasErrorPageSpans(ServerEndpoint endpoint) {
|
||||
false
|
||||
}
|
||||
|
|
@ -148,6 +160,10 @@ abstract class HttpServerTest<SERVER> extends InstrumentationSpecification imple
|
|||
true
|
||||
}
|
||||
|
||||
boolean testNonStandardHttpMethod() {
|
||||
true
|
||||
}
|
||||
|
||||
boolean verifyServerSpanEndTime() {
|
||||
return true
|
||||
}
|
||||
|
|
@ -199,8 +215,8 @@ abstract class HttpServerTest<SERVER> 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<SERVER> extends InstrumentationSpecification imple
|
|||
options.metricsInstrumentationName = {
|
||||
HttpServerTest.this.getMetricsInstrumentationName()
|
||||
}
|
||||
options.responseCodeOnNonStandardHttpMethod = getResponseCodeOnNonStandardHttpMethod()
|
||||
|
||||
options.testRedirect = testRedirect()
|
||||
options.testError = testError()
|
||||
|
|
@ -229,6 +246,9 @@ abstract class HttpServerTest<SERVER> 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<SERVER> 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<SERVER> 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<SERVER> 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<SERVER> 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<SERVER> 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 {
|
||||
|
|
|
|||
|
|
@ -27,8 +27,10 @@ import io.opentelemetry.api.trace.SpanKind;
|
|||
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 @@ public abstract class AbstractHttpServerTest<SERVER> extends AbstractHttpServerU
|
|||
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 @@ public abstract class AbstractHttpServerTest<SERVER> extends AbstractHttpServerU
|
|||
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 @@ public abstract class AbstractHttpServerTest<SERVER> extends AbstractHttpServerU
|
|||
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 @@ public abstract class AbstractHttpServerTest<SERVER> extends AbstractHttpServerU
|
|||
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<ServerEndpoint> provideServerEndpoints() {
|
||||
|
|
@ -217,7 +219,7 @@ public abstract class AbstractHttpServerTest<SERVER> extends AbstractHttpServerU
|
|||
.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 @@ public abstract class AbstractHttpServerTest<SERVER> extends AbstractHttpServerU
|
|||
}
|
||||
|
||||
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 @@ public abstract class AbstractHttpServerTest<SERVER> extends AbstractHttpServerU
|
|||
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 @@ public abstract class AbstractHttpServerTest<SERVER> extends AbstractHttpServerU
|
|||
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 @@ public abstract class AbstractHttpServerTest<SERVER> extends AbstractHttpServerU
|
|||
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 @@ public abstract class AbstractHttpServerTest<SERVER> extends AbstractHttpServerU
|
|||
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 @@ public abstract class AbstractHttpServerTest<SERVER> extends AbstractHttpServerU
|
|||
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 @@ public abstract class AbstractHttpServerTest<SERVER> extends AbstractHttpServerU
|
|||
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 @@ public abstract class AbstractHttpServerTest<SERVER> extends AbstractHttpServerU
|
|||
}
|
||||
}
|
||||
|
||||
@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 @@ public abstract class AbstractHttpServerTest<SERVER> extends AbstractHttpServerU
|
|||
String parentId,
|
||||
String spanId,
|
||||
String method,
|
||||
ServerEndpoint endpoint,
|
||||
AggregatedHttpResponse response) {
|
||||
ServerEndpoint endpoint) {
|
||||
List<Consumer<TraceAssert>> assertions = new ArrayList<>();
|
||||
for (int i = 0; i < size; i++) {
|
||||
assertions.add(
|
||||
|
|
@ -575,7 +625,7 @@ public abstract class AbstractHttpServerTest<SERVER> extends AbstractHttpServerU
|
|||
List<Consumer<SpanDataAssert>> 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 @@ public abstract class AbstractHttpServerTest<SERVER> extends AbstractHttpServerU
|
|||
@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<AttributeKey<?>> 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 @@ public abstract class AbstractHttpServerTest<SERVER> extends AbstractHttpServerU
|
|||
}
|
||||
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<String> netProtocolKey =
|
||||
getAttributeKey(SemanticAttributes.NET_PROTOCOL_NAME);
|
||||
|
|
@ -823,17 +873,12 @@ public abstract class AbstractHttpServerTest<SERVER> extends AbstractHttpServerU
|
|||
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 abstract class AbstractHttpServerTest<SERVER> extends AbstractHttpServerU
|
|||
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)) {
|
||||
|
|
|
|||
|
|
@ -9,11 +9,13 @@ import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint
|
|||
|
||||
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<ServerEndpoint, Set<AttributeKey<?>>> httpAttributes = unused -> DEFAULT_HTTP_ATTRIBUTES;
|
||||
SpanNameMapper expectedServerSpanNameMapper = DEFAULT_EXPECTED_SERVER_SPAN_NAME_MAPPER;
|
||||
Function<ServerEndpoint, String> expectedHttpRoute = unused -> null;
|
||||
BiFunction<ServerEndpoint, String, String> expectedHttpRoute = (endpoint, method) -> null;
|
||||
Function<ServerEndpoint, String> sockPeerAddr = unused -> "127.0.0.1";
|
||||
String contextPath = "";
|
||||
Throwable expectedException = new Exception(EXCEPTION.body);
|
||||
Supplier<String> metricsInstrumentationName = () -> null;
|
||||
// we're calling /success in the test, and most servers respond with 200 anyway
|
||||
int responseCodeOnNonStandardHttpMethod = ServerEndpoint.SUCCESS.status;
|
||||
|
||||
Predicate<ServerEndpoint> hasHandlerSpan = unused -> false;
|
||||
Predicate<ServerEndpoint> 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 final class HttpServerTestOptions {
|
|||
|
||||
@CanIgnoreReturnValue
|
||||
public HttpServerTestOptions setExpectedHttpRoute(
|
||||
Function<ServerEndpoint, String> expectedHttpRoute) {
|
||||
BiFunction<ServerEndpoint, String, String> expectedHttpRoute) {
|
||||
this.expectedHttpRoute = expectedHttpRoute;
|
||||
return this;
|
||||
}
|
||||
|
|
@ -107,6 +117,13 @@ public final class HttpServerTestOptions {
|
|||
return this;
|
||||
}
|
||||
|
||||
@CanIgnoreReturnValue
|
||||
public HttpServerTestOptions setResponseCodeOnNonStandardHttpMethod(
|
||||
int responseCodeOnNonStandardHttpMethod) {
|
||||
this.responseCodeOnNonStandardHttpMethod = responseCodeOnNonStandardHttpMethod;
|
||||
return this;
|
||||
}
|
||||
|
||||
@CanIgnoreReturnValue
|
||||
public HttpServerTestOptions setHasHandlerSpan(Predicate<ServerEndpoint> hasHandlerSpan) {
|
||||
this.hasHandlerSpan = hasHandlerSpan;
|
||||
|
|
@ -201,6 +218,13 @@ public final class HttpServerTestOptions {
|
|||
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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue