Update jaxrs-1.0 to Instrumenter API (#3797)
This commit is contained in:
parent
f5be16bc7c
commit
19711ca76b
|
@ -5,16 +5,7 @@
|
|||
|
||||
package io.opentelemetry.javaagent.instrumentation.jaxrs.v1_0;
|
||||
|
||||
import io.opentelemetry.api.trace.Span;
|
||||
import io.opentelemetry.api.trace.SpanBuilder;
|
||||
import io.opentelemetry.api.trace.SpanKind;
|
||||
import io.opentelemetry.context.Context;
|
||||
import io.opentelemetry.instrumentation.api.servlet.ServletContextPath;
|
||||
import io.opentelemetry.instrumentation.api.tracer.BaseTracer;
|
||||
import io.opentelemetry.instrumentation.api.tracer.ServerSpan;
|
||||
import io.opentelemetry.instrumentation.api.tracer.SpanNames;
|
||||
import io.opentelemetry.javaagent.bootstrap.jaxrs.ClassHierarchyIterable;
|
||||
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
|
@ -22,15 +13,9 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||
import javax.ws.rs.HttpMethod;
|
||||
import javax.ws.rs.Path;
|
||||
|
||||
public class JaxRsAnnotationsTracer extends BaseTracer {
|
||||
public class HandlerData {
|
||||
|
||||
private static final JaxRsAnnotationsTracer TRACER = new JaxRsAnnotationsTracer();
|
||||
|
||||
public static JaxRsAnnotationsTracer tracer() {
|
||||
return TRACER;
|
||||
}
|
||||
|
||||
private final ClassValue<Map<Method, String>> spanNames =
|
||||
private static final ClassValue<Map<Method, String>> serverSpanNames =
|
||||
new ClassValue<Map<Method, String>>() {
|
||||
@Override
|
||||
protected Map<Method, String> computeValue(Class<?> type) {
|
||||
|
@ -38,37 +23,20 @@ public class JaxRsAnnotationsTracer extends BaseTracer {
|
|||
}
|
||||
};
|
||||
|
||||
public Context startSpan(Class<?> target, Method method) {
|
||||
String pathBasedSpanName = getPathSpanName(target, method);
|
||||
Context parentContext = Context.current();
|
||||
Span serverSpan = ServerSpan.fromContextOrNull(parentContext);
|
||||
private final Class<?> target;
|
||||
private final Method method;
|
||||
|
||||
// When jax-rs is the root, we want to name using the path, otherwise use the class/method.
|
||||
String spanName;
|
||||
if (serverSpan == null) {
|
||||
spanName = pathBasedSpanName;
|
||||
} else {
|
||||
spanName = SpanNames.fromMethod(target, method);
|
||||
updateServerSpanName(parentContext, serverSpan, pathBasedSpanName);
|
||||
}
|
||||
|
||||
SpanBuilder spanBuilder = spanBuilder(parentContext, spanName, SpanKind.INTERNAL);
|
||||
setCodeAttributes(spanBuilder, target, method);
|
||||
Span span = spanBuilder.startSpan();
|
||||
return parentContext.with(span);
|
||||
public HandlerData(Class<?> target, Method method) {
|
||||
this.target = target;
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
private static void setCodeAttributes(SpanBuilder spanBuilder, Class<?> target, Method method) {
|
||||
spanBuilder.setAttribute(SemanticAttributes.CODE_NAMESPACE, target.getName());
|
||||
if (method != null) {
|
||||
spanBuilder.setAttribute(SemanticAttributes.CODE_FUNCTION, method.getName());
|
||||
}
|
||||
public Class<?> codeClass() {
|
||||
return target;
|
||||
}
|
||||
|
||||
private static void updateServerSpanName(Context context, Span span, String spanName) {
|
||||
if (!spanName.isEmpty()) {
|
||||
span.updateName(ServletContextPath.prepend(context, spanName));
|
||||
}
|
||||
public String methodName() {
|
||||
return method.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -77,8 +45,8 @@ public class JaxRsAnnotationsTracer extends BaseTracer {
|
|||
*
|
||||
* @return The result can be an empty string but will never be {@code null}.
|
||||
*/
|
||||
private String getPathSpanName(Class<?> target, Method method) {
|
||||
Map<Method, String> classMap = spanNames.get(target);
|
||||
String getServerSpanName() {
|
||||
Map<Method, String> classMap = serverSpanNames.get(target);
|
||||
String spanName = classMap.get(method);
|
||||
if (spanName == null) {
|
||||
String httpMethod = null;
|
||||
|
@ -168,11 +136,12 @@ public class JaxRsAnnotationsTracer extends BaseTracer {
|
|||
StringBuilder spanNameBuilder = new StringBuilder();
|
||||
boolean skipSlash = false;
|
||||
if (classPath != null) {
|
||||
if (!classPath.value().startsWith("/")) {
|
||||
String classPathValue = classPath.value();
|
||||
if (!classPathValue.startsWith("/")) {
|
||||
spanNameBuilder.append("/");
|
||||
}
|
||||
spanNameBuilder.append(classPath.value());
|
||||
skipSlash = classPath.value().endsWith("/");
|
||||
spanNameBuilder.append(classPathValue);
|
||||
skipSlash = classPathValue.endsWith("/") || classPathValue.isEmpty();
|
||||
}
|
||||
|
||||
if (methodPath != null) {
|
||||
|
@ -189,9 +158,4 @@ public class JaxRsAnnotationsTracer extends BaseTracer {
|
|||
|
||||
return spanNameBuilder.toString().trim();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getInstrumentationName() {
|
||||
return "io.opentelemetry.jaxrs-1.0";
|
||||
}
|
||||
}
|
|
@ -8,7 +8,7 @@ package io.opentelemetry.javaagent.instrumentation.jaxrs.v1_0;
|
|||
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
|
||||
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasSuperMethod;
|
||||
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasSuperType;
|
||||
import static io.opentelemetry.javaagent.instrumentation.jaxrs.v1_0.JaxRsAnnotationsTracer.tracer;
|
||||
import static io.opentelemetry.javaagent.instrumentation.jaxrs.v1_0.JaxrsSingletons.instrumenter;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.declaresMethod;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||
|
@ -17,16 +17,18 @@ import static net.bytebuddy.matcher.ElementMatchers.namedOneOf;
|
|||
|
||||
import io.opentelemetry.context.Context;
|
||||
import io.opentelemetry.context.Scope;
|
||||
import io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming;
|
||||
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
|
||||
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.CallDepth;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
|
||||
import java.lang.reflect.Method;
|
||||
import javax.ws.rs.Path;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
|
||||
public class JaxRsAnnotationsInstrumentation implements TypeInstrumentation {
|
||||
public class JaxrsAnnotationsInstrumentation implements TypeInstrumentation {
|
||||
|
||||
@Override
|
||||
public ElementMatcher<ClassLoader> classLoaderOptimization() {
|
||||
|
@ -55,7 +57,7 @@ public class JaxRsAnnotationsInstrumentation implements TypeInstrumentation {
|
|||
"javax.ws.rs.OPTIONS",
|
||||
"javax.ws.rs.POST",
|
||||
"javax.ws.rs.PUT")))),
|
||||
JaxRsAnnotationsInstrumentation.class.getName() + "$JaxRsAnnotationsAdvice");
|
||||
JaxrsAnnotationsInstrumentation.class.getName() + "$JaxRsAnnotationsAdvice");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
|
@ -66,13 +68,27 @@ public class JaxRsAnnotationsInstrumentation implements TypeInstrumentation {
|
|||
@Advice.This Object target,
|
||||
@Advice.Origin Method method,
|
||||
@Advice.Local("otelCallDepth") CallDepth callDepth,
|
||||
@Advice.Local("otelHandlerData") HandlerData handlerData,
|
||||
@Advice.Local("otelContext") Context context,
|
||||
@Advice.Local("otelScope") Scope scope) {
|
||||
callDepth = CallDepth.forClass(Path.class);
|
||||
if (callDepth.getAndIncrement() > 0) {
|
||||
return;
|
||||
}
|
||||
context = tracer().startSpan(target.getClass(), method);
|
||||
|
||||
Context parentContext = Java8BytecodeBridge.currentContext();
|
||||
handlerData = new HandlerData(target.getClass(), method);
|
||||
|
||||
ServerSpanNaming.updateServerSpanName(
|
||||
parentContext,
|
||||
ServerSpanNaming.Source.CONTROLLER,
|
||||
JaxrsServerSpanNaming.getServerSpanNameSupplier(parentContext, handlerData));
|
||||
|
||||
if (!instrumenter().shouldStart(parentContext, handlerData)) {
|
||||
return;
|
||||
}
|
||||
|
||||
context = instrumenter().start(parentContext, handlerData);
|
||||
scope = context.makeCurrent();
|
||||
}
|
||||
|
||||
|
@ -80,18 +96,18 @@ public class JaxRsAnnotationsInstrumentation implements TypeInstrumentation {
|
|||
public static void stopSpan(
|
||||
@Advice.Thrown Throwable throwable,
|
||||
@Advice.Local("otelCallDepth") CallDepth callDepth,
|
||||
@Advice.Local("otelHandlerData") HandlerData handlerData,
|
||||
@Advice.Local("otelContext") Context context,
|
||||
@Advice.Local("otelScope") Scope scope) {
|
||||
if (callDepth.decrementAndGet() > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
scope.close();
|
||||
if (throwable == null) {
|
||||
tracer().end(context);
|
||||
} else {
|
||||
tracer().endExceptionally(context, throwable);
|
||||
if (scope == null) {
|
||||
return;
|
||||
}
|
||||
scope.close();
|
||||
instrumenter().end(context, handlerData, null, throwable);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.jaxrs.v1_0;
|
||||
|
||||
import io.opentelemetry.instrumentation.api.instrumenter.code.CodeAttributesExtractor;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public class JaxrsCodeAttributesExtractor extends CodeAttributesExtractor<HandlerData, Void> {
|
||||
@Override
|
||||
protected @Nullable Class<?> codeClass(HandlerData handlerData) {
|
||||
return handlerData.codeClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable String methodName(HandlerData handlerData) {
|
||||
return handlerData.methodName();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable String filePath(HandlerData handlerData) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable Long lineNumber(HandlerData handlerData) {
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -16,8 +16,8 @@ import java.util.List;
|
|||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
|
||||
@AutoService(InstrumentationModule.class)
|
||||
public class JaxRsInstrumentationModule extends InstrumentationModule {
|
||||
public JaxRsInstrumentationModule() {
|
||||
public class JaxrsInstrumentationModule extends InstrumentationModule {
|
||||
public JaxrsInstrumentationModule() {
|
||||
super("jaxrs", "jaxrs-1.0");
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,6 @@ public class JaxRsInstrumentationModule extends InstrumentationModule {
|
|||
|
||||
@Override
|
||||
public List<TypeInstrumentation> typeInstrumentations() {
|
||||
return singletonList(new JaxRsAnnotationsInstrumentation());
|
||||
return singletonList(new JaxrsAnnotationsInstrumentation());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.jaxrs.v1_0;
|
||||
|
||||
import io.opentelemetry.context.Context;
|
||||
import io.opentelemetry.instrumentation.api.servlet.ServletContextPath;
|
||||
import io.opentelemetry.javaagent.bootstrap.jaxrs.JaxrsContextPath;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class JaxrsServerSpanNaming {
|
||||
|
||||
public static Supplier<String> getServerSpanNameSupplier(
|
||||
Context context, HandlerData handlerData) {
|
||||
return () -> {
|
||||
String pathBasedSpanName = handlerData.getServerSpanName();
|
||||
// If path based name is empty skip prepending context path so that path based name would
|
||||
// remain as an empty string for which we skip updating span name. Path base span name is
|
||||
// empty when method and class don't have a jax-rs path annotation, this can happen when
|
||||
// creating an "abort" span, see RequestContextHelper.
|
||||
if (!pathBasedSpanName.isEmpty()) {
|
||||
pathBasedSpanName = JaxrsContextPath.prepend(context, pathBasedSpanName);
|
||||
pathBasedSpanName = ServletContextPath.prepend(context, pathBasedSpanName);
|
||||
}
|
||||
return pathBasedSpanName;
|
||||
};
|
||||
}
|
||||
|
||||
private JaxrsServerSpanNaming() {}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.jaxrs.v1_0;
|
||||
|
||||
import io.opentelemetry.api.GlobalOpenTelemetry;
|
||||
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
|
||||
import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor;
|
||||
import io.opentelemetry.instrumentation.api.instrumenter.code.CodeAttributesExtractor;
|
||||
import io.opentelemetry.instrumentation.api.instrumenter.code.CodeSpanNameExtractor;
|
||||
|
||||
public final class JaxrsSingletons {
|
||||
|
||||
public static final String ABORT_FILTER_CLASS =
|
||||
"io.opentelemetry.javaagent.instrumentation.jaxrs2.filter.abort.class";
|
||||
public static final String ABORT_HANDLED =
|
||||
"io.opentelemetry.javaagent.instrumentation.jaxrs2.filter.abort.handled";
|
||||
|
||||
private static final String INSTRUMENTATION_NAME = "io.opentelemetry.jaxrs-1.0-common";
|
||||
|
||||
private static final Instrumenter<HandlerData, Void> INSTRUMENTER;
|
||||
|
||||
static {
|
||||
CodeAttributesExtractor<HandlerData, Void> codeAttributesExtractor =
|
||||
new JaxrsCodeAttributesExtractor();
|
||||
SpanNameExtractor<HandlerData> spanNameExtractor =
|
||||
CodeSpanNameExtractor.create(codeAttributesExtractor);
|
||||
|
||||
INSTRUMENTER =
|
||||
Instrumenter.<HandlerData, Void>newBuilder(
|
||||
GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, spanNameExtractor)
|
||||
.addAttributesExtractor(codeAttributesExtractor)
|
||||
.newInstrumenter();
|
||||
}
|
||||
|
||||
public static Instrumenter<HandlerData, Void> instrumenter() {
|
||||
return INSTRUMENTER;
|
||||
}
|
||||
|
||||
private JaxrsSingletons() {}
|
||||
}
|
|
@ -20,30 +20,6 @@ import spock.lang.Unroll
|
|||
|
||||
class JaxRsAnnotations1InstrumentationTest extends AgentInstrumentationSpecification {
|
||||
|
||||
def "instrumentation can be used as root span and resource is set to METHOD PATH"() {
|
||||
setup:
|
||||
def jax = new Jax() {
|
||||
@POST
|
||||
@Path("/a")
|
||||
void call() {
|
||||
}
|
||||
}
|
||||
jax.call()
|
||||
|
||||
expect:
|
||||
assertTraces(1) {
|
||||
trace(0, 1) {
|
||||
span(0) {
|
||||
name "/a"
|
||||
attributes {
|
||||
"${SemanticAttributes.CODE_NAMESPACE.key}" jax.getClass().getName()
|
||||
"${SemanticAttributes.CODE_FUNCTION.key}" "call"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Unroll
|
||||
def "span named '#paramName' from annotations on class '#className' when is not root span"() {
|
||||
setup:
|
||||
|
|
Loading…
Reference in New Issue