parent
357140c081
commit
1fba62807f
|
@ -56,7 +56,7 @@ public final class ServerSpanNaming {
|
||||||
ServerSpanNaming serverSpanNaming = context.get(CONTEXT_KEY);
|
ServerSpanNaming serverSpanNaming = context.get(CONTEXT_KEY);
|
||||||
if (serverSpanNaming == null) {
|
if (serverSpanNaming == null) {
|
||||||
String name = serverSpanName.get();
|
String name = serverSpanName.get();
|
||||||
if (name != null) {
|
if (name != null && !name.isEmpty()) {
|
||||||
serverSpan.updateName(name);
|
serverSpan.updateName(name);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
@ -67,7 +67,9 @@ public final class ServerSpanNaming {
|
||||||
!source.useFirst && source.order == serverSpanNaming.updatedBySource.order;
|
!source.useFirst && source.order == serverSpanNaming.updatedBySource.order;
|
||||||
if (source.order > serverSpanNaming.updatedBySource.order || onlyIfBetterName) {
|
if (source.order > serverSpanNaming.updatedBySource.order || onlyIfBetterName) {
|
||||||
String name = serverSpanName.get();
|
String name = serverSpanName.get();
|
||||||
if (name != null && (!onlyIfBetterName || serverSpanNaming.isBetterName(name))) {
|
if (name != null
|
||||||
|
&& !name.isEmpty()
|
||||||
|
&& (!onlyIfBetterName || serverSpanNaming.isBetterName(name))) {
|
||||||
serverSpan.updateName(name);
|
serverSpan.updateName(name);
|
||||||
serverSpanNaming.updatedBySource = source;
|
serverSpanNaming.updatedBySource = source;
|
||||||
serverSpanNaming.nameLength = name.length();
|
serverSpanNaming.nameLength = name.length();
|
||||||
|
|
|
@ -10,6 +10,7 @@ import static io.opentelemetry.api.trace.SpanKind.INTERNAL;
|
||||||
import io.opentelemetry.api.trace.Span;
|
import io.opentelemetry.api.trace.Span;
|
||||||
import io.opentelemetry.api.trace.SpanBuilder;
|
import io.opentelemetry.api.trace.SpanBuilder;
|
||||||
import io.opentelemetry.context.Context;
|
import io.opentelemetry.context.Context;
|
||||||
|
import io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming;
|
||||||
import io.opentelemetry.instrumentation.api.servlet.ServletContextPath;
|
import io.opentelemetry.instrumentation.api.servlet.ServletContextPath;
|
||||||
import io.opentelemetry.instrumentation.api.tracer.BaseTracer;
|
import io.opentelemetry.instrumentation.api.tracer.BaseTracer;
|
||||||
import io.opentelemetry.instrumentation.api.tracer.ServerSpan;
|
import io.opentelemetry.instrumentation.api.tracer.ServerSpan;
|
||||||
|
@ -20,6 +21,7 @@ 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;
|
||||||
|
|
||||||
|
@ -61,19 +63,12 @@ public class JaxRsAnnotationsTracer extends BaseTracer {
|
||||||
|
|
||||||
public void updateSpanNames(
|
public void updateSpanNames(
|
||||||
Context context, Span span, Span serverSpan, Class<?> target, Method method) {
|
Context context, Span span, Span serverSpan, Class<?> target, Method method) {
|
||||||
String pathBasedSpanName = getPathSpanName(target, method);
|
Supplier<String> spanNameSupplier = getPathSpanNameSupplier(context, 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);
|
|
||||||
}
|
|
||||||
if (serverSpan == null) {
|
if (serverSpan == null) {
|
||||||
updateSpanName(span, pathBasedSpanName);
|
updateSpanName(span, spanNameSupplier.get());
|
||||||
} else {
|
} else {
|
||||||
updateSpanName(serverSpan, pathBasedSpanName);
|
ServerSpanNaming.updateServerSpanName(
|
||||||
|
context, ServerSpanNaming.Source.CONTROLLER, spanNameSupplier);
|
||||||
updateSpanName(span, spanNameForMethod(target, method));
|
updateSpanName(span, spanNameForMethod(target, method));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,6 +86,22 @@ public class JaxRsAnnotationsTracer extends BaseTracer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the span name given a JaxRS annotated method. Results are cached so this method can be
|
* Returns the span name given a JaxRS annotated method. Results are cached so this method can be
|
||||||
* called multiple times without significantly impacting performance.
|
* called multiple times without significantly impacting performance.
|
||||||
|
|
|
@ -39,4 +39,9 @@ class CxfHttpServerTest extends JaxRsHttpServerTest<Server> {
|
||||||
void stopServer(Server httpServer) {
|
void stopServer(Server httpServer) {
|
||||||
httpServer.stop()
|
httpServer.stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean hasFrameworkInstrumentation() {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -5,4 +5,8 @@
|
||||||
|
|
||||||
class CxfJettyHttpServerTest extends JaxRsJettyHttpServerTest {
|
class CxfJettyHttpServerTest extends JaxRsJettyHttpServerTest {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean hasFrameworkInstrumentation() {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -36,4 +36,9 @@ class JerseyHttpServerTest extends JaxRsHttpServerTest<Server> {
|
||||||
boolean asyncCancelHasSendError() {
|
boolean asyncCancelHasSendError() {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean hasFrameworkInstrumentation() {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -9,4 +9,9 @@ class JerseyJettyHttpServerTest extends JaxRsJettyHttpServerTest {
|
||||||
boolean asyncCancelHasSendError() {
|
boolean asyncCancelHasSendError() {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean hasFrameworkInstrumentation() {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -25,6 +25,7 @@ dependencies {
|
||||||
library group: 'org.jboss.resteasy', name: 'resteasy-jaxrs', version: '3.0.0.Final'
|
library group: 'org.jboss.resteasy', name: 'resteasy-jaxrs', version: '3.0.0.Final'
|
||||||
|
|
||||||
implementation project(':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-common:javaagent')
|
implementation project(':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-common:javaagent')
|
||||||
|
implementation project(':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-resteasy-common:javaagent')
|
||||||
|
|
||||||
testInstrumentation project(':instrumentation:servlet:servlet-3.0:javaagent')
|
testInstrumentation project(':instrumentation:servlet:servlet-3.0:javaagent')
|
||||||
testInstrumentation project(':instrumentation:servlet:servlet-javax-common:javaagent')
|
testInstrumentation project(':instrumentation:servlet:servlet-javax-common:javaagent')
|
||||||
|
|
|
@ -22,6 +22,9 @@ public class Resteasy30InstrumentationModule extends InstrumentationModule {
|
||||||
public List<TypeInstrumentation> typeInstrumentations() {
|
public List<TypeInstrumentation> typeInstrumentations() {
|
||||||
return asList(
|
return asList(
|
||||||
new Resteasy30RequestContextInstrumentation(),
|
new Resteasy30RequestContextInstrumentation(),
|
||||||
new Resteasy30ServletContainerDispatcherInstrumentation());
|
new ResteasyServletContainerDispatcherInstrumentation(),
|
||||||
|
new ResteasyRootNodeTypeInstrumentation(),
|
||||||
|
new ResteasyResourceMethodInvokerInstrumentation(),
|
||||||
|
new ResteasyResourceLocatorInvokerInstrumentation());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ dependencies {
|
||||||
library group: 'org.jboss.resteasy', name: 'resteasy-jaxrs', version: '3.1.0.Final'
|
library group: 'org.jboss.resteasy', name: 'resteasy-jaxrs', version: '3.1.0.Final'
|
||||||
|
|
||||||
implementation project(':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-common:javaagent')
|
implementation project(':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-common:javaagent')
|
||||||
|
implementation project(':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-resteasy-common:javaagent')
|
||||||
|
|
||||||
testInstrumentation project(':instrumentation:servlet:servlet-3.0:javaagent')
|
testInstrumentation project(':instrumentation:servlet:servlet-3.0:javaagent')
|
||||||
testInstrumentation project(':instrumentation:servlet:servlet-javax-common:javaagent')
|
testInstrumentation project(':instrumentation:servlet:servlet-javax-common:javaagent')
|
||||||
|
|
|
@ -22,6 +22,9 @@ public class Resteasy31InstrumentationModule extends InstrumentationModule {
|
||||||
public List<TypeInstrumentation> typeInstrumentations() {
|
public List<TypeInstrumentation> typeInstrumentations() {
|
||||||
return asList(
|
return asList(
|
||||||
new Resteasy31RequestContextInstrumentation(),
|
new Resteasy31RequestContextInstrumentation(),
|
||||||
new Resteasy31ServletContainerDispatcherInstrumentation());
|
new ResteasyServletContainerDispatcherInstrumentation(),
|
||||||
|
new ResteasyRootNodeTypeInstrumentation(),
|
||||||
|
new ResteasyResourceMethodInvokerInstrumentation(),
|
||||||
|
new ResteasyResourceLocatorInvokerInstrumentation());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
apply from: "$rootDir/gradle/instrumentation.gradle"
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnly group: 'javax.ws.rs', name: 'javax.ws.rs-api', version: '2.0'
|
||||||
|
compileOnly group: 'org.jboss.resteasy', name: 'resteasy-jaxrs', version: '3.1.0.Final'
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0;
|
||||||
|
|
||||||
|
import static java.util.Collections.singletonMap;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||||
|
|
||||||
|
import io.opentelemetry.context.Context;
|
||||||
|
import io.opentelemetry.context.Scope;
|
||||||
|
import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext;
|
||||||
|
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
|
||||||
|
import io.opentelemetry.javaagent.instrumentation.api.jaxrs.JaxrsContextPath;
|
||||||
|
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
|
||||||
|
import java.util.Map;
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
import org.jboss.resteasy.core.ResourceLocatorInvoker;
|
||||||
|
|
||||||
|
public class ResteasyResourceLocatorInvokerInstrumentation implements TypeInstrumentation {
|
||||||
|
@Override
|
||||||
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
|
return named("org.jboss.resteasy.core.ResourceLocatorInvoker");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||||
|
return singletonMap(
|
||||||
|
named("invokeOnTargetObject")
|
||||||
|
.and(takesArgument(0, named("org.jboss.resteasy.spi.HttpRequest")))
|
||||||
|
.and(takesArgument(1, named("org.jboss.resteasy.spi.HttpResponse")))
|
||||||
|
.and(takesArgument(2, Object.class)),
|
||||||
|
ResteasyResourceLocatorInvokerInstrumentation.class.getName()
|
||||||
|
+ "$InvokeOnTargetObjectAdvice");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class InvokeOnTargetObjectAdvice {
|
||||||
|
|
||||||
|
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||||
|
public static void onEnter(
|
||||||
|
@Advice.This ResourceLocatorInvoker resourceInvoker,
|
||||||
|
@Advice.Local("otelScope") Scope scope) {
|
||||||
|
|
||||||
|
Context currentContext = Java8BytecodeBridge.currentContext();
|
||||||
|
|
||||||
|
String name =
|
||||||
|
InstrumentationContext.get(ResourceLocatorInvoker.class, String.class)
|
||||||
|
.get(resourceInvoker);
|
||||||
|
ResteasyTracingUtil.updateServerSpanName(currentContext, name);
|
||||||
|
|
||||||
|
// subresource locator returns a resources class that may have @Path annotations
|
||||||
|
// append current path to jax-rs context path so that it would be present in the final path
|
||||||
|
Context context =
|
||||||
|
JaxrsContextPath.init(currentContext, JaxrsContextPath.prepend(currentContext, name));
|
||||||
|
if (context != null) {
|
||||||
|
scope = context.makeCurrent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||||
|
public static void stopSpan(@Advice.Local("otelScope") Scope scope) {
|
||||||
|
if (scope != null) {
|
||||||
|
scope.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0;
|
||||||
|
|
||||||
|
import static java.util.Collections.singletonMap;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||||
|
|
||||||
|
import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext;
|
||||||
|
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
|
||||||
|
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
|
||||||
|
import java.util.Map;
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
import org.jboss.resteasy.core.ResourceMethodInvoker;
|
||||||
|
|
||||||
|
public class ResteasyResourceMethodInvokerInstrumentation implements TypeInstrumentation {
|
||||||
|
@Override
|
||||||
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
|
return named("org.jboss.resteasy.core.ResourceMethodInvoker");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||||
|
return singletonMap(
|
||||||
|
named("invokeOnTarget")
|
||||||
|
.and(takesArgument(0, named("org.jboss.resteasy.spi.HttpRequest")))
|
||||||
|
.and(takesArgument(1, named("org.jboss.resteasy.spi.HttpResponse")))
|
||||||
|
.and(takesArgument(2, Object.class)),
|
||||||
|
ResteasyResourceMethodInvokerInstrumentation.class.getName() + "$InvokeOnTargetAdvice");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class InvokeOnTargetAdvice {
|
||||||
|
|
||||||
|
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||||
|
public static void onEnter(@Advice.This ResourceMethodInvoker resourceInvoker) {
|
||||||
|
|
||||||
|
String name =
|
||||||
|
InstrumentationContext.get(ResourceMethodInvoker.class, String.class)
|
||||||
|
.get(resourceInvoker);
|
||||||
|
ResteasyTracingUtil.updateServerSpanName(Java8BytecodeBridge.currentContext(), name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0;
|
||||||
|
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||||
|
|
||||||
|
import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext;
|
||||||
|
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.implementation.bytecode.assign.Assigner;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
import org.jboss.resteasy.core.ResourceLocatorInvoker;
|
||||||
|
import org.jboss.resteasy.core.ResourceMethodInvoker;
|
||||||
|
|
||||||
|
public class ResteasyRootNodeTypeInstrumentation implements TypeInstrumentation {
|
||||||
|
@Override
|
||||||
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
|
return named("org.jboss.resteasy.core.registry.RootNode");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||||
|
Map<ElementMatcher<MethodDescription>, String> transformers = new HashMap<>();
|
||||||
|
transformers.put(
|
||||||
|
named("addInvoker")
|
||||||
|
.and(takesArgument(0, String.class))
|
||||||
|
// package of ResourceInvoker was changed in reasteasy 4
|
||||||
|
.and(
|
||||||
|
takesArgument(
|
||||||
|
1,
|
||||||
|
named("org.jboss.resteasy.core.ResourceInvoker")
|
||||||
|
.or(named("org.jboss.resteasy.spi.ResourceInvoker")))),
|
||||||
|
ResteasyRootNodeTypeInstrumentation.class.getName() + "$AddInvokerAdvice");
|
||||||
|
|
||||||
|
return transformers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class AddInvokerAdvice {
|
||||||
|
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||||
|
public static void addInvoker(
|
||||||
|
@Advice.Argument(0) String path,
|
||||||
|
@Advice.Argument(value = 1, typing = Assigner.Typing.DYNAMIC) Object invoker) {
|
||||||
|
String normalizedPath = ResteasyTracingUtil.normalizePath(path);
|
||||||
|
if (invoker instanceof ResourceLocatorInvoker) {
|
||||||
|
ResourceLocatorInvoker resourceLocatorInvoker = (ResourceLocatorInvoker) invoker;
|
||||||
|
InstrumentationContext.get(ResourceLocatorInvoker.class, String.class)
|
||||||
|
.put(resourceLocatorInvoker, normalizedPath);
|
||||||
|
} else if (invoker instanceof ResourceMethodInvoker) {
|
||||||
|
ResourceMethodInvoker resourceMethodInvoker = (ResourceMethodInvoker) invoker;
|
||||||
|
InstrumentationContext.get(ResourceMethodInvoker.class, String.class)
|
||||||
|
.put(resourceMethodInvoker, normalizedPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,7 +20,7 @@ import net.bytebuddy.description.method.MethodDescription;
|
||||||
import net.bytebuddy.description.type.TypeDescription;
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
import net.bytebuddy.matcher.ElementMatcher;
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
|
||||||
public class Resteasy31ServletContainerDispatcherInstrumentation implements TypeInstrumentation {
|
public class ResteasyServletContainerDispatcherInstrumentation implements TypeInstrumentation {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
|
@ -31,7 +31,7 @@ public class Resteasy31ServletContainerDispatcherInstrumentation implements Type
|
||||||
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||||
return Collections.singletonMap(
|
return Collections.singletonMap(
|
||||||
isMethod().and(named("service")),
|
isMethod().and(named("service")),
|
||||||
Resteasy31ServletContainerDispatcherInstrumentation.class.getName() + "$ServiceAdvice");
|
ResteasyServletContainerDispatcherInstrumentation.class.getName() + "$ServiceAdvice");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ServiceAdvice {
|
public static class ServiceAdvice {
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0;
|
||||||
|
|
||||||
|
import io.opentelemetry.api.trace.Span;
|
||||||
|
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.ServerSpan;
|
||||||
|
import io.opentelemetry.javaagent.instrumentation.api.jaxrs.JaxrsContextPath;
|
||||||
|
|
||||||
|
public final class ResteasyTracingUtil {
|
||||||
|
|
||||||
|
private ResteasyTracingUtil() {}
|
||||||
|
|
||||||
|
public static String normalizePath(String path) {
|
||||||
|
// ensure that non-empty path starts with /
|
||||||
|
if (path == null || "/".equals(path)) {
|
||||||
|
path = "";
|
||||||
|
} else if (!path.startsWith("/")) {
|
||||||
|
path = "/" + path;
|
||||||
|
}
|
||||||
|
// remove trailing /
|
||||||
|
if (path.endsWith("/")) {
|
||||||
|
path = path.substring(0, path.length() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void updateServerSpanName(Context context, String name) {
|
||||||
|
if (name == null || name.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Span serverSpan = ServerSpan.fromContextOrNull(context);
|
||||||
|
if (serverSpan == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
serverSpan.updateName(
|
||||||
|
ServletContextPath.prepend(context, JaxrsContextPath.prepend(context, name)));
|
||||||
|
// mark span name as updated from controller to avoid JaxRsAnnotationsTracer updating it
|
||||||
|
ServerSpanNaming.updateSource(context, ServerSpanNaming.Source.CONTROLLER);
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ import static io.opentelemetry.api.trace.StatusCode.ERROR
|
||||||
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.EXCEPTION
|
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.EXCEPTION
|
||||||
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.PATH_PARAM
|
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.PATH_PARAM
|
||||||
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.SUCCESS
|
import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.SUCCESS
|
||||||
|
import static io.opentelemetry.instrumentation.test.utils.TraceUtils.basicSpan
|
||||||
import static java.util.concurrent.TimeUnit.SECONDS
|
import static java.util.concurrent.TimeUnit.SECONDS
|
||||||
import static org.junit.Assume.assumeTrue
|
import static org.junit.Assume.assumeTrue
|
||||||
|
|
||||||
|
@ -27,6 +28,94 @@ import spock.lang.Unroll
|
||||||
import test.JaxRsTestResource
|
import test.JaxRsTestResource
|
||||||
|
|
||||||
abstract class JaxRsHttpServerTest<S> extends HttpServerTest<S> implements AgentTestTrait {
|
abstract class JaxRsHttpServerTest<S> extends HttpServerTest<S> implements AgentTestTrait {
|
||||||
|
|
||||||
|
def "test super method without @Path"() {
|
||||||
|
assumeTrue(hasFrameworkInstrumentation())
|
||||||
|
|
||||||
|
given:
|
||||||
|
def url = HttpUrl.get(address.resolve("test-resource-super")).newBuilder()
|
||||||
|
.build()
|
||||||
|
def request = request(url, "GET", null).build()
|
||||||
|
def response = client.newCall(request).execute()
|
||||||
|
|
||||||
|
expect:
|
||||||
|
response.withCloseable {
|
||||||
|
assert response.code() == SUCCESS.status
|
||||||
|
assert response.body().string() == SUCCESS.body
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 2) {
|
||||||
|
span(0) {
|
||||||
|
hasNoParent()
|
||||||
|
kind SERVER
|
||||||
|
name getContextPath() + "/test-resource-super"
|
||||||
|
}
|
||||||
|
basicSpan(it, 1, "controller", span(0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def "test interface method with @Path"() {
|
||||||
|
assumeTrue(hasFrameworkInstrumentation())
|
||||||
|
|
||||||
|
given:
|
||||||
|
def url = HttpUrl.get(address.resolve("test-resource-interface/call")).newBuilder()
|
||||||
|
.build()
|
||||||
|
def request = request(url, "GET", null).build()
|
||||||
|
def response = client.newCall(request).execute()
|
||||||
|
|
||||||
|
expect:
|
||||||
|
response.withCloseable {
|
||||||
|
assert response.code() == SUCCESS.status
|
||||||
|
assert response.body().string() == SUCCESS.body
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 2) {
|
||||||
|
span(0) {
|
||||||
|
hasNoParent()
|
||||||
|
kind SERVER
|
||||||
|
name getContextPath() + "/test-resource-interface/call"
|
||||||
|
}
|
||||||
|
basicSpan(it, 1, "controller", span(0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def "test sub resource locator"() {
|
||||||
|
assumeTrue(hasFrameworkInstrumentation())
|
||||||
|
|
||||||
|
given:
|
||||||
|
def url = HttpUrl.get(address.resolve("test-sub-resource-locator/call/sub")).newBuilder()
|
||||||
|
.build()
|
||||||
|
def request = request(url, "GET", null).build()
|
||||||
|
def response = client.newCall(request).execute()
|
||||||
|
|
||||||
|
expect:
|
||||||
|
response.withCloseable {
|
||||||
|
assert response.code() == SUCCESS.status
|
||||||
|
assert response.body().string() == SUCCESS.body
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 5) {
|
||||||
|
span(0) {
|
||||||
|
hasNoParent()
|
||||||
|
kind SERVER
|
||||||
|
name getContextPath() + "/test-sub-resource-locator/call/sub"
|
||||||
|
}
|
||||||
|
basicSpan(it, 1,"JaxRsSubResourceLocatorTestResource.call", span(0))
|
||||||
|
basicSpan(it, 2, "controller", span(1))
|
||||||
|
basicSpan(it, 3, "SubResource.call", span(0))
|
||||||
|
basicSpan(it, 4, "controller", span(3))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Unroll
|
@Unroll
|
||||||
def "should handle #desc AsyncResponse"() {
|
def "should handle #desc AsyncResponse"() {
|
||||||
given:
|
given:
|
||||||
|
@ -130,6 +219,10 @@ abstract class JaxRsHttpServerTest<S> extends HttpServerTest<S> implements Agent
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean hasFrameworkInstrumentation() {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
boolean asyncCancelHasSendError() {
|
boolean asyncCancelHasSendError() {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
|
@ -147,6 +147,58 @@ class JaxRsTestResource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Path("test-resource-super")
|
||||||
|
class JaxRsSuperClassTestResource extends JaxRsSuperClassTestResourceSuper {
|
||||||
|
}
|
||||||
|
|
||||||
|
class JaxRsSuperClassTestResourceSuper {
|
||||||
|
@GET
|
||||||
|
Object call() {
|
||||||
|
HttpServerTest.controller(SUCCESS) {
|
||||||
|
SUCCESS.body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class JaxRsInterfaceClassTestResource extends JaxRsInterfaceClassTestResourceSuper implements JaxRsInterface {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Path("test-resource-interface")
|
||||||
|
interface JaxRsInterface {
|
||||||
|
@Path("call")
|
||||||
|
@GET
|
||||||
|
Object call()
|
||||||
|
}
|
||||||
|
|
||||||
|
class JaxRsInterfaceClassTestResourceSuper {
|
||||||
|
Object call() {
|
||||||
|
HttpServerTest.controller(SUCCESS) {
|
||||||
|
SUCCESS.body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Path("test-sub-resource-locator")
|
||||||
|
class JaxRsSubResourceLocatorTestResource {
|
||||||
|
@Path("call")
|
||||||
|
Object call() {
|
||||||
|
HttpServerTest.controller(SUCCESS) {
|
||||||
|
return new SubResource()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SubResource {
|
||||||
|
@Path("sub")
|
||||||
|
@GET
|
||||||
|
String call() {
|
||||||
|
HttpServerTest.controller(SUCCESS) {
|
||||||
|
new Exception().printStackTrace()
|
||||||
|
SUCCESS.body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class JaxRsTestExceptionMapper implements ExceptionMapper<Exception> {
|
class JaxRsTestExceptionMapper implements ExceptionMapper<Exception> {
|
||||||
@Override
|
@Override
|
||||||
Response toResponse(Exception exception) {
|
Response toResponse(Exception exception) {
|
||||||
|
@ -161,6 +213,9 @@ class JaxRsTestApplication extends Application {
|
||||||
Set<Class<?>> getClasses() {
|
Set<Class<?>> getClasses() {
|
||||||
def classes = new HashSet()
|
def classes = new HashSet()
|
||||||
classes.add(JaxRsTestResource)
|
classes.add(JaxRsTestResource)
|
||||||
|
classes.add(JaxRsSuperClassTestResource)
|
||||||
|
classes.add(JaxRsInterfaceClassTestResource)
|
||||||
|
classes.add(JaxRsSubResourceLocatorTestResource)
|
||||||
classes.add(JaxRsTestExceptionMapper)
|
classes.add(JaxRsTestExceptionMapper)
|
||||||
return classes
|
return classes
|
||||||
}
|
}
|
||||||
|
|
|
@ -135,6 +135,7 @@ include ':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-jersey-2.0:javaagent'
|
||||||
include ':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-payara-testing'
|
include ':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-payara-testing'
|
||||||
include ':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-resteasy-3.0:javaagent'
|
include ':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-resteasy-3.0:javaagent'
|
||||||
include ':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-resteasy-3.1:javaagent'
|
include ':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-resteasy-3.1:javaagent'
|
||||||
|
include ':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-resteasy-common:javaagent'
|
||||||
include ':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-testing'
|
include ':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-testing'
|
||||||
include ':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-tomee-testing'
|
include ':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-tomee-testing'
|
||||||
include ':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-wildfly-testing'
|
include ':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-wildfly-testing'
|
||||||
|
|
Loading…
Reference in New Issue