JAX-RS: add code.namespace and code.function attributes
MODIFY
instrumentation/jaxrs/jaxrs-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrs/v1_0/JaxRsAnnotationsTracer.java
instrumentation/jaxrs/jaxrs-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrs/v1_0/JaxRsAnnotationsTracer.java
package io.opentelemetry.javaagent.instrumentation.jaxrs.v1_0;
import io.opentelemetry.api.trace.Span;
+import io.opentelemetry.api.trace.SpanBuilder;
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.api.servlet.ServletContextPath;
import io.opentelemetry.instrumentation.api.tracer.BaseTracer;
import io.opentelemetry.instrumentation.api.tracer.ServerSpan;
import io.opentelemetry.javaagent.instrumentation.api.ClassHierarchyIterable;
+import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Map;
@@ -49,7 +51,17 @@ public class JaxRsAnnotationsTracer extends BaseTracer {
updateServerSpanName(parentContext, serverSpan, pathBasedSpanName);
}
- return startSpan(parentContext, spanName, SpanKind.INTERNAL);
+ SpanBuilder spanBuilder = spanBuilder(parentContext, spanName, SpanKind.INTERNAL);
+ setCodeAttributes(spanBuilder, target, method);
+ Span span = spanBuilder.startSpan();
+ return parentContext.with(span);
+ }
+
+ private 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 void updateServerSpanName(Context context, Span span, String spanName) {

MODIFY
instrumentation/jaxrs/jaxrs-1.0/javaagent/src/test/groovy/JaxRsAnnotations1InstrumentationTest.groovy
instrumentation/jaxrs/jaxrs-1.0/javaagent/src/test/groovy/JaxRsAnnotations1InstrumentationTest.groovy
@@ -8,6 +8,7 @@ import static io.opentelemetry.instrumentation.test.utils.ClassUtils.getClassNam
import static io.opentelemetry.instrumentation.test.utils.TraceUtils.runUnderServerTrace
import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification
+import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
import javax.ws.rs.DELETE
import javax.ws.rs.GET
import javax.ws.rs.HEAD
@@ -21,12 +22,13 @@ class JaxRsAnnotations1InstrumentationTest extends AgentInstrumentationSpecifica
def "instrumentation can be used as root span and resource is set to METHOD PATH"() {
setup:
- new Jax() {
+ def jax = new Jax() {
@POST
@Path("/a")
void call() {
}
- }.call()
+ }
+ jax.call()
expect:
assertTraces(1) {
@@ -34,6 +36,8 @@ class JaxRsAnnotations1InstrumentationTest extends AgentInstrumentationSpecifica
span(0) {
name "/a"
attributes {
+ "${SemanticAttributes.CODE_NAMESPACE.key}" jax.getClass().getName()
+ "${SemanticAttributes.CODE_FUNCTION.key}" "call"
}
}
}
@@ -61,6 +65,8 @@ class JaxRsAnnotations1InstrumentationTest extends AgentInstrumentationSpecifica
name "${className}.call"
childOf span(0)
attributes {
+ "${SemanticAttributes.CODE_NAMESPACE.key}" obj.getClass().getName()
+ "${SemanticAttributes.CODE_FUNCTION.key}" "call"
}
}
}

MODIFY
instrumentation/jaxrs/jaxrs-1.0/javaagent/src/test/groovy/JerseyTest.groovy
instrumentation/jaxrs/jaxrs-1.0/javaagent/src/test/groovy/JerseyTest.groovy
@@ -9,6 +9,7 @@ import static io.opentelemetry.instrumentation.test.utils.TraceUtils.runUnderSer
import io.dropwizard.testing.junit.ResourceTestRule
import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification
+import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
import org.junit.ClassRule
import spock.lang.Shared
import spock.lang.Unroll
@@ -47,6 +48,8 @@ class JerseyTest extends AgentInstrumentationSpecification {
childOf span(0)
name controllerName
attributes {
+ "${SemanticAttributes.CODE_NAMESPACE.key}" ~/Resource[$]Test*/
+ "${SemanticAttributes.CODE_FUNCTION.key}" "hello"
}
}
}
@@ -83,6 +86,8 @@ class JerseyTest extends AgentInstrumentationSpecification {
name controller1Name
kind INTERNAL
attributes {
+ "${SemanticAttributes.CODE_NAMESPACE.key}" ~/Resource[$]Test*/
+ "${SemanticAttributes.CODE_FUNCTION.key}" "nested"
}
}
}

