Update jaxrs-common to Instrumenter API (#3796)
This commit is contained in:
parent
19711ca76b
commit
7e007e8f32
|
@ -19,4 +19,7 @@ dependencies {
|
||||||
compileOnly(project(":instrumentation:jaxrs:bootstrap"))
|
compileOnly(project(":instrumentation:jaxrs:bootstrap"))
|
||||||
|
|
||||||
compileOnly("javax.ws.rs:javax.ws.rs-api:2.0")
|
compileOnly("javax.ws.rs:javax.ws.rs-api:2.0")
|
||||||
|
|
||||||
|
compileOnly("com.google.auto.value:auto-value-annotations")
|
||||||
|
annotationProcessor("com.google.auto.value:auto-value")
|
||||||
}
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0;
|
||||||
|
|
||||||
|
import com.google.auto.value.AutoValue;
|
||||||
|
import io.opentelemetry.context.Context;
|
||||||
|
|
||||||
|
@AutoValue
|
||||||
|
public abstract class AsyncResponseData {
|
||||||
|
|
||||||
|
public static AsyncResponseData create(Context context, HandlerData handlerData) {
|
||||||
|
return new AutoValue_AsyncResponseData(context, handlerData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract Context getContext();
|
||||||
|
|
||||||
|
public abstract HandlerData getHandlerData();
|
||||||
|
}
|
|
@ -5,25 +5,23 @@
|
||||||
|
|
||||||
package io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0;
|
package io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0;
|
||||||
|
|
||||||
import static io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0.JaxRsAnnotationsTracer.tracer;
|
import static io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0.JaxrsSingletons.instrumenter;
|
||||||
|
|
||||||
import io.opentelemetry.context.Context;
|
import io.opentelemetry.context.Context;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
public class CompletionStageFinishCallback<T> implements BiFunction<T, Throwable, T> {
|
public class CompletionStageFinishCallback<T> implements BiFunction<T, Throwable, T> {
|
||||||
private final Context context;
|
private final Context context;
|
||||||
|
private final HandlerData handlerData;
|
||||||
|
|
||||||
public CompletionStageFinishCallback(Context context) {
|
public CompletionStageFinishCallback(Context context, HandlerData handlerData) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
|
this.handlerData = handlerData;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public T apply(T result, Throwable throwable) {
|
public T apply(T result, Throwable throwable) {
|
||||||
if (throwable == null) {
|
instrumenter().end(context, handlerData, null, throwable);
|
||||||
tracer().end(context);
|
|
||||||
} else {
|
|
||||||
tracer().endExceptionally(context, throwable);
|
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@ public class ContainerRequestFilterInstrumentation implements TypeInstrumentatio
|
||||||
public static void setFilterClass(
|
public static void setFilterClass(
|
||||||
@Advice.This ContainerRequestFilter filter,
|
@Advice.This ContainerRequestFilter filter,
|
||||||
@Advice.Argument(0) ContainerRequestContext context) {
|
@Advice.Argument(0) ContainerRequestContext context) {
|
||||||
context.setProperty(JaxRsAnnotationsTracer.ABORT_FILTER_CLASS, filter.getClass());
|
context.setProperty(JaxrsSingletons.ABORT_FILTER_CLASS, filter.getClass());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,10 +5,12 @@
|
||||||
|
|
||||||
package io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0;
|
package io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0;
|
||||||
|
|
||||||
import static io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0.JaxRsAnnotationsTracer.tracer;
|
import static io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0.JaxrsSingletons.instrumenter;
|
||||||
|
|
||||||
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.servlet.ServerSpanNaming;
|
||||||
|
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import javax.ws.rs.container.ContainerRequestContext;
|
import javax.ws.rs.container.ContainerRequestContext;
|
||||||
import net.bytebuddy.asm.Advice;
|
import net.bytebuddy.asm.Advice;
|
||||||
|
@ -35,11 +37,15 @@ public class DefaultRequestContextInstrumentation extends AbstractRequestContext
|
||||||
@Advice.OnMethodEnter(suppress = Throwable.class)
|
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||||
public static void createGenericSpan(
|
public static void createGenericSpan(
|
||||||
@Advice.This ContainerRequestContext requestContext,
|
@Advice.This ContainerRequestContext requestContext,
|
||||||
|
@Local("otelHandlerData") HandlerData handlerData,
|
||||||
@Local("otelContext") Context context,
|
@Local("otelContext") Context context,
|
||||||
@Local("otelScope") Scope scope) {
|
@Local("otelScope") Scope scope) {
|
||||||
if (requestContext.getProperty(JaxRsAnnotationsTracer.ABORT_HANDLED) == null) {
|
if (requestContext.getProperty(JaxrsSingletons.ABORT_HANDLED) != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Class<?> filterClass =
|
Class<?> filterClass =
|
||||||
(Class<?>) requestContext.getProperty(JaxRsAnnotationsTracer.ABORT_FILTER_CLASS);
|
(Class<?>) requestContext.getProperty(JaxrsSingletons.ABORT_FILTER_CLASS);
|
||||||
Method method = null;
|
Method method = null;
|
||||||
try {
|
try {
|
||||||
method = filterClass.getMethod("filter", ContainerRequestContext.class);
|
method = filterClass.getMethod("filter", ContainerRequestContext.class);
|
||||||
|
@ -48,17 +54,38 @@ public class DefaultRequestContextInstrumentation extends AbstractRequestContext
|
||||||
// can only be aborted inside the filter method
|
// can only be aborted inside the filter method
|
||||||
}
|
}
|
||||||
|
|
||||||
context = tracer().startSpan(filterClass, method);
|
if (filterClass == null || method == null) {
|
||||||
scope = context.makeCurrent();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Context parentContext = Java8BytecodeBridge.currentContext();
|
||||||
|
handlerData = new HandlerData(filterClass, 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();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||||
public static void stopSpan(
|
public static void stopSpan(
|
||||||
|
@Local("otelHandlerData") HandlerData handlerData,
|
||||||
@Local("otelContext") Context context,
|
@Local("otelContext") Context context,
|
||||||
@Local("otelScope") Scope scope,
|
@Local("otelScope") Scope scope,
|
||||||
@Advice.Thrown Throwable throwable) {
|
@Advice.Thrown Throwable throwable) {
|
||||||
RequestContextHelper.closeSpanAndScope(context, scope, throwable);
|
if (scope == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.close();
|
||||||
|
instrumenter().end(context, handlerData, null, throwable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,40 +5,17 @@
|
||||||
|
|
||||||
package io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0;
|
package io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0;
|
||||||
|
|
||||||
import static io.opentelemetry.api.trace.SpanKind.INTERNAL;
|
|
||||||
|
|
||||||
import io.opentelemetry.api.trace.Span;
|
|
||||||
import io.opentelemetry.api.trace.SpanBuilder;
|
|
||||||
import io.opentelemetry.context.Context;
|
|
||||||
import io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming;
|
|
||||||
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.javaagent.bootstrap.jaxrs.ClassHierarchyIterable;
|
||||||
import io.opentelemetry.javaagent.bootstrap.jaxrs.JaxrsContextPath;
|
|
||||||
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
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;
|
||||||
import java.util.function.Supplier;
|
|
||||||
import javax.ws.rs.HttpMethod;
|
import javax.ws.rs.HttpMethod;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
|
|
||||||
public class JaxRsAnnotationsTracer extends BaseTracer {
|
public class HandlerData {
|
||||||
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 JaxRsAnnotationsTracer TRACER = new JaxRsAnnotationsTracer();
|
private static final ClassValue<Map<Method, String>> serverSpanNames =
|
||||||
|
|
||||||
public static JaxRsAnnotationsTracer tracer() {
|
|
||||||
return TRACER;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final ClassValue<Map<Method, String>> spanNames =
|
|
||||||
new ClassValue<Map<Method, String>>() {
|
new ClassValue<Map<Method, String>>() {
|
||||||
@Override
|
@Override
|
||||||
protected Map<Method, String> computeValue(Class<?> type) {
|
protected Map<Method, String> computeValue(Class<?> type) {
|
||||||
|
@ -46,61 +23,20 @@ public class JaxRsAnnotationsTracer extends BaseTracer {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public Context startSpan(Class<?> target, Method method) {
|
private final Class<?> target;
|
||||||
return startSpan(Context.current(), target, method);
|
private final Method method;
|
||||||
|
|
||||||
|
public HandlerData(Class<?> target, Method method) {
|
||||||
|
this.target = target;
|
||||||
|
this.method = method;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Context startSpan(Context parentContext, Class<?> target, Method method) {
|
public Class<?> codeClass() {
|
||||||
// We create span and immediately update its name
|
return target;
|
||||||
// We do that in order to reuse logic inside updateSpanNames method, which is used externally as
|
|
||||||
// well.
|
|
||||||
SpanBuilder spanBuilder = spanBuilder(parentContext, "jax-rs.request", INTERNAL);
|
|
||||||
setCodeAttributes(spanBuilder, target, method);
|
|
||||||
Span span = spanBuilder.startSpan();
|
|
||||||
updateSpanNames(
|
|
||||||
parentContext, span, ServerSpan.fromContextOrNull(parentContext), target, method);
|
|
||||||
return parentContext.with(span);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateSpanNames(
|
public String methodName() {
|
||||||
Context context, Span span, Span serverSpan, Class<?> target, Method method) {
|
return method.getName();
|
||||||
Supplier<String> spanNameSupplier = getPathSpanNameSupplier(context, target, method);
|
|
||||||
if (serverSpan == null) {
|
|
||||||
updateSpanName(span, spanNameSupplier.get());
|
|
||||||
} else {
|
|
||||||
ServerSpanNaming.updateServerSpanName(
|
|
||||||
context, ServerSpanNaming.Source.CONTROLLER, spanNameSupplier);
|
|
||||||
updateSpanName(span, SpanNames.fromMethod(target, method));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void updateSpanName(Span span, String spanName) {
|
|
||||||
if (!spanName.isEmpty()) {
|
|
||||||
span.updateName(spanName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Supplier<String> getPathSpanNameSupplier(
|
|
||||||
Context context, Class<?> target, Method method) {
|
|
||||||
return () -> {
|
|
||||||
String pathBasedSpanName = getPathSpanName(target, method);
|
|
||||||
// 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;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -109,8 +45,8 @@ public class JaxRsAnnotationsTracer extends BaseTracer {
|
||||||
*
|
*
|
||||||
* @return The result can be an empty string but will never be {@code null}.
|
* @return The result can be an empty string but will never be {@code null}.
|
||||||
*/
|
*/
|
||||||
private String getPathSpanName(Class<?> target, Method method) {
|
String getServerSpanName() {
|
||||||
Map<Method, String> classMap = spanNames.get(target);
|
Map<Method, String> classMap = serverSpanNames.get(target);
|
||||||
String spanName = classMap.get(method);
|
String spanName = classMap.get(method);
|
||||||
if (spanName == null) {
|
if (spanName == null) {
|
||||||
String httpMethod = null;
|
String httpMethod = null;
|
||||||
|
@ -222,9 +158,4 @@ public class JaxRsAnnotationsTracer extends BaseTracer {
|
||||||
|
|
||||||
return spanNameBuilder.toString().trim();
|
return spanNameBuilder.toString().trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getInstrumentationName() {
|
|
||||||
return "io.opentelemetry.jaxrs-2.0-common";
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -8,7 +8,7 @@ package io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0;
|
||||||
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
|
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.hasSuperMethod;
|
||||||
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasSuperType;
|
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasSuperType;
|
||||||
import static io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0.JaxRsAnnotationsTracer.tracer;
|
import static io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0.JaxrsSingletons.instrumenter;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.declaresMethod;
|
import static net.bytebuddy.matcher.ElementMatchers.declaresMethod;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith;
|
import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
|
@ -17,11 +17,13 @@ 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.servlet.ServerSpanNaming;
|
||||||
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;
|
||||||
import io.opentelemetry.javaagent.instrumentation.api.ContextStore;
|
import io.opentelemetry.javaagent.instrumentation.api.ContextStore;
|
||||||
import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext;
|
import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext;
|
||||||
|
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.concurrent.CompletionStage;
|
import java.util.concurrent.CompletionStage;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
|
@ -31,7 +33,7 @@ import net.bytebuddy.description.type.TypeDescription;
|
||||||
import net.bytebuddy.implementation.bytecode.assign.Assigner.Typing;
|
import net.bytebuddy.implementation.bytecode.assign.Assigner.Typing;
|
||||||
import net.bytebuddy.matcher.ElementMatcher;
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
|
||||||
public class JaxRsAnnotationsInstrumentation implements TypeInstrumentation {
|
public class JaxrsAnnotationsInstrumentation implements TypeInstrumentation {
|
||||||
@Override
|
@Override
|
||||||
public ElementMatcher<ClassLoader> classLoaderOptimization() {
|
public ElementMatcher<ClassLoader> classLoaderOptimization() {
|
||||||
return hasClassesNamed("javax.ws.rs.Path");
|
return hasClassesNamed("javax.ws.rs.Path");
|
||||||
|
@ -60,7 +62,7 @@ public class JaxRsAnnotationsInstrumentation implements TypeInstrumentation {
|
||||||
"javax.ws.rs.PATCH",
|
"javax.ws.rs.PATCH",
|
||||||
"javax.ws.rs.POST",
|
"javax.ws.rs.POST",
|
||||||
"javax.ws.rs.PUT")))),
|
"javax.ws.rs.PUT")))),
|
||||||
JaxRsAnnotationsInstrumentation.class.getName() + "$JaxRsAnnotationsAdvice");
|
JaxrsAnnotationsInstrumentation.class.getName() + "$JaxRsAnnotationsAdvice");
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
|
@ -72,6 +74,7 @@ public class JaxRsAnnotationsInstrumentation implements TypeInstrumentation {
|
||||||
@Advice.Origin Method method,
|
@Advice.Origin Method method,
|
||||||
@Advice.AllArguments Object[] args,
|
@Advice.AllArguments Object[] args,
|
||||||
@Advice.Local("otelCallDepth") CallDepth callDepth,
|
@Advice.Local("otelCallDepth") CallDepth callDepth,
|
||||||
|
@Advice.Local("otelHandlerData") HandlerData handlerData,
|
||||||
@Advice.Local("otelContext") Context context,
|
@Advice.Local("otelContext") Context context,
|
||||||
@Advice.Local("otelScope") Scope scope,
|
@Advice.Local("otelScope") Scope scope,
|
||||||
@Advice.Local("otelAsyncResponse") AsyncResponse asyncResponse) {
|
@Advice.Local("otelAsyncResponse") AsyncResponse asyncResponse) {
|
||||||
|
@ -80,11 +83,11 @@ public class JaxRsAnnotationsInstrumentation implements TypeInstrumentation {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ContextStore<AsyncResponse, Context> contextStore = null;
|
ContextStore<AsyncResponse, AsyncResponseData> contextStore = null;
|
||||||
for (Object arg : args) {
|
for (Object arg : args) {
|
||||||
if (arg instanceof AsyncResponse) {
|
if (arg instanceof AsyncResponse) {
|
||||||
asyncResponse = (AsyncResponse) arg;
|
asyncResponse = (AsyncResponse) arg;
|
||||||
contextStore = InstrumentationContext.get(AsyncResponse.class, Context.class);
|
contextStore = InstrumentationContext.get(AsyncResponse.class, AsyncResponseData.class);
|
||||||
if (contextStore.get(asyncResponse) != null) {
|
if (contextStore.get(asyncResponse) != null) {
|
||||||
/*
|
/*
|
||||||
* We are probably in a recursive call and don't want to start a new span because it
|
* We are probably in a recursive call and don't want to start a new span because it
|
||||||
|
@ -98,13 +101,24 @@ public class JaxRsAnnotationsInstrumentation implements TypeInstrumentation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
context = tracer().startSpan(target.getClass(), method);
|
Context parentContext = Java8BytecodeBridge.currentContext();
|
||||||
|
handlerData = new HandlerData(target.getClass(), method);
|
||||||
|
|
||||||
if (contextStore != null && asyncResponse != null) {
|
ServerSpanNaming.updateServerSpanName(
|
||||||
contextStore.put(asyncResponse, context);
|
parentContext,
|
||||||
|
ServerSpanNaming.Source.CONTROLLER,
|
||||||
|
JaxrsServerSpanNaming.getServerSpanNameSupplier(parentContext, handlerData));
|
||||||
|
|
||||||
|
if (!instrumenter().shouldStart(parentContext, handlerData)) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context = instrumenter().start(parentContext, handlerData);
|
||||||
scope = context.makeCurrent();
|
scope = context.makeCurrent();
|
||||||
|
|
||||||
|
if (contextStore != null && asyncResponse != null) {
|
||||||
|
contextStore.put(asyncResponse, AsyncResponseData.create(context, handlerData));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||||
|
@ -112,6 +126,7 @@ public class JaxRsAnnotationsInstrumentation implements TypeInstrumentation {
|
||||||
@Advice.Return(readOnly = false, typing = Typing.DYNAMIC) Object returnValue,
|
@Advice.Return(readOnly = false, typing = Typing.DYNAMIC) Object returnValue,
|
||||||
@Advice.Thrown Throwable throwable,
|
@Advice.Thrown Throwable throwable,
|
||||||
@Advice.Local("otelCallDepth") CallDepth callDepth,
|
@Advice.Local("otelCallDepth") CallDepth callDepth,
|
||||||
|
@Advice.Local("otelHandlerData") HandlerData handlerData,
|
||||||
@Advice.Local("otelContext") Context context,
|
@Advice.Local("otelContext") Context context,
|
||||||
@Advice.Local("otelScope") Scope scope,
|
@Advice.Local("otelScope") Scope scope,
|
||||||
@Advice.Local("otelAsyncResponse") AsyncResponse asyncResponse) {
|
@Advice.Local("otelAsyncResponse") AsyncResponse asyncResponse) {
|
||||||
|
@ -119,13 +134,14 @@ public class JaxRsAnnotationsInstrumentation implements TypeInstrumentation {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context == null || scope == null) {
|
if (scope == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (throwable != null) {
|
|
||||||
tracer().endExceptionally(context, throwable);
|
|
||||||
scope.close();
|
scope.close();
|
||||||
|
|
||||||
|
if (throwable != null) {
|
||||||
|
instrumenter().end(context, handlerData, null, throwable);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,18 +150,18 @@ public class JaxRsAnnotationsInstrumentation implements TypeInstrumentation {
|
||||||
|
|
||||||
if (asyncResponse != null && !asyncResponse.isSuspended()) {
|
if (asyncResponse != null && !asyncResponse.isSuspended()) {
|
||||||
// Clear span from the asyncResponse. Logically this should never happen. Added to be safe.
|
// Clear span from the asyncResponse. Logically this should never happen. Added to be safe.
|
||||||
InstrumentationContext.get(AsyncResponse.class, Context.class).put(asyncResponse, null);
|
InstrumentationContext.get(AsyncResponse.class, AsyncResponseData.class)
|
||||||
|
.put(asyncResponse, null);
|
||||||
}
|
}
|
||||||
if (asyncReturnValue != null) {
|
if (asyncReturnValue != null) {
|
||||||
// span finished by CompletionStageFinishCallback
|
// span finished by CompletionStageFinishCallback
|
||||||
asyncReturnValue = asyncReturnValue.handle(new CompletionStageFinishCallback<>(context));
|
asyncReturnValue =
|
||||||
|
asyncReturnValue.handle(new CompletionStageFinishCallback<>(context, handlerData));
|
||||||
}
|
}
|
||||||
if ((asyncResponse == null || !asyncResponse.isSuspended()) && asyncReturnValue == null) {
|
if ((asyncResponse == null || !asyncResponse.isSuspended()) && asyncReturnValue == null) {
|
||||||
tracer().end(context);
|
instrumenter().end(context, handlerData, null, null);
|
||||||
}
|
}
|
||||||
// else span finished by AsyncResponseAdvice
|
// else span finished by AsyncResponse*Advice
|
||||||
|
|
||||||
scope.close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -7,7 +7,7 @@ package io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0;
|
||||||
|
|
||||||
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
|
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
|
||||||
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
|
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
|
||||||
import static io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0.JaxRsAnnotationsTracer.tracer;
|
import static io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0.JaxrsSingletons.instrumenter;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
|
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||||
|
@ -23,7 +23,7 @@ import net.bytebuddy.asm.Advice;
|
||||||
import net.bytebuddy.description.type.TypeDescription;
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
import net.bytebuddy.matcher.ElementMatcher;
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
|
||||||
public class JaxRsAsyncResponseInstrumentation implements TypeInstrumentation {
|
public class JaxrsAsyncResponseInstrumentation implements TypeInstrumentation {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ElementMatcher<ClassLoader> classLoaderOptimization() {
|
public ElementMatcher<ClassLoader> classLoaderOptimization() {
|
||||||
|
@ -39,13 +39,13 @@ public class JaxRsAsyncResponseInstrumentation implements TypeInstrumentation {
|
||||||
public void transform(TypeTransformer transformer) {
|
public void transform(TypeTransformer transformer) {
|
||||||
transformer.applyAdviceToMethod(
|
transformer.applyAdviceToMethod(
|
||||||
named("resume").and(takesArgument(0, Object.class)).and(isPublic()),
|
named("resume").and(takesArgument(0, Object.class)).and(isPublic()),
|
||||||
JaxRsAsyncResponseInstrumentation.class.getName() + "$AsyncResponseAdvice");
|
JaxrsAsyncResponseInstrumentation.class.getName() + "$AsyncResponseAdvice");
|
||||||
transformer.applyAdviceToMethod(
|
transformer.applyAdviceToMethod(
|
||||||
named("resume").and(takesArgument(0, Throwable.class)).and(isPublic()),
|
named("resume").and(takesArgument(0, Throwable.class)).and(isPublic()),
|
||||||
JaxRsAsyncResponseInstrumentation.class.getName() + "$AsyncResponseThrowableAdvice");
|
JaxrsAsyncResponseInstrumentation.class.getName() + "$AsyncResponseThrowableAdvice");
|
||||||
transformer.applyAdviceToMethod(
|
transformer.applyAdviceToMethod(
|
||||||
named("cancel"),
|
named("cancel"),
|
||||||
JaxRsAsyncResponseInstrumentation.class.getName() + "$AsyncResponseCancelAdvice");
|
JaxrsAsyncResponseInstrumentation.class.getName() + "$AsyncResponseCancelAdvice");
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
|
@ -54,13 +54,13 @@ public class JaxRsAsyncResponseInstrumentation implements TypeInstrumentation {
|
||||||
@Advice.OnMethodExit(suppress = Throwable.class)
|
@Advice.OnMethodExit(suppress = Throwable.class)
|
||||||
public static void stopSpan(@Advice.This AsyncResponse asyncResponse) {
|
public static void stopSpan(@Advice.This AsyncResponse asyncResponse) {
|
||||||
|
|
||||||
ContextStore<AsyncResponse, Context> contextStore =
|
ContextStore<AsyncResponse, AsyncResponseData> contextStore =
|
||||||
InstrumentationContext.get(AsyncResponse.class, Context.class);
|
InstrumentationContext.get(AsyncResponse.class, AsyncResponseData.class);
|
||||||
|
|
||||||
Context context = contextStore.get(asyncResponse);
|
AsyncResponseData data = contextStore.get(asyncResponse);
|
||||||
if (context != null) {
|
if (data != null) {
|
||||||
contextStore.put(asyncResponse, null);
|
contextStore.put(asyncResponse, null);
|
||||||
tracer().end(context);
|
instrumenter().end(data.getContext(), data.getHandlerData(), null, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,13 +72,13 @@ public class JaxRsAsyncResponseInstrumentation implements TypeInstrumentation {
|
||||||
public static void stopSpan(
|
public static void stopSpan(
|
||||||
@Advice.This AsyncResponse asyncResponse, @Advice.Argument(0) Throwable throwable) {
|
@Advice.This AsyncResponse asyncResponse, @Advice.Argument(0) Throwable throwable) {
|
||||||
|
|
||||||
ContextStore<AsyncResponse, Context> contextStore =
|
ContextStore<AsyncResponse, AsyncResponseData> contextStore =
|
||||||
InstrumentationContext.get(AsyncResponse.class, Context.class);
|
InstrumentationContext.get(AsyncResponse.class, AsyncResponseData.class);
|
||||||
|
|
||||||
Context context = contextStore.get(asyncResponse);
|
AsyncResponseData data = contextStore.get(asyncResponse);
|
||||||
if (context != null) {
|
if (data != null) {
|
||||||
contextStore.put(asyncResponse, null);
|
contextStore.put(asyncResponse, null);
|
||||||
tracer().endExceptionally(context, throwable);
|
instrumenter().end(data.getContext(), data.getHandlerData(), null, throwable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,16 +89,17 @@ public class JaxRsAsyncResponseInstrumentation implements TypeInstrumentation {
|
||||||
@Advice.OnMethodExit(suppress = Throwable.class)
|
@Advice.OnMethodExit(suppress = Throwable.class)
|
||||||
public static void stopSpan(@Advice.This AsyncResponse asyncResponse) {
|
public static void stopSpan(@Advice.This AsyncResponse asyncResponse) {
|
||||||
|
|
||||||
ContextStore<AsyncResponse, Context> contextStore =
|
ContextStore<AsyncResponse, AsyncResponseData> contextStore =
|
||||||
InstrumentationContext.get(AsyncResponse.class, Context.class);
|
InstrumentationContext.get(AsyncResponse.class, AsyncResponseData.class);
|
||||||
|
|
||||||
Context context = contextStore.get(asyncResponse);
|
AsyncResponseData data = contextStore.get(asyncResponse);
|
||||||
if (context != null) {
|
if (data != null) {
|
||||||
contextStore.put(asyncResponse, null);
|
contextStore.put(asyncResponse, null);
|
||||||
|
Context context = data.getContext();
|
||||||
if (JaxrsConfig.CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES) {
|
if (JaxrsConfig.CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES) {
|
||||||
Java8BytecodeBridge.spanFromContext(context).setAttribute("jaxrs.canceled", true);
|
Java8BytecodeBridge.spanFromContext(context).setAttribute("jaxrs.canceled", true);
|
||||||
}
|
}
|
||||||
tracer().end(context);
|
instrumenter().end(context, data.getHandlerData(), null, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.jaxrs.v2_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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,8 +15,8 @@ import java.util.List;
|
||||||
import net.bytebuddy.matcher.ElementMatcher;
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
|
||||||
@AutoService(InstrumentationModule.class)
|
@AutoService(InstrumentationModule.class)
|
||||||
public class JaxRsInstrumentationModule extends InstrumentationModule {
|
public class JaxrsInstrumentationModule extends InstrumentationModule {
|
||||||
public JaxRsInstrumentationModule() {
|
public JaxrsInstrumentationModule() {
|
||||||
super("jaxrs", "jaxrs-2.0");
|
super("jaxrs", "jaxrs-2.0");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ public class JaxRsInstrumentationModule extends InstrumentationModule {
|
||||||
return asList(
|
return asList(
|
||||||
new ContainerRequestFilterInstrumentation(),
|
new ContainerRequestFilterInstrumentation(),
|
||||||
new DefaultRequestContextInstrumentation(),
|
new DefaultRequestContextInstrumentation(),
|
||||||
new JaxRsAnnotationsInstrumentation(),
|
new JaxrsAnnotationsInstrumentation(),
|
||||||
new JaxRsAsyncResponseInstrumentation());
|
new JaxrsAsyncResponseInstrumentation());
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,8 +5,8 @@
|
||||||
|
|
||||||
package io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0;
|
package io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0;
|
||||||
|
|
||||||
public final class JaxRsPathUtil {
|
public final class JaxrsPathUtil {
|
||||||
private JaxRsPathUtil() {}
|
private JaxrsPathUtil() {}
|
||||||
|
|
||||||
public static String normalizePath(String path) {
|
public static String normalizePath(String path) {
|
||||||
// ensure that non-empty path starts with /
|
// ensure that non-empty path starts with /
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.jaxrs.v2_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.v2_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-2.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() {}
|
||||||
|
}
|
|
@ -5,51 +5,44 @@
|
||||||
|
|
||||||
package io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0;
|
package io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0;
|
||||||
|
|
||||||
import static io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0.JaxRsAnnotationsTracer.tracer;
|
import static io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0.JaxrsSingletons.instrumenter;
|
||||||
|
|
||||||
import io.opentelemetry.api.trace.Span;
|
import io.opentelemetry.api.trace.Span;
|
||||||
import io.opentelemetry.context.Context;
|
import io.opentelemetry.context.Context;
|
||||||
import io.opentelemetry.context.Scope;
|
import io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming;
|
||||||
import io.opentelemetry.instrumentation.api.tracer.ServerSpan;
|
import io.opentelemetry.instrumentation.api.tracer.ServerSpan;
|
||||||
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
|
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import javax.ws.rs.container.ContainerRequestContext;
|
import javax.ws.rs.container.ContainerRequestContext;
|
||||||
|
|
||||||
public final class RequestContextHelper {
|
public final class RequestContextHelper {
|
||||||
public static Context createOrUpdateAbortSpan(
|
public static Context createOrUpdateAbortSpan(
|
||||||
ContainerRequestContext requestContext, Class<?> resourceClass, Method method) {
|
ContainerRequestContext requestContext, HandlerData handlerData) {
|
||||||
|
|
||||||
if (method != null && resourceClass != null) {
|
if (handlerData == null) {
|
||||||
requestContext.setProperty(JaxRsAnnotationsTracer.ABORT_HANDLED, true);
|
|
||||||
Context context = Java8BytecodeBridge.currentContext();
|
|
||||||
Span serverSpan = ServerSpan.fromContextOrNull(context);
|
|
||||||
Span currentSpan = Java8BytecodeBridge.spanFromContext(context);
|
|
||||||
|
|
||||||
// if there's no current span or it's the same as the server (servlet) span we need to start
|
|
||||||
// a JAX-RS one
|
|
||||||
// in other case, DefaultRequestContextInstrumentation must have already run so it's enough
|
|
||||||
// to just update the names
|
|
||||||
if (currentSpan == null || currentSpan == serverSpan) {
|
|
||||||
return tracer().startSpan(context, resourceClass, method);
|
|
||||||
} else {
|
|
||||||
tracer().updateSpanNames(context, currentSpan, serverSpan, resourceClass, method);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void closeSpanAndScope(Context context, Scope scope, Throwable throwable) {
|
requestContext.setProperty(JaxrsSingletons.ABORT_HANDLED, true);
|
||||||
if (context == null || scope == null) {
|
Context parentContext = Java8BytecodeBridge.currentContext();
|
||||||
return;
|
Span serverSpan = ServerSpan.fromContextOrNull(parentContext);
|
||||||
|
Span currentSpan = Java8BytecodeBridge.spanFromContext(parentContext);
|
||||||
|
|
||||||
|
ServerSpanNaming.updateServerSpanName(
|
||||||
|
parentContext,
|
||||||
|
ServerSpanNaming.Source.CONTROLLER,
|
||||||
|
JaxrsServerSpanNaming.getServerSpanNameSupplier(parentContext, handlerData));
|
||||||
|
|
||||||
|
if (currentSpan != null && currentSpan != serverSpan) {
|
||||||
|
// there's already an active span, and it's not the same as the server (servlet) span,
|
||||||
|
// so we don't want to start a JAX-RS one
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (throwable != null) {
|
if (!instrumenter().shouldStart(parentContext, handlerData)) {
|
||||||
tracer().endExceptionally(context, throwable);
|
return null;
|
||||||
} else {
|
|
||||||
tracer().end(context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
scope.close();
|
return instrumenter().start(parentContext, handlerData);
|
||||||
}
|
}
|
||||||
|
|
||||||
private RequestContextHelper() {}
|
private RequestContextHelper() {}
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
package io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0;
|
package io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0;
|
||||||
|
|
||||||
|
import static io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0.JaxrsSingletons.instrumenter;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||||
|
@ -58,11 +59,15 @@ public class CxfRequestContextInstrumentation implements TypeInstrumentation {
|
||||||
@Advice.OnMethodEnter(suppress = Throwable.class)
|
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||||
public static void decorateAbortSpan(
|
public static void decorateAbortSpan(
|
||||||
@Advice.This AbstractRequestContextImpl requestContext,
|
@Advice.This AbstractRequestContextImpl requestContext,
|
||||||
|
@Local("otelHandlerData") HandlerData handlerData,
|
||||||
@Local("otelContext") Context context,
|
@Local("otelContext") Context context,
|
||||||
@Local("otelScope") Scope scope) {
|
@Local("otelScope") Scope scope) {
|
||||||
|
|
||||||
if (requestContext.getProperty(JaxRsAnnotationsTracer.ABORT_HANDLED) == null
|
if (requestContext.getProperty(JaxrsSingletons.ABORT_HANDLED) != null
|
||||||
&& requestContext instanceof ContainerRequestContext) {
|
|| !(requestContext instanceof ContainerRequestContext)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Message message = requestContext.getMessage();
|
Message message = requestContext.getMessage();
|
||||||
OperationResourceInfoStack resourceInfoStack =
|
OperationResourceInfoStack resourceInfoStack =
|
||||||
(OperationResourceInfoStack)
|
(OperationResourceInfoStack)
|
||||||
|
@ -75,21 +80,26 @@ public class CxfRequestContextInstrumentation implements TypeInstrumentation {
|
||||||
Method method = invocationInfo.getMethodInfo().getMethodToInvoke();
|
Method method = invocationInfo.getMethodInfo().getMethodToInvoke();
|
||||||
Class<?> resourceClass = invocationInfo.getRealClass();
|
Class<?> resourceClass = invocationInfo.getRealClass();
|
||||||
|
|
||||||
|
handlerData = new HandlerData(resourceClass, method);
|
||||||
context =
|
context =
|
||||||
RequestContextHelper.createOrUpdateAbortSpan(
|
RequestContextHelper.createOrUpdateAbortSpan(
|
||||||
(ContainerRequestContext) requestContext, resourceClass, method);
|
(ContainerRequestContext) requestContext, handlerData);
|
||||||
if (context != null) {
|
if (context != null) {
|
||||||
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(
|
||||||
|
@Local("otelHandlerData") HandlerData handlerData,
|
||||||
@Local("otelContext") Context context,
|
@Local("otelContext") Context context,
|
||||||
@Local("otelScope") Scope scope,
|
@Local("otelScope") Scope scope,
|
||||||
@Advice.Thrown Throwable throwable) {
|
@Advice.Thrown Throwable throwable) {
|
||||||
RequestContextHelper.closeSpanAndScope(context, scope, throwable);
|
if (scope == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
scope.close();
|
||||||
|
instrumenter().end(context, handlerData, null, throwable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
package io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0;
|
package io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0;
|
||||||
|
|
||||||
import static io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0.JaxRsPathUtil.normalizePath;
|
import static io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0.JaxrsPathUtil.normalizePath;
|
||||||
|
|
||||||
import io.opentelemetry.api.trace.Span;
|
import io.opentelemetry.api.trace.Span;
|
||||||
import io.opentelemetry.context.Context;
|
import io.opentelemetry.context.Context;
|
||||||
|
|
|
@ -3,5 +3,5 @@
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class CxfAnnotationInstrumentationTest extends JaxRsAnnotationsInstrumentationTest {
|
class CxfAnnotationInstrumentationTest extends JaxrsAnnotationsInstrumentationTest {
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
|
|
||||||
package io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0;
|
package io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0;
|
||||||
|
|
||||||
|
import static io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0.JaxrsSingletons.instrumenter;
|
||||||
|
|
||||||
import io.opentelemetry.context.Context;
|
import io.opentelemetry.context.Context;
|
||||||
import io.opentelemetry.context.Scope;
|
import io.opentelemetry.context.Scope;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
@ -35,31 +37,42 @@ public class JerseyRequestContextInstrumentation extends AbstractRequestContextI
|
||||||
@Advice.OnMethodEnter(suppress = Throwable.class)
|
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||||
public static void decorateAbortSpan(
|
public static void decorateAbortSpan(
|
||||||
@Advice.This ContainerRequestContext requestContext,
|
@Advice.This ContainerRequestContext requestContext,
|
||||||
|
@Local("otelHandlerData") HandlerData handlerData,
|
||||||
@Local("otelContext") Context context,
|
@Local("otelContext") Context context,
|
||||||
@Local("otelScope") Scope scope) {
|
@Local("otelScope") Scope scope) {
|
||||||
UriInfo uriInfo = requestContext.getUriInfo();
|
UriInfo uriInfo = requestContext.getUriInfo();
|
||||||
|
|
||||||
if (requestContext.getProperty(JaxRsAnnotationsTracer.ABORT_HANDLED) == null
|
if (requestContext.getProperty(JaxrsSingletons.ABORT_HANDLED) != null
|
||||||
&& uriInfo instanceof ResourceInfo) {
|
|| !(uriInfo instanceof ResourceInfo)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ResourceInfo resourceInfo = (ResourceInfo) uriInfo;
|
ResourceInfo resourceInfo = (ResourceInfo) uriInfo;
|
||||||
Method method = resourceInfo.getResourceMethod();
|
Method method = resourceInfo.getResourceMethod();
|
||||||
Class<?> resourceClass = resourceInfo.getResourceClass();
|
Class<?> resourceClass = resourceInfo.getResourceClass();
|
||||||
|
|
||||||
context =
|
if (resourceClass == null || method == null) {
|
||||||
RequestContextHelper.createOrUpdateAbortSpan(requestContext, resourceClass, method);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handlerData = new HandlerData(resourceClass, method);
|
||||||
|
context = RequestContextHelper.createOrUpdateAbortSpan(requestContext, handlerData);
|
||||||
if (context != null) {
|
if (context != null) {
|
||||||
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(
|
||||||
|
@Local("otelHandlerData") HandlerData handlerData,
|
||||||
@Local("otelContext") Context context,
|
@Local("otelContext") Context context,
|
||||||
@Local("otelScope") Scope scope,
|
@Local("otelScope") Scope scope,
|
||||||
@Advice.Thrown Throwable throwable) {
|
@Advice.Thrown Throwable throwable) {
|
||||||
RequestContextHelper.closeSpanAndScope(context, scope, throwable);
|
if (scope == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
scope.close();
|
||||||
|
instrumenter().end(context, handlerData, null, throwable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
package io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0;
|
package io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0;
|
||||||
|
|
||||||
import static io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0.JaxRsPathUtil.normalizePath;
|
import static io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0.JaxrsPathUtil.normalizePath;
|
||||||
|
|
||||||
import io.opentelemetry.api.trace.Span;
|
import io.opentelemetry.api.trace.Span;
|
||||||
import io.opentelemetry.context.Context;
|
import io.opentelemetry.context.Context;
|
||||||
|
|
|
@ -3,5 +3,5 @@
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class JerseyAnnotationInstrumentationTest extends JaxRsAnnotationsInstrumentationTest {
|
class JerseyAnnotationInstrumentationTest extends JaxrsAnnotationsInstrumentationTest {
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
|
|
||||||
package io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0;
|
package io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0;
|
||||||
|
|
||||||
|
import static io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0.JaxrsSingletons.instrumenter;
|
||||||
|
|
||||||
import io.opentelemetry.context.Context;
|
import io.opentelemetry.context.Context;
|
||||||
import io.opentelemetry.context.Scope;
|
import io.opentelemetry.context.Scope;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
@ -36,30 +38,37 @@ public class Resteasy30RequestContextInstrumentation extends AbstractRequestCont
|
||||||
@Advice.OnMethodEnter(suppress = Throwable.class)
|
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||||
public static void decorateAbortSpan(
|
public static void decorateAbortSpan(
|
||||||
@Advice.This ContainerRequestContext requestContext,
|
@Advice.This ContainerRequestContext requestContext,
|
||||||
|
@Local("otelHandlerData") HandlerData handlerData,
|
||||||
@Local("otelContext") Context context,
|
@Local("otelContext") Context context,
|
||||||
@Local("otelScope") Scope scope) {
|
@Local("otelScope") Scope scope) {
|
||||||
if (requestContext.getProperty(JaxRsAnnotationsTracer.ABORT_HANDLED) == null
|
if (requestContext.getProperty(JaxrsSingletons.ABORT_HANDLED) != null
|
||||||
&& requestContext instanceof PostMatchContainerRequestContext) {
|
|| !(requestContext instanceof PostMatchContainerRequestContext)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ResourceMethodInvoker resourceMethodInvoker =
|
ResourceMethodInvoker resourceMethodInvoker =
|
||||||
((PostMatchContainerRequestContext) requestContext).getResourceMethod();
|
((PostMatchContainerRequestContext) requestContext).getResourceMethod();
|
||||||
Method method = resourceMethodInvoker.getMethod();
|
Method method = resourceMethodInvoker.getMethod();
|
||||||
Class<?> resourceClass = resourceMethodInvoker.getResourceClass();
|
Class<?> resourceClass = resourceMethodInvoker.getResourceClass();
|
||||||
|
|
||||||
context =
|
handlerData = new HandlerData(resourceClass, method);
|
||||||
RequestContextHelper.createOrUpdateAbortSpan(requestContext, resourceClass, method);
|
context = RequestContextHelper.createOrUpdateAbortSpan(requestContext, handlerData);
|
||||||
if (context != null) {
|
if (context != null) {
|
||||||
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(
|
||||||
|
@Local("otelHandlerData") HandlerData handlerData,
|
||||||
@Local("otelContext") Context context,
|
@Local("otelContext") Context context,
|
||||||
@Local("otelScope") Scope scope,
|
@Local("otelScope") Scope scope,
|
||||||
@Advice.Thrown Throwable throwable) {
|
@Advice.Thrown Throwable throwable) {
|
||||||
RequestContextHelper.closeSpanAndScope(context, scope, throwable);
|
if (scope == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
scope.close();
|
||||||
|
instrumenter().end(context, handlerData, null, throwable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,5 +3,5 @@
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class ResteasyAnnotationInstrumentationTest extends JaxRsAnnotationsInstrumentationTest {
|
class ResteasyAnnotationInstrumentationTest extends JaxrsAnnotationsInstrumentationTest {
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
|
|
||||||
package io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0;
|
package io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0;
|
||||||
|
|
||||||
|
import static io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0.JaxrsSingletons.instrumenter;
|
||||||
|
|
||||||
import io.opentelemetry.context.Context;
|
import io.opentelemetry.context.Context;
|
||||||
import io.opentelemetry.context.Scope;
|
import io.opentelemetry.context.Scope;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
@ -36,30 +38,37 @@ public class Resteasy31RequestContextInstrumentation extends AbstractRequestCont
|
||||||
@Advice.OnMethodEnter(suppress = Throwable.class)
|
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||||
public static void decorateAbortSpan(
|
public static void decorateAbortSpan(
|
||||||
@Advice.This ContainerRequestContext requestContext,
|
@Advice.This ContainerRequestContext requestContext,
|
||||||
|
@Local("otelHandlerData") HandlerData handlerData,
|
||||||
@Local("otelContext") Context context,
|
@Local("otelContext") Context context,
|
||||||
@Local("otelScope") Scope scope) {
|
@Local("otelScope") Scope scope) {
|
||||||
if (requestContext.getProperty(JaxRsAnnotationsTracer.ABORT_HANDLED) == null
|
if (requestContext.getProperty(JaxrsSingletons.ABORT_HANDLED) != null
|
||||||
&& requestContext instanceof PostMatchContainerRequestContext) {
|
|| !(requestContext instanceof PostMatchContainerRequestContext)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ResourceMethodInvoker resourceMethodInvoker =
|
ResourceMethodInvoker resourceMethodInvoker =
|
||||||
((PostMatchContainerRequestContext) requestContext).getResourceMethod();
|
((PostMatchContainerRequestContext) requestContext).getResourceMethod();
|
||||||
Method method = resourceMethodInvoker.getMethod();
|
Method method = resourceMethodInvoker.getMethod();
|
||||||
Class<?> resourceClass = resourceMethodInvoker.getResourceClass();
|
Class<?> resourceClass = resourceMethodInvoker.getResourceClass();
|
||||||
|
|
||||||
context =
|
handlerData = new HandlerData(resourceClass, method);
|
||||||
RequestContextHelper.createOrUpdateAbortSpan(requestContext, resourceClass, method);
|
context = RequestContextHelper.createOrUpdateAbortSpan(requestContext, handlerData);
|
||||||
if (context != null) {
|
if (context != null) {
|
||||||
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(
|
||||||
|
@Local("otelHandlerData") HandlerData handlerData,
|
||||||
@Local("otelContext") Context context,
|
@Local("otelContext") Context context,
|
||||||
@Local("otelScope") Scope scope,
|
@Local("otelScope") Scope scope,
|
||||||
@Advice.Thrown Throwable throwable) {
|
@Advice.Thrown Throwable throwable) {
|
||||||
RequestContextHelper.closeSpanAndScope(context, scope, throwable);
|
if (scope == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
scope.close();
|
||||||
|
instrumenter().end(context, handlerData, null, throwable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,5 +3,5 @@
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class ResteasyAnnotationInstrumentationTest extends JaxRsAnnotationsInstrumentationTest {
|
class ResteasyAnnotationInstrumentationTest extends JaxrsAnnotationsInstrumentationTest {
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ public class ResteasyRootNodeTypeInstrumentation implements TypeInstrumentation
|
||||||
public static void addInvoker(
|
public static void addInvoker(
|
||||||
@Advice.Argument(0) String path,
|
@Advice.Argument(0) String path,
|
||||||
@Advice.Argument(value = 1, typing = Assigner.Typing.DYNAMIC) Object invoker) {
|
@Advice.Argument(value = 1, typing = Assigner.Typing.DYNAMIC) Object invoker) {
|
||||||
String normalizedPath = JaxRsPathUtil.normalizePath(path);
|
String normalizedPath = JaxrsPathUtil.normalizePath(path);
|
||||||
if (invoker instanceof ResourceLocatorInvoker) {
|
if (invoker instanceof ResourceLocatorInvoker) {
|
||||||
ResourceLocatorInvoker resourceLocatorInvoker = (ResourceLocatorInvoker) invoker;
|
ResourceLocatorInvoker resourceLocatorInvoker = (ResourceLocatorInvoker) invoker;
|
||||||
InstrumentationContext.get(ResourceLocatorInvoker.class, String.class)
|
InstrumentationContext.get(ResourceLocatorInvoker.class, String.class)
|
||||||
|
|
|
@ -18,31 +18,7 @@ import javax.ws.rs.PUT
|
||||||
import javax.ws.rs.Path
|
import javax.ws.rs.Path
|
||||||
import spock.lang.Unroll
|
import spock.lang.Unroll
|
||||||
|
|
||||||
abstract class JaxRsAnnotationsInstrumentationTest extends AgentInstrumentationSpecification {
|
abstract class JaxrsAnnotationsInstrumentationTest 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
|
@Unroll
|
||||||
def "span named '#paramName' from annotations on class '#className' when is not root span"() {
|
def "span named '#paramName' from annotations on class '#className' when is not root span"() {
|
Loading…
Reference in New Issue