Skip to content

Commit

Permalink
Add code attributes to spring webflux controller spans and remove `sp…
Browse files Browse the repository at this point in the history
…ring-webflux.handler.type` (#12887)

Co-authored-by: Trask Stalnaker <[email protected]>
  • Loading branch information
cuichenli and trask authored Dec 20, 2024
1 parent de6e0ef commit ce2ec18
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,28 @@

import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeAttributesGetter;
import javax.annotation.Nullable;
import org.springframework.web.method.HandlerMethod;

public class HandlerCodeAttributesGetter implements CodeAttributesGetter<Object> {
@Nullable
@Override
public Class<?> getCodeClass(Object handler) {
return handler.getClass();
if (handler instanceof HandlerMethod) {
// Special case for requests mapped with annotations
HandlerMethod handlerMethod = (HandlerMethod) handler;
return handlerMethod.getMethod().getDeclaringClass();
} else {
return handler.getClass();
}
}

@Nullable
@Override
public String getMethodName(Object handler) {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
return handlerMethod.getMethod().getName();
}
return "handle";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
package io.opentelemetry.javaagent.instrumentation.spring.webflux.v5_0.server;

import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeAttributesExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder;
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRouteGetter;
import io.opentelemetry.javaagent.bootstrap.internal.ExperimentalConfig;
import io.opentelemetry.javaagent.instrumentation.spring.webflux.v5_0.SpringWebfluxConfig;
import org.springframework.web.reactive.HandlerMapping;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.pattern.PathPattern;
Expand All @@ -25,13 +25,11 @@ public final class WebfluxSingletons {
Instrumenter.builder(
GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, new WebfluxSpanNameExtractor());

if (SpringWebfluxConfig.captureExperimentalSpanAttributes()) {
builder.addAttributesExtractor(new ExperimentalAttributesExtractor());
}

INSTRUMENTER =
builder
.setEnabled(ExperimentalConfig.get().controllerTelemetryEnabled())
.addAttributesExtractor(
CodeAttributesExtractor.create(new HandlerCodeAttributesGetter()))
.buildInstrumenter();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

package io.opentelemetry.javaagent.instrumentation.spring.webflux.v5_0.server;

import static io.opentelemetry.api.common.AttributeKey.stringKey;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies;
Expand All @@ -25,6 +24,8 @@
import static io.opentelemetry.semconv.UrlAttributes.URL_PATH;
import static io.opentelemetry.semconv.UrlAttributes.URL_SCHEME;
import static io.opentelemetry.semconv.UserAgentAttributes.USER_AGENT_ORIGINAL;
import static io.opentelemetry.semconv.incubating.CodeIncubatingAttributes.CODE_FUNCTION;
import static io.opentelemetry.semconv.incubating.CodeIncubatingAttributes.CODE_NAMESPACE;
import static org.junit.jupiter.api.Named.named;

import io.opentelemetry.api.trace.SpanKind;
Expand Down Expand Up @@ -139,7 +140,12 @@ void basicGetTest(Parameter parameter) {
.hasParent(trace.getSpan(0))
.hasAttributesSatisfyingExactly(
satisfies(
stringKey("spring-webflux.handler.type"),
CODE_FUNCTION,
parameter.annotatedMethod == null
? val -> val.isEqualTo("handle")
: val -> val.isEqualTo(parameter.annotatedMethod)),
satisfies(
CODE_NAMESPACE,
parameter.annotatedMethod == null
? val -> val.contains(INNER_HANDLER_FUNCTION_CLASS_TAG_PREFIX)
: val -> val.isEqualTo(TestController.class.getName())));
Expand Down Expand Up @@ -258,7 +264,12 @@ void getAsyncResponseTest(Parameter parameter) {
.hasParent(trace.getSpan(0))
.hasAttributesSatisfyingExactly(
satisfies(
stringKey("spring-webflux.handler.type"),
CODE_FUNCTION,
parameter.annotatedMethod == null
? val -> val.isEqualTo("handle")
: val -> val.isEqualTo(parameter.annotatedMethod)),
satisfies(
CODE_NAMESPACE,
parameter.annotatedMethod == null
? val -> val.contains(INNER_HANDLER_FUNCTION_CLASS_TAG_PREFIX)
: val -> val.isEqualTo(TestController.class.getName())));
Expand Down Expand Up @@ -364,7 +375,12 @@ void createSpanDuringHandlerFunctionTest(Parameter parameter) {
.hasParent(trace.getSpan(0))
.hasAttributesSatisfyingExactly(
satisfies(
stringKey("spring-webflux.handler.type"),
CODE_FUNCTION,
parameter.annotatedMethod == null
? val -> val.isEqualTo("handle")
: val -> val.isEqualTo(parameter.annotatedMethod)),
satisfies(
CODE_NAMESPACE,
parameter.annotatedMethod == null
? val -> val.contains(INNER_HANDLER_FUNCTION_CLASS_TAG_PREFIX)
: val -> val.isEqualTo(TestController.class.getName())));
Expand Down Expand Up @@ -428,8 +444,9 @@ void get404Test() {
.hasStatus(StatusData.error())
.hasEventsSatisfyingExactly(SpringWebfluxTest::resource404Exception)
.hasAttributesSatisfyingExactly(
equalTo(CODE_FUNCTION, "handle"),
equalTo(
stringKey("spring-webflux.handler.type"),
CODE_NAMESPACE,
"org.springframework.web.reactive.resource.ResourceWebHandler"))));
}

Expand Down Expand Up @@ -485,9 +502,8 @@ void basicPostTest() {
.hasKind(SpanKind.INTERNAL)
.hasParent(trace.getSpan(0))
.hasAttributesSatisfyingExactly(
satisfies(
stringKey("spring-webflux.handler.type"),
val -> val.contains(EchoHandlerFunction.class.getName()))),
equalTo(CODE_FUNCTION, "handle"),
equalTo(CODE_NAMESPACE, EchoHandlerFunction.class.getName())),
span ->
span.hasName("echo").hasParent(trace.getSpan(1)).hasTotalAttributeCount(0)));
}
Expand Down Expand Up @@ -545,7 +561,12 @@ void getToBadEndpointTest(Parameter parameter) {
val -> val.isInstanceOf(String.class))))
.hasAttributesSatisfyingExactly(
satisfies(
stringKey("spring-webflux.handler.type"),
CODE_FUNCTION,
parameter.annotatedMethod == null
? val -> val.isEqualTo("handle")
: val -> val.isEqualTo(parameter.annotatedMethod)),
satisfies(
CODE_NAMESPACE,
parameter.annotatedMethod == null
? val -> val.contains(INNER_HANDLER_FUNCTION_CLASS_TAG_PREFIX)
: val -> val.isEqualTo(TestController.class.getName())));
Expand Down Expand Up @@ -603,8 +624,9 @@ void redirectTest() {
.hasKind(SpanKind.INTERNAL)
.hasParent(trace.getSpan(0))
.hasAttributesSatisfyingExactly(
equalTo(CODE_FUNCTION, "handle"),
satisfies(
stringKey("spring-webflux.handler.type"),
CODE_NAMESPACE,
val -> val.startsWith("server.RedirectComponent$$Lambda")))),
trace ->
trace.hasSpansSatisfyingExactly(
Expand All @@ -631,8 +653,9 @@ void redirectTest() {
span.hasKind(SpanKind.INTERNAL)
.hasParent(trace.getSpan(0))
.hasAttributesSatisfyingExactly(
equalTo(CODE_FUNCTION, "handle"),
satisfies(
stringKey("spring-webflux.handler.type"),
CODE_NAMESPACE,
val -> val.contains(INNER_HANDLER_FUNCTION_CLASS_TAG_PREFIX)));
}));
}
Expand Down Expand Up @@ -689,7 +712,12 @@ void multipleGetsToDelayingRoute(Parameter parameter) {
.hasParent(trace.getSpan(0))
.hasAttributesSatisfyingExactly(
satisfies(
stringKey("spring-webflux.handler.type"),
CODE_FUNCTION,
parameter.annotatedMethod == null
? val -> val.isEqualTo("handle")
: val -> val.isEqualTo(parameter.annotatedMethod)),
satisfies(
CODE_NAMESPACE,
parameter.annotatedMethod == null
? val -> val.contains(INNER_HANDLER_FUNCTION_CLASS_TAG_PREFIX)
: val -> val.isEqualTo(TestController.class.getName())));
Expand Down Expand Up @@ -760,10 +788,11 @@ void cancelRequestTest() throws Exception {
.hasKind(SpanKind.INTERNAL)
.hasParent(trace.getSpan(0))
.hasAttributesSatisfyingExactly(
equalTo(CODE_FUNCTION, "handle"),
satisfies(
stringKey("spring-webflux.handler.type"),
value ->
value.startsWith(
CODE_NAMESPACE,
val ->
val.startsWith(
"server.SpringWebFluxTestApplication$$Lambda")))));

SpringWebFluxTestApplication.resumeSlowRequest();
Expand Down

0 comments on commit ce2ec18

Please sign in to comment.