Jersey and cxf server span naming (#2919)
This commit is contained in:
parent
53c38ac8e4
commit
8dca27868c
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0;
|
||||||
|
|
||||||
|
public final class JaxRsPathUtil {
|
||||||
|
private JaxRsPathUtil() {}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,6 +23,7 @@ public class CxfInstrumentationModule extends InstrumentationModule {
|
||||||
return asList(
|
return asList(
|
||||||
new CxfRequestContextInstrumentation(),
|
new CxfRequestContextInstrumentation(),
|
||||||
new CxfServletControllerInstrumentation(),
|
new CxfServletControllerInstrumentation(),
|
||||||
new CxfRsHttpListenerInstrumentation());
|
new CxfRsHttpListenerInstrumentation(),
|
||||||
|
new CxfJaxRsInvokerInstrumentation());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* 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.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.apache.cxf.message.Exchange;
|
||||||
|
|
||||||
|
public class CxfJaxRsInvokerInstrumentation implements TypeInstrumentation {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
|
return named("org.apache.cxf.jaxrs.JAXRSInvoker");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||||
|
return singletonMap(
|
||||||
|
named("invoke")
|
||||||
|
.and(takesArgument(0, named("org.apache.cxf.message.Exchange")))
|
||||||
|
.and(takesArgument(1, Object.class))
|
||||||
|
.and(takesArgument(2, Object.class)),
|
||||||
|
CxfJaxRsInvokerInstrumentation.class.getName() + "$InvokeAdvice");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class InvokeAdvice {
|
||||||
|
|
||||||
|
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||||
|
public static void onEnter(
|
||||||
|
@Advice.Argument(0) Exchange exchange, @Advice.Local("otelScope") Scope scope) {
|
||||||
|
Context context = CxfTracingUtil.updateServerSpanName(exchange);
|
||||||
|
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,61 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0;
|
||||||
|
|
||||||
|
import static io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0.JaxRsPathUtil.normalizePath;
|
||||||
|
|
||||||
|
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;
|
||||||
|
import org.apache.cxf.jaxrs.model.ClassResourceInfo;
|
||||||
|
import org.apache.cxf.jaxrs.model.OperationResourceInfo;
|
||||||
|
import org.apache.cxf.jaxrs.model.URITemplate;
|
||||||
|
import org.apache.cxf.message.Exchange;
|
||||||
|
|
||||||
|
public final class CxfTracingUtil {
|
||||||
|
|
||||||
|
private CxfTracingUtil() {}
|
||||||
|
|
||||||
|
public static Context updateServerSpanName(Exchange exchange) {
|
||||||
|
Context context = Context.current();
|
||||||
|
Span serverSpan = ServerSpan.fromContextOrNull(context);
|
||||||
|
if (serverSpan == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
OperationResourceInfo ori = exchange.get(OperationResourceInfo.class);
|
||||||
|
ClassResourceInfo cri = ori.getClassResourceInfo();
|
||||||
|
String name = getName(cri.getURITemplate(), ori.getURITemplate());
|
||||||
|
if (name.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
return JaxrsContextPath.init(context, JaxrsContextPath.prepend(context, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getName(URITemplate classTemplate, URITemplate operationTemplate) {
|
||||||
|
String classPath = normalize(classTemplate);
|
||||||
|
String operationPath = normalize(operationTemplate);
|
||||||
|
|
||||||
|
return classPath + operationPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String normalize(URITemplate uriTemplate) {
|
||||||
|
if (uriTemplate == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalizePath(uriTemplate.getValue());
|
||||||
|
}
|
||||||
|
}
|
|
@ -39,9 +39,4 @@ class CxfHttpServerTest extends JaxRsHttpServerTest<Server> {
|
||||||
void stopServer(Server httpServer) {
|
void stopServer(Server httpServer) {
|
||||||
httpServer.stop()
|
httpServer.stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
boolean hasFrameworkInstrumentation() {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -4,9 +4,4 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class CxfJettyHttpServerTest extends JaxRsJettyHttpServerTest {
|
class CxfJettyHttpServerTest extends JaxRsJettyHttpServerTest {
|
||||||
|
|
||||||
@Override
|
|
||||||
boolean hasFrameworkInstrumentation() {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -21,6 +21,8 @@ public class JerseyInstrumentationModule extends InstrumentationModule {
|
||||||
@Override
|
@Override
|
||||||
public List<TypeInstrumentation> typeInstrumentations() {
|
public List<TypeInstrumentation> typeInstrumentations() {
|
||||||
return asList(
|
return asList(
|
||||||
new JerseyRequestContextInstrumentation(), new JerseyServletContainerInstrumentation());
|
new JerseyRequestContextInstrumentation(),
|
||||||
|
new JerseyServletContainerInstrumentation(),
|
||||||
|
new JerseyResourceMethodDispatcherInstrumentation());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* 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.tooling.TypeInstrumentation;
|
||||||
|
import java.util.Map;
|
||||||
|
import javax.ws.rs.core.Request;
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
|
||||||
|
public class JerseyResourceMethodDispatcherInstrumentation implements TypeInstrumentation {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
|
return named("org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||||
|
return singletonMap(
|
||||||
|
named("dispatch")
|
||||||
|
.and(
|
||||||
|
takesArgument(
|
||||||
|
1,
|
||||||
|
named("javax.ws.rs.core.Request")
|
||||||
|
.or(named("org.glassfish.jersey.server.ContainerRequest")))),
|
||||||
|
JerseyResourceMethodDispatcherInstrumentation.class.getName() + "$DispatchAdvice");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DispatchAdvice {
|
||||||
|
|
||||||
|
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||||
|
public static void onEnter(@Advice.Argument(1) Request request) {
|
||||||
|
JerseyTracingUtil.updateServerSpanName(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0;
|
||||||
|
|
||||||
|
import static io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0.JaxRsPathUtil.normalizePath;
|
||||||
|
|
||||||
|
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;
|
||||||
|
import java.util.Optional;
|
||||||
|
import javax.ws.rs.core.Request;
|
||||||
|
import javax.ws.rs.core.UriInfo;
|
||||||
|
import org.glassfish.jersey.server.ContainerRequest;
|
||||||
|
import org.glassfish.jersey.server.ExtendedUriInfo;
|
||||||
|
|
||||||
|
public class JerseyTracingUtil {
|
||||||
|
|
||||||
|
public static void updateServerSpanName(Request request) {
|
||||||
|
Context context = Context.current();
|
||||||
|
Span serverSpan = ServerSpan.fromContextOrNull(context);
|
||||||
|
if (serverSpan == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ContainerRequest containerRequest = (ContainerRequest) request;
|
||||||
|
UriInfo uriInfo = containerRequest.getUriInfo();
|
||||||
|
ExtendedUriInfo extendedUriInfo = (ExtendedUriInfo) uriInfo;
|
||||||
|
Optional<String> name =
|
||||||
|
extendedUriInfo.getMatchedTemplates().stream()
|
||||||
|
.map((uriTemplate) -> normalizePath(uriTemplate.getTemplate()))
|
||||||
|
.reduce((a, b) -> b + a);
|
||||||
|
if (!name.isPresent()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
serverSpan.updateName(
|
||||||
|
ServletContextPath.prepend(context, JaxrsContextPath.prepend(context, name.get())));
|
||||||
|
// mark span name as updated from controller to avoid JaxRsAnnotationsTracer updating it
|
||||||
|
ServerSpanNaming.updateSource(context, ServerSpanNaming.Source.CONTROLLER);
|
||||||
|
}
|
||||||
|
}
|
|
@ -38,7 +38,8 @@ class JerseyHttpServerTest extends JaxRsHttpServerTest<Server> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
boolean hasFrameworkInstrumentation() {
|
boolean testInterfaceMethodWithPath() {
|
||||||
|
// disables a test that jersey deems invalid
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -11,7 +11,8 @@ class JerseyJettyHttpServerTest extends JaxRsJettyHttpServerTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
boolean hasFrameworkInstrumentation() {
|
boolean testInterfaceMethodWithPath() {
|
||||||
|
// disables a test that jersey deems invalid
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,4 +3,6 @@ apply from: "$rootDir/gradle/instrumentation.gradle"
|
||||||
dependencies {
|
dependencies {
|
||||||
compileOnly group: 'javax.ws.rs', name: 'javax.ws.rs-api', version: '2.0'
|
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'
|
compileOnly group: 'org.jboss.resteasy', name: 'resteasy-jaxrs', version: '3.1.0.Final'
|
||||||
|
|
||||||
|
implementation project(':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-common:javaagent')
|
||||||
}
|
}
|
|
@ -48,7 +48,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 = ResteasyTracingUtil.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)
|
||||||
|
|
|
@ -16,21 +16,6 @@ public final class ResteasyTracingUtil {
|
||||||
|
|
||||||
private 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) {
|
public static void updateServerSpanName(Context context, String name) {
|
||||||
if (name == null || name.isEmpty()) {
|
if (name == null || name.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -30,8 +30,6 @@ 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"() {
|
def "test super method without @Path"() {
|
||||||
assumeTrue(hasFrameworkInstrumentation())
|
|
||||||
|
|
||||||
given:
|
given:
|
||||||
def url = HttpUrl.get(address.resolve("test-resource-super")).newBuilder()
|
def url = HttpUrl.get(address.resolve("test-resource-super")).newBuilder()
|
||||||
.build()
|
.build()
|
||||||
|
@ -58,7 +56,7 @@ abstract class JaxRsHttpServerTest<S> extends HttpServerTest<S> implements Agent
|
||||||
}
|
}
|
||||||
|
|
||||||
def "test interface method with @Path"() {
|
def "test interface method with @Path"() {
|
||||||
assumeTrue(hasFrameworkInstrumentation())
|
assumeTrue(testInterfaceMethodWithPath())
|
||||||
|
|
||||||
given:
|
given:
|
||||||
def url = HttpUrl.get(address.resolve("test-resource-interface/call")).newBuilder()
|
def url = HttpUrl.get(address.resolve("test-resource-interface/call")).newBuilder()
|
||||||
|
@ -86,8 +84,6 @@ abstract class JaxRsHttpServerTest<S> extends HttpServerTest<S> implements Agent
|
||||||
}
|
}
|
||||||
|
|
||||||
def "test sub resource locator"() {
|
def "test sub resource locator"() {
|
||||||
assumeTrue(hasFrameworkInstrumentation())
|
|
||||||
|
|
||||||
given:
|
given:
|
||||||
def url = HttpUrl.get(address.resolve("test-sub-resource-locator/call/sub")).newBuilder()
|
def url = HttpUrl.get(address.resolve("test-sub-resource-locator/call/sub")).newBuilder()
|
||||||
.build()
|
.build()
|
||||||
|
@ -219,7 +215,7 @@ abstract class JaxRsHttpServerTest<S> extends HttpServerTest<S> implements Agent
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean hasFrameworkInstrumentation() {
|
boolean testInterfaceMethodWithPath() {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue