Add a ClassAndMethod class to Instrumentation API (#4619)

* Add a ClassAndMethod class to Instrumentation API

* remove sentence

* Update docs/contributing/writing-instrumentation.md

Co-authored-by: Trask Stalnaker <trask.stalnaker@gmail.com>

* address review comment

Co-authored-by: Trask Stalnaker <trask.stalnaker@gmail.com>
This commit is contained in:
Lauri Tulmin 2021-11-10 23:33:11 +02:00 committed by GitHub
parent 4c39b212da
commit 16728e2445
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 93 additions and 60 deletions

View File

@ -340,3 +340,18 @@ bytecode tweaks to optimize it. Because of this, retrieving a `VirtualField` ins
limited: the `VirtualField#get()` method must receive class references as its parameters; it won't limited: the `VirtualField#get()` method must receive class references as its parameters; it won't
work with variables, method params, etc. Both the owner class and the field class must be known at work with variables, method params, etc. Both the owner class and the field class must be known at
compile time for it to work. compile time for it to work.
### Why we don't use ByteBuddy @Advice.Origin Method
Instead of
```
@Advice.Origin Method method
```
we prefer to use
```
@Advice.Origin("#t") Class<?> declaringClass,
@Advice.Origin("#m") String methodName
```
because the former inserts a call to `Class.getMethod(...)` in transformed method. In contrast,
getting the declaring class and method name is just loading constants from constant pool, which is
a much simpler operation.

View File

@ -5,6 +5,7 @@
package io.opentelemetry.instrumentation.api.tracer; package io.opentelemetry.instrumentation.api.tracer;
import io.opentelemetry.instrumentation.api.util.ClassAndMethod;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@ -37,6 +38,14 @@ public final class SpanNames {
return fromMethod(clazz, method == null ? "<unknown>" : method.getName()); return fromMethod(clazz, method == null ? "<unknown>" : method.getName());
} }
/**
* This method is used to generate a span name based on a method. Anonymous classes are named
* based on their parent.
*/
public static String fromMethod(ClassAndMethod classAndMethod) {
return fromMethod(classAndMethod.declaringClass(), classAndMethod.methodName());
}
/** /**
* This method is used to generate a span name based on a method. Anonymous classes are named * This method is used to generate a span name based on a method. Anonymous classes are named
* based on their parent. * based on their parent.

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
package io.opentelemetry.javaagent.instrumentation.rmi.server; package io.opentelemetry.instrumentation.api.util;
import com.google.auto.value.AutoValue; import com.google.auto.value.AutoValue;

View File

@ -1,20 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.extannotations;
import com.google.auto.value.AutoValue;
@AutoValue
public abstract class ClassAndMethod {
public static ClassAndMethod create(Class<?> declaringClass, String methodName) {
return new AutoValue_ClassAndMethod(declaringClass, methodName);
}
public abstract Class<?> declaringClass();
public abstract String methodName();
}

View File

@ -6,6 +6,7 @@
package io.opentelemetry.javaagent.instrumentation.extannotations; package io.opentelemetry.javaagent.instrumentation.extannotations;
import io.opentelemetry.instrumentation.api.instrumenter.code.CodeAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.code.CodeAttributesExtractor;
import io.opentelemetry.instrumentation.api.util.ClassAndMethod;
import javax.annotation.Nullable; import javax.annotation.Nullable;
final class ExternalAnnotationAttributesExtractor final class ExternalAnnotationAttributesExtractor

View File

@ -18,6 +18,7 @@ import static net.bytebuddy.matcher.ElementMatchers.not;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope; import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.config.Config; import io.opentelemetry.instrumentation.api.config.Config;
import io.opentelemetry.instrumentation.api.util.ClassAndMethod;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge; import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;

View File

@ -8,6 +8,7 @@ package io.opentelemetry.javaagent.instrumentation.extannotations;
import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.instrumenter.code.CodeSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.code.CodeSpanNameExtractor;
import io.opentelemetry.instrumentation.api.util.ClassAndMethod;
public final class ExternalAnnotationSingletons { public final class ExternalAnnotationSingletons {

View File

@ -15,9 +15,9 @@ import static net.bytebuddy.matcher.ElementMatchers.namedOneOf;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope; import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.annotation.support.async.AsyncOperationEndSupport; import io.opentelemetry.instrumentation.api.annotation.support.async.AsyncOperationEndSupport;
import io.opentelemetry.instrumentation.api.util.ClassAndMethod;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import java.lang.reflect.Method;
import java.util.Set; import java.util.Set;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
@ -55,21 +55,25 @@ public class MethodInstrumentation implements TypeInstrumentation {
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter( public static void onEnter(
@Advice.Origin Method method, @Advice.Origin("#t") Class<?> declaringClass,
@Advice.Origin("#m") String methodName,
@Advice.Local("otelMethod") ClassAndMethod classAndMethod,
@Advice.Local("otelContext") Context context, @Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) { @Advice.Local("otelScope") Scope scope) {
Context parentContext = currentContext(); Context parentContext = currentContext();
if (!instrumenter().shouldStart(parentContext, method)) { classAndMethod = ClassAndMethod.create(declaringClass, methodName);
if (!instrumenter().shouldStart(parentContext, classAndMethod)) {
return; return;
} }
context = instrumenter().start(parentContext, method); context = instrumenter().start(parentContext, classAndMethod);
scope = context.makeCurrent(); scope = context.makeCurrent();
} }
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan( public static void stopSpan(
@Advice.Origin Method method, @Advice.Origin("#r") Class<?> returnType,
@Advice.Local("otelMethod") ClassAndMethod classAndMethod,
@Advice.Local("otelContext") Context context, @Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope, @Advice.Local("otelScope") Scope scope,
@Advice.Return(typing = Assigner.Typing.DYNAMIC, readOnly = false) Object returnValue, @Advice.Return(typing = Assigner.Typing.DYNAMIC, readOnly = false) Object returnValue,
@ -77,8 +81,8 @@ public class MethodInstrumentation implements TypeInstrumentation {
scope.close(); scope.close();
returnValue = returnValue =
AsyncOperationEndSupport.create(instrumenter(), Void.class, method.getReturnType()) AsyncOperationEndSupport.create(instrumenter(), Void.class, returnType)
.asyncEnd(context, method, returnValue, throwable); .asyncEnd(context, classAndMethod, returnValue, throwable);
} }
} }
} }

View File

@ -10,23 +10,23 @@ import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor; import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor;
import io.opentelemetry.instrumentation.api.tracer.SpanNames; import io.opentelemetry.instrumentation.api.tracer.SpanNames;
import java.lang.reflect.Method; import io.opentelemetry.instrumentation.api.util.ClassAndMethod;
public final class MethodSingletons { public final class MethodSingletons {
private static final String INSTRUMENTATION_NAME = "io.opentelemetry.methods"; private static final String INSTRUMENTATION_NAME = "io.opentelemetry.methods";
private static final Instrumenter<Method, Void> INSTRUMENTER; private static final Instrumenter<ClassAndMethod, Void> INSTRUMENTER;
static { static {
SpanNameExtractor<Method> spanName = SpanNames::fromMethod; SpanNameExtractor<ClassAndMethod> spanName = SpanNames::fromMethod;
INSTRUMENTER = INSTRUMENTER =
Instrumenter.<Method, Void>builder( Instrumenter.<ClassAndMethod, Void>builder(
GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, spanName) GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, spanName)
.newInstrumenter(SpanKindExtractor.alwaysInternal()); .newInstrumenter(SpanKindExtractor.alwaysInternal());
} }
public static Instrumenter<Method, Void> instrumenter() { public static Instrumenter<ClassAndMethod, Void> instrumenter() {
return INSTRUMENTER; return INSTRUMENTER;
} }

View File

@ -115,12 +115,17 @@ public class WithSpanInstrumentation implements TypeInstrumentation {
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter( public static void onEnter(
@Advice.Origin Method method, @Advice.Origin Method originMethod,
@Advice.Local("otelMethod") Method method,
@Advice.Local("otelOperationEndSupport") @Advice.Local("otelOperationEndSupport")
AsyncOperationEndSupport<Method, Object> operationEndSupport, AsyncOperationEndSupport<Method, Object> operationEndSupport,
@Advice.Local("otelContext") Context context, @Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) { @Advice.Local("otelScope") Scope scope) {
// Every usage of @Advice.Origin Method is replaced with a call to Class.getMethod, copy it
// to local variable so that there would be only one call to Class.getMethod.
method = originMethod;
Instrumenter<Method, Object> instrumenter = instrumenter(); Instrumenter<Method, Object> instrumenter = instrumenter();
Context current = Java8BytecodeBridge.currentContext(); Context current = Java8BytecodeBridge.currentContext();
@ -134,7 +139,7 @@ public class WithSpanInstrumentation implements TypeInstrumentation {
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan( public static void stopSpan(
@Advice.Origin Method method, @Advice.Local("otelMethod") Method method,
@Advice.Local("otelOperationEndSupport") @Advice.Local("otelOperationEndSupport")
AsyncOperationEndSupport<Method, Object> operationEndSupport, AsyncOperationEndSupport<Method, Object> operationEndSupport,
@Advice.Local("otelContext") Context context, @Advice.Local("otelContext") Context context,
@ -154,7 +159,8 @@ public class WithSpanInstrumentation implements TypeInstrumentation {
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter( public static void onEnter(
@Advice.Origin Method method, @Advice.Origin Method originMethod,
@Advice.Local("otelMethod") Method method,
@Advice.AllArguments(typing = Assigner.Typing.DYNAMIC) Object[] args, @Advice.AllArguments(typing = Assigner.Typing.DYNAMIC) Object[] args,
@Advice.Local("otelOperationEndSupport") @Advice.Local("otelOperationEndSupport")
AsyncOperationEndSupport<MethodRequest, Object> operationEndSupport, AsyncOperationEndSupport<MethodRequest, Object> operationEndSupport,
@ -162,6 +168,10 @@ public class WithSpanInstrumentation implements TypeInstrumentation {
@Advice.Local("otelContext") Context context, @Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) { @Advice.Local("otelScope") Scope scope) {
// Every usage of @Advice.Origin Method is replaced with a call to Class.getMethod, copy it
// to local variable so that there would be only one call to Class.getMethod.
method = originMethod;
Instrumenter<MethodRequest, Object> instrumenter = instrumenterWithAttributes(); Instrumenter<MethodRequest, Object> instrumenter = instrumenterWithAttributes();
Context current = Java8BytecodeBridge.currentContext(); Context current = Java8BytecodeBridge.currentContext();
request = new MethodRequest(method, args); request = new MethodRequest(method, args);
@ -176,7 +186,7 @@ public class WithSpanInstrumentation implements TypeInstrumentation {
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan( public static void stopSpan(
@Advice.Origin Method method, @Advice.Local("otelMethod") Method method,
@Advice.Local("otelOperationEndSupport") @Advice.Local("otelOperationEndSupport")
AsyncOperationEndSupport<MethodRequest, Object> operationEndSupport, AsyncOperationEndSupport<MethodRequest, Object> operationEndSupport,
@Advice.Local("otelRequest") MethodRequest request, @Advice.Local("otelRequest") MethodRequest request,

View File

@ -16,6 +16,7 @@ import static net.bytebuddy.matcher.ElementMatchers.not;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope; import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.util.ClassAndMethod;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.api.CallDepth; import io.opentelemetry.javaagent.instrumentation.api.CallDepth;

View File

@ -6,6 +6,7 @@
package io.opentelemetry.javaagent.instrumentation.rmi.server; package io.opentelemetry.javaagent.instrumentation.rmi.server;
import io.opentelemetry.instrumentation.api.instrumenter.rpc.RpcAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.rpc.RpcAttributesExtractor;
import io.opentelemetry.instrumentation.api.util.ClassAndMethod;
final class RmiServerAttributesExtractor extends RpcAttributesExtractor<ClassAndMethod, Void> { final class RmiServerAttributesExtractor extends RpcAttributesExtractor<ClassAndMethod, Void> {

View File

@ -9,6 +9,7 @@ import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor; import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.rpc.RpcSpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.rpc.RpcSpanNameExtractor;
import io.opentelemetry.instrumentation.api.util.ClassAndMethod;
public final class RmiServerSingletons { public final class RmiServerSingletons {

View File

@ -9,11 +9,11 @@ import static io.opentelemetry.javaagent.instrumentation.servlet.v5_0.response.R
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope; import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.util.ClassAndMethod;
import io.opentelemetry.javaagent.instrumentation.api.CallDepth; import io.opentelemetry.javaagent.instrumentation.api.CallDepth;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge; import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import io.opentelemetry.javaagent.instrumentation.servlet.common.response.HttpServletResponseAdviceHelper; import io.opentelemetry.javaagent.instrumentation.servlet.common.response.HttpServletResponseAdviceHelper;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
@SuppressWarnings("unused") @SuppressWarnings("unused")
@ -21,7 +21,10 @@ public class ResponseSendAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static void start( public static void start(
@Advice.Origin Method method, @Advice.This Object response,
@Advice.Origin("#t") Class<?> declaringClass,
@Advice.Origin("#m") String methodName,
@Advice.Local("otelMethod") ClassAndMethod classAndMethod,
@Advice.Local("otelContext") Context context, @Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope, @Advice.Local("otelScope") Scope scope,
@Advice.Local("otelCallDepth") CallDepth callDepth) { @Advice.Local("otelCallDepth") CallDepth callDepth) {
@ -33,8 +36,9 @@ public class ResponseSendAdvice {
Context parentContext = Java8BytecodeBridge.currentContext(); Context parentContext = Java8BytecodeBridge.currentContext();
// Don't want to generate a new top-level span // Don't want to generate a new top-level span
if (Java8BytecodeBridge.spanFromContext(parentContext).getSpanContext().isValid()) { if (Java8BytecodeBridge.spanFromContext(parentContext).getSpanContext().isValid()) {
if (instrumenter().shouldStart(parentContext, method)) { classAndMethod = ClassAndMethod.create(declaringClass, methodName);
context = instrumenter().start(parentContext, method); if (instrumenter().shouldStart(parentContext, classAndMethod)) {
context = instrumenter().start(parentContext, classAndMethod);
scope = context.makeCurrent(); scope = context.makeCurrent();
} }
} }
@ -42,14 +46,15 @@ public class ResponseSendAdvice {
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan( public static void stopSpan(
@Advice.Origin Method method,
@Advice.Thrown Throwable throwable, @Advice.Thrown Throwable throwable,
@Advice.Local("otelMethod") ClassAndMethod classAndMethod,
@Advice.Local("otelContext") Context context, @Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope, @Advice.Local("otelScope") Scope scope,
@Advice.Local("otelCallDepth") CallDepth callDepth) { @Advice.Local("otelCallDepth") CallDepth callDepth) {
if (callDepth.decrementAndGet() > 0) { if (callDepth.decrementAndGet() > 0) {
return; return;
} }
HttpServletResponseAdviceHelper.stopSpan(instrumenter(), throwable, context, scope, method); HttpServletResponseAdviceHelper.stopSpan(
instrumenter(), throwable, context, scope, classAndMethod);
} }
} }

View File

@ -8,20 +8,20 @@ package io.opentelemetry.javaagent.instrumentation.servlet.v5_0.response;
import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.tracer.SpanNames; import io.opentelemetry.instrumentation.api.tracer.SpanNames;
import java.lang.reflect.Method; import io.opentelemetry.instrumentation.api.util.ClassAndMethod;
public class ResponseSingletons { public class ResponseSingletons {
private static final Instrumenter<Method, Void> INSTRUMENTER; private static final Instrumenter<ClassAndMethod, Void> INSTRUMENTER;
static { static {
INSTRUMENTER = INSTRUMENTER =
Instrumenter.<Method, Void>builder( Instrumenter.<ClassAndMethod, Void>builder(
GlobalOpenTelemetry.get(), "io.opentelemetry.servlet-5.0", SpanNames::fromMethod) GlobalOpenTelemetry.get(), "io.opentelemetry.servlet-5.0", SpanNames::fromMethod)
.newInstrumenter(); .newInstrumenter();
} }
public static Instrumenter<Method, Void> instrumenter() { public static Instrumenter<ClassAndMethod, Void> instrumenter() {
return INSTRUMENTER; return INSTRUMENTER;
} }
} }

View File

@ -8,15 +8,15 @@ package io.opentelemetry.javaagent.instrumentation.servlet.common.response;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope; import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import java.lang.reflect.Method; import io.opentelemetry.instrumentation.api.util.ClassAndMethod;
public class HttpServletResponseAdviceHelper { public class HttpServletResponseAdviceHelper {
public static void stopSpan( public static void stopSpan(
Instrumenter<Method, Void> instrumenter, Instrumenter<ClassAndMethod, Void> instrumenter,
Throwable throwable, Throwable throwable,
Context context, Context context,
Scope scope, Scope scope,
Method request) { ClassAndMethod request) {
if (scope != null) { if (scope != null) {
scope.close(); scope.close();

View File

@ -9,10 +9,10 @@ import static io.opentelemetry.javaagent.instrumentation.servlet.javax.response.
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope; import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.util.ClassAndMethod;
import io.opentelemetry.javaagent.instrumentation.api.CallDepth; import io.opentelemetry.javaagent.instrumentation.api.CallDepth;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge; import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import io.opentelemetry.javaagent.instrumentation.servlet.common.response.HttpServletResponseAdviceHelper; import io.opentelemetry.javaagent.instrumentation.servlet.common.response.HttpServletResponseAdviceHelper;
import java.lang.reflect.Method;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
@ -21,7 +21,9 @@ public class ResponseSendAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)
public static void start( public static void start(
@Advice.Origin Method method, @Advice.Origin("#t") Class<?> declaringClass,
@Advice.Origin("#m") String methodName,
@Advice.Local("otelMethod") ClassAndMethod classAndMethod,
@Advice.Local("otelContext") Context context, @Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope, @Advice.Local("otelScope") Scope scope,
@Advice.Local("otelCallDepth") CallDepth callDepth) { @Advice.Local("otelCallDepth") CallDepth callDepth) {
@ -32,8 +34,9 @@ public class ResponseSendAdvice {
Context parentContext = Java8BytecodeBridge.currentContext(); Context parentContext = Java8BytecodeBridge.currentContext();
// Don't want to generate a new top-level span // Don't want to generate a new top-level span
if (Java8BytecodeBridge.spanFromContext(parentContext).getSpanContext().isValid()) { if (Java8BytecodeBridge.spanFromContext(parentContext).getSpanContext().isValid()) {
if (instrumenter().shouldStart(parentContext, method)) { classAndMethod = ClassAndMethod.create(declaringClass, methodName);
context = instrumenter().start(parentContext, method); if (instrumenter().shouldStart(parentContext, classAndMethod)) {
context = instrumenter().start(parentContext, classAndMethod);
scope = context.makeCurrent(); scope = context.makeCurrent();
} }
} }
@ -41,14 +44,15 @@ public class ResponseSendAdvice {
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan( public static void stopSpan(
@Advice.Origin Method method,
@Advice.Thrown Throwable throwable, @Advice.Thrown Throwable throwable,
@Advice.Local("otelMethod") ClassAndMethod classAndMethod,
@Advice.Local("otelContext") Context context, @Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope, @Advice.Local("otelScope") Scope scope,
@Advice.Local("otelCallDepth") CallDepth callDepth) { @Advice.Local("otelCallDepth") CallDepth callDepth) {
if (callDepth.decrementAndGet() > 0) { if (callDepth.decrementAndGet() > 0) {
return; return;
} }
HttpServletResponseAdviceHelper.stopSpan(instrumenter(), throwable, context, scope, method); HttpServletResponseAdviceHelper.stopSpan(
instrumenter(), throwable, context, scope, classAndMethod);
} }
} }

View File

@ -8,22 +8,22 @@ package io.opentelemetry.javaagent.instrumentation.servlet.javax.response;
import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.tracer.SpanNames; import io.opentelemetry.instrumentation.api.tracer.SpanNames;
import java.lang.reflect.Method; import io.opentelemetry.instrumentation.api.util.ClassAndMethod;
public class ResponseSingletons { public class ResponseSingletons {
private static final Instrumenter<Method, Void> INSTRUMENTER; private static final Instrumenter<ClassAndMethod, Void> INSTRUMENTER;
static { static {
INSTRUMENTER = INSTRUMENTER =
Instrumenter.<Method, Void>builder( Instrumenter.<ClassAndMethod, Void>builder(
GlobalOpenTelemetry.get(), GlobalOpenTelemetry.get(),
"io.opentelemetry.servlet-javax-common", "io.opentelemetry.servlet-javax-common",
SpanNames::fromMethod) SpanNames::fromMethod)
.newInstrumenter(); .newInstrumenter();
} }
public static Instrumenter<Method, Void> instrumenter() { public static Instrumenter<ClassAndMethod, Void> instrumenter() {
return INSTRUMENTER; return INSTRUMENTER;
} }
} }