diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/interceptor/AddingSpanAttributesInterceptorTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/interceptor/AddingSpanAttributesInterceptorTest.java index 9022f3bd1f0b9..8b3ed3ba16354 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/interceptor/AddingSpanAttributesInterceptorTest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/interceptor/AddingSpanAttributesInterceptorTest.java @@ -1,6 +1,8 @@ package io.quarkus.opentelemetry.deployment.interceptor; import static io.opentelemetry.api.trace.SpanKind.INTERNAL; +import static io.opentelemetry.semconv.incubating.CodeIncubatingAttributes.CODE_FUNCTION; +import static io.opentelemetry.semconv.incubating.CodeIncubatingAttributes.CODE_NAMESPACE; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -104,9 +106,11 @@ void withSpanAttributesTest_newSpan() { SpanData spanDataOut = spanItems.get(0); assertEquals("HelloRouter.withSpanTakesPrecedence", spanDataOut.getName()); assertEquals(INTERNAL, spanDataOut.getKind()); - assertEquals(2, spanDataOut.getAttributes().size()); + assertEquals(4, spanDataOut.getAttributes().size()); assertEquals("implicit", getAttribute(spanDataOut, "implicitName")); assertEquals("explicit", getAttribute(spanDataOut, "explicitName")); + assertEquals("withSpanTakesPrecedence", spanDataOut.getAttributes().get((CODE_FUNCTION))); + assertEquals(HelloRouter.class.getName(), spanDataOut.getAttributes().get((CODE_NAMESPACE))); } @Test diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/interceptor/WithSpanInterceptorTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/interceptor/WithSpanInterceptorTest.java index 16dac82bf1e60..b8b5495276c37 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/interceptor/WithSpanInterceptorTest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/interceptor/WithSpanInterceptorTest.java @@ -4,6 +4,8 @@ import static io.opentelemetry.api.trace.SpanKind.INTERNAL; import static io.opentelemetry.api.trace.SpanKind.SERVER; import static io.opentelemetry.api.trace.StatusCode.ERROR; +import static io.opentelemetry.semconv.incubating.CodeIncubatingAttributes.CODE_FUNCTION; +import static io.opentelemetry.semconv.incubating.CodeIncubatingAttributes.CODE_NAMESPACE; import static io.quarkus.opentelemetry.deployment.common.exporter.TestSpanExporter.getSpanByKindAndParentId; import static java.net.HttpURLConnection.HTTP_OK; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -87,17 +89,21 @@ void spanName() { void spanKind() { spanBean.spanKind(); List spanItems = spanExporter.getFinishedSpanItems(1); - assertEquals("SpanBean.spanKind", spanItems.get(0).getName()); - assertEquals(SERVER, spanItems.get(0).getKind()); + SpanData span = spanItems.get(0); + assertEquals("SpanBean.spanKind", span.getName()); + assertEquals(SERVER, span.getKind()); + assertClassMethodNames(span, SpanBean.class, "spanKind"); } @Test void spanArgs() { spanBean.spanArgs("argument"); List spanItems = spanExporter.getFinishedSpanItems(1); - assertEquals("SpanBean.spanArgs", spanItems.get(0).getName()); - assertEquals(INTERNAL, spanItems.get(0).getKind()); - assertEquals("argument", spanItems.get(0).getAttributes().get(AttributeKey.stringKey("arg"))); + SpanData span = spanItems.get(0); + assertEquals("SpanBean.spanArgs", span.getName()); + assertEquals(INTERNAL, span.getKind()); + assertEquals("argument", span.getAttributes().get(AttributeKey.stringKey("arg"))); + assertClassMethodNames(span, SpanBean.class, "spanArgs"); } @Test @@ -107,9 +113,12 @@ void spanChild() { final SpanData parent = getSpanByKindAndParentId(spans, INTERNAL, "0000000000000000"); assertEquals("SpanBean.spanChild", parent.getName()); + assertClassMethodNames(parent, SpanBean.class, "spanChild"); final SpanData child = getSpanByKindAndParentId(spans, INTERNAL, parent.getSpanId()); assertEquals("SpanChildBean.spanChild", child.getName()); + assertClassMethodNames(child, SpanChildBean.class, "spanChild"); + } @Test @@ -120,7 +129,7 @@ void spanCdiRest() { final SpanData parent = getSpanByKindAndParentId(spans, INTERNAL, "0000000000000000"); final SpanData child = getSpanByKindAndParentId(spans, INTERNAL, parent.getSpanId()); final SpanData client = getSpanByKindAndParentId(spans, CLIENT, child.getSpanId()); - final SpanData server = getSpanByKindAndParentId(spans, SERVER, client.getSpanId()); + getSpanByKindAndParentId(spans, SERVER, client.getSpanId()); } @Test @@ -134,12 +143,14 @@ void spanWithException() { }); } List spanItems = spanExporter.getFinishedSpanItems(1); - assertEquals("SpanBean.spanWithException", spanItems.get(0).getName()); - assertEquals(INTERNAL, spanItems.get(0).getKind()); - assertEquals(ERROR, spanItems.get(0).getStatus().getStatusCode()); - assertEquals(1, spanItems.get(0).getEvents().size()); + SpanData span = spanItems.get(0); + assertEquals("SpanBean.spanWithException", span.getName()); + assertEquals(INTERNAL, span.getKind()); + assertEquals(ERROR, span.getStatus().getStatusCode()); + assertEquals(1, span.getEvents().size()); assertEquals("spanWithException for tests", - ((ExceptionEventData) spanItems.get(0).getEvents().get(0)).getException().getMessage()); + ((ExceptionEventData) span.getEvents().get(0)).getException().getMessage()); + assertClassMethodNames(span, SpanBean.class, "spanWithException"); } @Test @@ -150,6 +161,7 @@ void spanUni() { final SpanData parent = getSpanByKindAndParentId(spans, INTERNAL, "0000000000000000"); assertEquals("withSpanAndUni", parent.getName()); assertEquals(StatusCode.UNSET, parent.getStatus().getStatusCode()); + assertClassMethodNames(parent, SpanBean.class, "spanUni"); } @Test @@ -163,12 +175,14 @@ void spanUniWithException() { }); } List spanItems = spanExporter.getFinishedSpanItems(1); - assertEquals("withSpanAndUni", spanItems.get(0).getName()); - assertEquals(INTERNAL, spanItems.get(0).getKind()); - assertEquals(ERROR, spanItems.get(0).getStatus().getStatusCode()); - assertEquals(1, spanItems.get(0).getEvents().size()); + SpanData span = spanItems.get(0); + assertEquals("withSpanAndUni", span.getName()); + assertEquals(INTERNAL, span.getKind()); + assertEquals(ERROR, span.getStatus().getStatusCode()); + assertEquals(1, span.getEvents().size()); assertEquals("hello Uni", - ((ExceptionEventData) spanItems.get(0).getEvents().get(0)).getException().getMessage()); + ((ExceptionEventData) span.getEvents().get(0)).getException().getMessage()); + assertClassMethodNames(span, SpanBean.class, "spanUniWithException"); } @Test @@ -179,6 +193,7 @@ void spanMulti() { final SpanData parent = getSpanByKindAndParentId(spans, INTERNAL, "0000000000000000"); assertEquals("withSpanAndMulti", parent.getName()); assertEquals(StatusCode.UNSET, parent.getStatus().getStatusCode()); + assertClassMethodNames(parent, SpanBean.class, "spanMulti"); } @Test @@ -198,6 +213,12 @@ void spanMultiWithException() { assertEquals(1, spanItems.get(0).getEvents().size()); assertEquals("hello Multi", ((ExceptionEventData) spanItems.get(0).getEvents().get(0)).getException().getMessage()); + assertClassMethodNames(spanItems.get(0), SpanBean.class, "spanMultiWithException"); + } + + private void assertClassMethodNames(SpanData span, Class clazz, String method) { + assertEquals(method, span.getAttributes().get((CODE_FUNCTION))); + assertEquals(clazz.getName(), span.getAttributes().get((CODE_NAMESPACE))); } @ApplicationScoped diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/OpenTelemetryHttpCDITest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/OpenTelemetryHttpCDITest.java index 379e5ef80cd9e..bb9dbaca6d19d 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/OpenTelemetryHttpCDITest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/OpenTelemetryHttpCDITest.java @@ -89,6 +89,7 @@ void withSpan() { final SpanData withSpan = getSpanByKindAndParentId(spans, INTERNAL, server.getSpanId()); assertEquals("withSpan", withSpan.getName()); + assertEquals(2, withSpan.getAttributes().size()); final SpanData bean = getSpanByKindAndParentId(spans, INTERNAL, withSpan.getSpanId()); assertEquals("HelloBean.hello", bean.getName()); diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/cdi/WithSpanInterceptor.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/cdi/WithSpanInterceptor.java index 0050a849d4adb..6e2c009a993d5 100644 --- a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/cdi/WithSpanInterceptor.java +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/cdi/WithSpanInterceptor.java @@ -1,5 +1,7 @@ package io.quarkus.opentelemetry.runtime.tracing.cdi; +import static io.opentelemetry.semconv.incubating.CodeIncubatingAttributes.CODE_FUNCTION; +import static io.opentelemetry.semconv.incubating.CodeIncubatingAttributes.CODE_NAMESPACE; import static io.quarkus.opentelemetry.runtime.config.build.OTelBuildConfig.INSTRUMENTATION_NAME; import java.lang.annotation.Annotation; @@ -15,6 +17,7 @@ import jakarta.interceptor.Interceptor; import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; @@ -23,6 +26,7 @@ import io.opentelemetry.instrumentation.api.annotation.support.MethodSpanAttributesExtractor; import io.opentelemetry.instrumentation.api.annotation.support.ParameterAttributeNamesExtractor; import io.opentelemetry.instrumentation.api.incubator.semconv.util.SpanNames; +import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder; import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor; @@ -52,7 +56,8 @@ public WithSpanInterceptor(final OpenTelemetry openTelemetry, final OTelRuntimeC new WithSpanParameterAttributeNamesExtractor(), MethodRequest::getArgs); - this.instrumenter = builder.addAttributesExtractor(attributesExtractor) + this.instrumenter = builder.addAttributesExtractor(ClassMethodNameAttributesExtractor.INSTANCE) + .addAttributesExtractor(attributesExtractor) .buildInstrumenter(new SpanKindExtractor() { @Override public SpanKind extract(MethodRequest methodRequest) { @@ -158,6 +163,26 @@ public void accept(Object o, Throwable throwable) { } } + private static final class ClassMethodNameAttributesExtractor implements AttributesExtractor { + private static final ClassMethodNameAttributesExtractor INSTANCE = new ClassMethodNameAttributesExtractor(); + + private ClassMethodNameAttributesExtractor() { + //no-op + } + + @Override + public void onStart(AttributesBuilder attributesBuilder, Context context, MethodRequest methodRequest) { + attributesBuilder.put(CODE_NAMESPACE, methodRequest.getMethod().getDeclaringClass().getName()); + attributesBuilder.put(CODE_FUNCTION, methodRequest.getMethod().getName()); + } + + @Override + public void onEnd(AttributesBuilder attributesBuilder, Context context, MethodRequest methodRequest, Void unused, + Throwable throwable) { + // no-op + } + } + private static boolean isUni(Class clazz) { return Uni.class.isAssignableFrom(clazz); } diff --git a/integration-tests/rest-client-reactive/src/main/resources/application.properties b/integration-tests/rest-client-reactive/src/main/resources/application.properties index f8c5070ef3a4c..898f549738388 100644 --- a/integration-tests/rest-client-reactive/src/main/resources/application.properties +++ b/integration-tests/rest-client-reactive/src/main/resources/application.properties @@ -7,4 +7,4 @@ io.quarkus.it.rest.client.main.ParamClient/mp-rest/url=${test.url} quarkus.rest-client.logging.scope=request-response # speed up build -quarkus.otel.bsp.schedule.delay=100 +quarkus.otel.bsp.schedule.delay=10 diff --git a/integration-tests/rest-client-reactive/src/test/java/io/quarkus/it/rest/client/BasicTest.java b/integration-tests/rest-client-reactive/src/test/java/io/quarkus/it/rest/client/BasicTest.java index 7d575565b53f0..74ece3afbb356 100644 --- a/integration-tests/rest-client-reactive/src/test/java/io/quarkus/it/rest/client/BasicTest.java +++ b/integration-tests/rest-client-reactive/src/test/java/io/quarkus/it/rest/client/BasicTest.java @@ -14,7 +14,9 @@ import java.util.stream.Collectors; import org.awaitility.Awaitility; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import io.opentelemetry.api.trace.SpanId; @@ -38,6 +40,13 @@ public class BasicTest { @TestHTTPResource("/params") String paramsUrl; + @BeforeEach + @AfterEach + public void clear() { + // Reset captured traces + RestAssured.given().when().get("/export-clear").then().statusCode(200); + } + @Test public void shouldMakeTextRequest() { Response response = with().body(helloUrl).post("/call-hello-client"); @@ -139,9 +148,6 @@ void shouldApplyInterfaceLevelInterceptorBinding() { @Test void shouldCreateClientSpans() { - // Reset captured traces - RestAssured.given().when().get("/export-clear").then().statusCode(200); - Response response = with().body(helloUrl).post("/call-hello-client-trace"); assertThat(response.asString()).isEqualTo("Hello, MaryMaryMary"); @@ -179,7 +185,7 @@ void shouldCreateClientSpans() { Assertions.assertNotNull(initialServerSpan.get("attr_client.address")); Assertions.assertNotNull(initialServerSpan.get("attr_user_agent.original")); - Awaitility.await().atMost(Duration.ofSeconds(30)) + Awaitility.await().atMost(Duration.ofSeconds(5)) .until(() -> getClientSpansFromFullUrl("POST", "http://localhost:8081/hello?count=3").size() > 0); spans = getClientSpansFromFullUrl("POST", "http://localhost:8081/hello?count=3"); @@ -210,10 +216,10 @@ void shouldCreateClientSpans() { clientSpanId = (String) clientSpan.get("spanId"); - Awaitility.await().atMost(Duration.ofSeconds(30)) + Awaitility.await().atMost(Duration.ofSeconds(5)) .until(() -> getServerSpansFromPath("POST /hello", "/hello").size() > 0); spans = getServerSpansFromPath("POST /hello", "/hello"); - Assertions.assertEquals(1, spans.size()); + Assertions.assertEquals(1, spans.size(), "found: " + spans); final Map serverSpanClientSide = spans.get(0); Assertions.assertNotNull(serverSpanClientSide);