Skip to content

Commit

Permalink
Update jaxrs-common to Instrumenter API (#3796)
Browse files Browse the repository at this point in the history
  • Loading branch information
trask authored Aug 10, 2021
1 parent 19711ca commit 7e007e8
Show file tree
Hide file tree
Showing 26 changed files with 377 additions and 264 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,7 @@ dependencies {
compileOnly(project(":instrumentation:jaxrs:bootstrap"))

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")
}
Original file line number Diff line number Diff line change
@@ -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();
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,23 @@

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 java.util.function.BiFunction;

public class CompletionStageFinishCallback<T> implements BiFunction<T, Throwable, T> {
private final Context context;
private final HandlerData handlerData;

public CompletionStageFinishCallback(Context context) {
public CompletionStageFinishCallback(Context context, HandlerData handlerData) {
this.context = context;
this.handlerData = handlerData;
}

@Override
public T apply(T result, Throwable throwable) {
if (throwable == null) {
tracer().end(context);
} else {
tracer().endExceptionally(context, throwable);
}
instrumenter().end(context, handlerData, null, throwable);
return result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public static class RequestFilterAdvice {
public static void setFilterClass(
@Advice.This ContainerRequestFilter filter,
@Advice.Argument(0) ContainerRequestContext context) {
context.setProperty(JaxRsAnnotationsTracer.ABORT_FILTER_CLASS, filter.getClass());
context.setProperty(JaxrsSingletons.ABORT_FILTER_CLASS, filter.getClass());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@

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.Scope;
import io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import java.lang.reflect.Method;
import javax.ws.rs.container.ContainerRequestContext;
import net.bytebuddy.asm.Advice;
Expand All @@ -35,30 +37,55 @@ public static class ContainerRequestContextAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void createGenericSpan(
@Advice.This ContainerRequestContext requestContext,
@Local("otelHandlerData") HandlerData handlerData,
@Local("otelContext") Context context,
@Local("otelScope") Scope scope) {
if (requestContext.getProperty(JaxRsAnnotationsTracer.ABORT_HANDLED) == null) {
Class<?> filterClass =
(Class<?>) requestContext.getProperty(JaxRsAnnotationsTracer.ABORT_FILTER_CLASS);
Method method = null;
try {
method = filterClass.getMethod("filter", ContainerRequestContext.class);
} catch (NoSuchMethodException e) {
// Unable to find the filter method. This should not be reachable because the context
// can only be aborted inside the filter method
}
if (requestContext.getProperty(JaxrsSingletons.ABORT_HANDLED) != null) {
return;
}

Class<?> filterClass =
(Class<?>) requestContext.getProperty(JaxrsSingletons.ABORT_FILTER_CLASS);
Method method = null;
try {
method = filterClass.getMethod("filter", ContainerRequestContext.class);
} catch (NoSuchMethodException e) {
// Unable to find the filter method. This should not be reachable because the context
// can only be aborted inside the filter method
}

if (filterClass == null || method == null) {
return;
}

context = tracer().startSpan(filterClass, method);
scope = context.makeCurrent();
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)
public static void stopSpan(
@Local("otelHandlerData") HandlerData handlerData,
@Local("otelContext") Context context,
@Local("otelScope") Scope scope,
@Advice.Thrown Throwable throwable) {
RequestContextHelper.closeSpanAndScope(context, scope, throwable);
if (scope == null) {
return;
}

scope.close();
instrumenter().end(context, handlerData, null, throwable);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,102 +5,38 @@

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.JaxrsContextPath;
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.Path;

public class JaxRsAnnotationsTracer extends BaseTracer {
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";
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) {
return new ConcurrentHashMap<>();
}
};

public Context startSpan(Class<?> target, Method method) {
return startSpan(Context.current(), target, method);
}

public Context startSpan(Context parentContext, Class<?> target, Method method) {
// We create span and immediately update its name
// 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(
Context context, Span span, Span serverSpan, Class<?> target, Method method) {
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 final Class<?> target;
private final Method method;

private static void updateSpanName(Span span, String spanName) {
if (!spanName.isEmpty()) {
span.updateName(spanName);
}
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 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;
};
public String methodName() {
return method.getName();
}

/**
Expand All @@ -109,8 +45,8 @@ private Supplier<String> getPathSpanNameSupplier(
*
* @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;
Expand Down Expand Up @@ -222,9 +158,4 @@ private static String buildSpanName(Path classPath, Path methodPath) {

return spanNameBuilder.toString().trim();
}

@Override
protected String getInstrumentationName() {
return "io.opentelemetry.jaxrs-2.0-common";
}
}
Loading

0 comments on commit 7e007e8

Please sign in to comment.