MODIFY
instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrs/v2_0/JaxRsAnnotationsTracer.java
instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrs/v2_0/JaxRsAnnotationsTracer.java
@@ -8,11 +8,13 @@ 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.ServletContextPath;
import io.opentelemetry.instrumentation.api.tracer.BaseTracer;
import io.opentelemetry.instrumentation.api.tracer.ServerSpan;
import io.opentelemetry.javaagent.instrumentation.api.ClassHierarchyIterable;
+import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Map;
@@ -48,7 +50,9 @@ public class JaxRsAnnotationsTracer extends BaseTracer {
// 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.
- Span span = spanBuilder(parentContext, "jax-rs.request", INTERNAL).startSpan();
+ 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);
@@ -56,7 +60,14 @@ public class JaxRsAnnotationsTracer extends BaseTracer {
public void updateSpanNames(
Context context, Span span, Span serverSpan, Class<?> target, Method method) {
- String pathBasedSpanName = ServletContextPath.prepend(context, getPathSpanName(target, method));
+ 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 = ServletContextPath.prepend(context, pathBasedSpanName);
+ }
if (serverSpan == null) {
updateSpanName(span, pathBasedSpanName);
} else {
@@ -71,6 +82,13 @@ public class JaxRsAnnotationsTracer extends BaseTracer {
}
}
+ private 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());
+ }
+ }
+
/**
* Returns the span name given a JaxRS annotated method. Results are cached so this method can be
* called multiple times without significantly impacting performance.

MODIFY
instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-testing/src/main/groovy/JaxRsAnnotationsInstrumentationTest.groovy
instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-testing/src/main/groovy/JaxRsAnnotationsInstrumentationTest.groovy
@@ -8,6 +8,7 @@ import static io.opentelemetry.instrumentation.test.utils.ClassUtils.getClassNam
import static io.opentelemetry.instrumentation.test.utils.TraceUtils.runUnderServerTrace
import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification
+import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
import javax.ws.rs.DELETE
import javax.ws.rs.GET
import javax.ws.rs.HEAD
@@ -21,12 +22,13 @@ abstract class JaxRsAnnotationsInstrumentationTest extends AgentInstrumentationS
def "instrumentation can be used as root span and resource is set to METHOD PATH"() {
setup:
- new Jax() {
+ def jax = new Jax() {
@POST
@Path("/a")
void call() {
}
- }.call()
+ }
+ jax.call()
expect:
assertTraces(1) {
@@ -34,6 +36,8 @@ abstract class JaxRsAnnotationsInstrumentationTest extends AgentInstrumentationS
span(0) {
name "/a"
attributes {
+ "${SemanticAttributes.CODE_NAMESPACE.key}" jax.getClass().getName()
+ "${SemanticAttributes.CODE_FUNCTION.key}" "call"
}
}
}
@@ -61,6 +65,8 @@ abstract class JaxRsAnnotationsInstrumentationTest extends AgentInstrumentationS
name "${className}.call"
childOf span(0)
attributes {
+ "${SemanticAttributes.CODE_NAMESPACE.key}" obj.getClass().getName()
+ "${SemanticAttributes.CODE_FUNCTION.key}" "call"
}
}
}

MODIFY
instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-testing/src/main/groovy/JaxRsFilterTest.groovy
instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-testing/src/main/groovy/JaxRsFilterTest.groovy
@@ -8,6 +8,7 @@ import static io.opentelemetry.api.trace.SpanKind.SERVER
import static io.opentelemetry.instrumentation.test.utils.TraceUtils.runUnderServerTrace
import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification
+import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
import javax.ws.rs.container.ContainerRequestContext
import javax.ws.rs.container.ContainerRequestFilter
import javax.ws.rs.container.PreMatching
@@ -83,8 +84,15 @@ abstract class JaxRsFilterTest extends AgentInstrumentationSpecification {
span(1) {
childOf span(0)
name controllerName
- if (!runsOnServer()) {
+ if (abortPrematch) {
+ attributes {
+ "${SemanticAttributes.CODE_NAMESPACE.key}" "JaxRsFilterTest\$PrematchRequestFilter"
+ "${SemanticAttributes.CODE_FUNCTION.key}" "filter"
+ }
+ } else {
attributes {
+ "${SemanticAttributes.CODE_NAMESPACE.key}" ~/Resource[$]Test*/
+ "${SemanticAttributes.CODE_FUNCTION.key}" "hello"
}
}
}
@@ -135,9 +143,9 @@ abstract class JaxRsFilterTest extends AgentInstrumentationSpecification {
childOf span(0)
name controller1Name
kind INTERNAL
- if (!runsOnServer()) {
attributes {
- }
+ "${SemanticAttributes.CODE_NAMESPACE.key}" ~/Resource[$]Test*/
+ "${SemanticAttributes.CODE_FUNCTION.key}" "nested"
}
}
}

MODIFY
instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-testing/src/main/groovy/JaxRsHttpServerTest.groovy
instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-testing/src/main/groovy/JaxRsHttpServerTest.groovy
@@ -222,6 +222,8 @@ abstract class JaxRsHttpServerTest<S> extends HttpServerTest<S> implements Agent
}
childOf((SpanData) parent)
attributes {
+ "${SemanticAttributes.CODE_NAMESPACE.key}" "JaxRsTestResource"
+ "${SemanticAttributes.CODE_FUNCTION.key}" methodName
if (isCancelled) {
"jaxrs.canceled" true
}

JAX-RS: add code.namespace and code.function attributes (#2805)