Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_MESSAGE;
import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_STACKTRACE;
import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_TYPE;
import static io.opentelemetry.semconv.incubating.CodeIncubatingAttributes.CODE_FUNCTION;
import static io.opentelemetry.semconv.incubating.CodeIncubatingAttributes.CODE_NAMESPACE;
import static org.assertj.core.api.Assertions.assertThat;

import com.google.common.collect.ImmutableMap;
Expand All @@ -27,6 +29,7 @@
import org.junit.jupiter.api.extension.RegisterExtension;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;

class SpringBootBasedTest extends AbstractSpringBootBasedTest {

Expand Down Expand Up @@ -73,6 +76,9 @@ protected SpanDataAssert assertHandlerSpan(
span.hasName(handlerSpanName)
.hasKind(SpanKind.INTERNAL)
.hasStatus(StatusData.error())
.hasAttributesSatisfyingExactly(
equalTo(CODE_NAMESPACE, ResourceHttpRequestHandler.class.getName()),
equalTo(CODE_FUNCTION, "handleRequest"))
.hasEventsSatisfyingExactly(
event ->
event
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,48 +5,50 @@

package io.opentelemetry.javaagent.instrumentation.spring.webmvc;

import io.opentelemetry.instrumentation.api.incubator.semconv.util.SpanNames;
import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor;
import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeAttributesGetter;
import java.lang.reflect.Method;
import javax.annotation.Nullable;
import org.springframework.web.HttpRequestHandler;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.Controller;

public class HandlerSpanNameExtractor implements SpanNameExtractor<Object> {
public class HandlerCodeAttributesGetter implements CodeAttributesGetter<Object> {

@Nullable private static final Class<?> JAVAX_SERVLET = loadOrNull("javax.servlet.Servlet");
@Nullable private static final Class<?> JAKARTA_SERVLET = loadOrNull("jakarta.servlet.Servlet");

@Nullable
@Override
public String extract(Object handler) {
Class<?> clazz;
String methodName;
public Class<?> getCodeClass(Object handler) {
if (handler instanceof HandlerMethod) {
// name span based on the class and method name defined in the handler
Method method = ((HandlerMethod) handler).getMethod();
return method.getDeclaringClass();
} else {
return handler.getClass();
}
}

@Nullable
@Override
public String getMethodName(Object handler) {
if (handler instanceof HandlerMethod) {
// name span based on the class and method name defined in the handler
Method method = ((HandlerMethod) handler).getMethod();
clazz = method.getDeclaringClass();
methodName = method.getName();
return method.getName();
} else if (handler instanceof HttpRequestHandler) {
// org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter
clazz = handler.getClass();
methodName = "handleRequest";
return "handleRequest";
} else if (handler instanceof Controller) {
// org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter
clazz = handler.getClass();
methodName = "handleRequest";
return "handleRequest";
} else if (isServlet(handler)) {
// org.springframework.web.servlet.handler.SimpleServletHandlerAdapter
clazz = handler.getClass();
methodName = "service";
return "service";
} else {
// perhaps org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
clazz = handler.getClass();
methodName = "<annotation>";
return "<annotation>";
}

return SpanNames.fromMethod(clazz, methodName);
}

private static boolean isServlet(Object handler) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
package io.opentelemetry.javaagent.instrumentation.spring.webmvc;

import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeAttributesExtractor;
import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeSpanNameExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.javaagent.bootstrap.internal.ExperimentalConfig;
import org.springframework.web.servlet.ModelAndView;
Expand All @@ -19,8 +21,12 @@ public SpringWebMvcInstrumenterFactory(String instrumentationName) {
}

public Instrumenter<Object, Void> createHandlerInstrumenter() {
HandlerCodeAttributesGetter codeAttributesGetter = new HandlerCodeAttributesGetter();
return Instrumenter.<Object, Void>builder(
GlobalOpenTelemetry.get(), instrumentationName, new HandlerSpanNameExtractor())
GlobalOpenTelemetry.get(),
instrumentationName,
CodeSpanNameExtractor.create(codeAttributesGetter))
.addAttributesExtractor(CodeAttributesExtractor.create(codeAttributesGetter))
.setEnabled(ExperimentalConfig.get().controllerTelemetryEnabled())
.buildInstrumenter();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import static org.assertj.core.api.Assertions.assertThat;

import io.opentelemetry.api.common.AttributeKey;
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.http.AbstractHttpServerTest;
Expand All @@ -46,6 +45,7 @@
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.security.web.util.OnCommittedResponseWrapper;
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;
import org.springframework.web.servlet.view.RedirectView;

public abstract class AbstractSpringBootBasedTest
Expand Down Expand Up @@ -149,7 +149,9 @@ protected List<Consumer<SpanDataAssert>> errorPageSpanAssertions(
span ->
span.hasName("BasicErrorController.error")
.hasKind(SpanKind.INTERNAL)
.hasAttributes(Attributes.empty()));
.hasAttributesSatisfyingExactly(
satisfies(CODE_NAMESPACE, v -> v.endsWith(".BasicErrorController")),
equalTo(CODE_FUNCTION, "error")));
return spanAssertions;
}

Expand Down Expand Up @@ -196,10 +198,16 @@ protected SpanDataAssert assertRenderSpan(
protected SpanDataAssert assertHandlerSpan(
SpanDataAssert span, String method, ServerEndpoint endpoint) {
String handlerSpanName = getHandlerSpanName(endpoint);
String codeNamespace = TestController.class.getName();
if (endpoint == NOT_FOUND) {
handlerSpanName = "ResourceHttpRequestHandler.handleRequest";
codeNamespace = ResourceHttpRequestHandler.class.getName();
}
span.hasName(handlerSpanName).hasKind(SpanKind.INTERNAL);
String codeFunction = handlerSpanName.substring(handlerSpanName.indexOf('.') + 1);
span.hasName(handlerSpanName)
.hasKind(SpanKind.INTERNAL)
.hasAttributesSatisfyingExactly(
equalTo(CODE_NAMESPACE, codeNamespace), equalTo(CODE_FUNCTION, codeFunction));
if (endpoint == EXCEPTION) {
span.hasStatus(StatusData.error());
span.hasEventsSatisfyingExactly(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.PATH_PARAM;
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.QUERY_PARAM;
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies;
import static io.opentelemetry.semconv.incubating.CodeIncubatingAttributes.CODE_FUNCTION;
import static io.opentelemetry.semconv.incubating.CodeIncubatingAttributes.CODE_NAMESPACE;
import static org.assertj.core.api.Assertions.assertThat;

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.http.AbstractHttpServerTest;
Expand Down Expand Up @@ -97,7 +100,9 @@ protected List<Consumer<SpanDataAssert>> errorPageSpanAssertions(
span ->
span.hasName("BasicErrorController.error")
.hasKind(SpanKind.INTERNAL)
.hasAttributes(Attributes.empty()));
.hasAttributesSatisfyingExactly(
satisfies(CODE_NAMESPACE, v -> v.endsWith(".BasicErrorController")),
equalTo(CODE_FUNCTION, "error")));
return spanAssertions;
}

Expand Down
Loading