diff --git a/docs/src/main/asciidoc/opentelemetry-tracing.adoc b/docs/src/main/asciidoc/opentelemetry-tracing.adoc index f456d8dc8a221..4c5944576ee1d 100644 --- a/docs/src/main/asciidoc/opentelemetry-tracing.adoc +++ b/docs/src/main/asciidoc/opentelemetry-tracing.adoc @@ -590,81 +590,30 @@ quarkus.otel.instrument.rest=false quarkus.otel.instrument.resteasy=false ---- -=== Disable specific REST endpoints +[[disabling-traces-for-app-endpoints]] +=== Disabling Specific Traces for Application Endpoints -There are two ways to disable tracing for a specific REST endpoint. +You can use the `quarkus.otel.traces.suppress-application-uris` property to exclude specific endpoints from being traced. -You can use the `@io.quarkus.opentelemetry.runtime.tracing.Traceless` (or simply `@Traceless`) annotation to disable tracing for a specific endpoint. - -Examples: - -==== `@Traceless` annotation on a class - -[source,java] -.PingResource.java ----- -@Path("/health") -public class PingResource { - - @Path("/ping") - public String ping() { - return "pong"; - } -} ----- - -When the `@Traceless` annotation is placed on a class, all methods annotated with `@Path` will be excluded from tracing. - -==== `@Traceless` annotation on a method - -[source,java] -.TraceResource.java ----- -@Path("/trace") -@Traceless -public class TraceResource { - - @Path("/no") - @GET - @Traceless - public String noTrace() { - return "no"; - } - - @Path("/yes") - @GET - public String withTrace() { - return "yes"; - } -} ----- - -In the example above, only `GET /trace/yes` will be included in tracing. - -==== Disable using configuration - -If you do not want to modify the source code, you can use your `application.properties` to disable a specific endpoint through the `quarkus.otel.traces.suppress-application-uris` property. - -Example: +==== Example Configuration [source,properties] -.application.properties ---- +# application.properties quarkus.otel.traces.suppress-application-uris=trace,ping,people* ---- This configuration will: -- Disable tracing for the `/trace` URI; -- Disable tracing for the `/ping` URI; -- Disable tracing for the `/people` URI and all other URIs under it, e.g., `/people/1`, `/people/1/cars`. +- Disable tracing for the `/trace` URI. +- Disable tracing for the `/ping` URI. +- Disable tracing for the `/people` URI and all subpaths, such as `/people/1` and `/people/1/cars`. [NOTE] ==== -If you are using `quarkus.http.root-path`, you need to remember to include the root path in the configuration. Unlike `@Traceless`, this configuration does not automatically add the root path. +If you are using `quarkus.http.root-path`, ensure you include the root path in the configuration. ==== - [[configuration-reference]] == OpenTelemetry Configuration Reference diff --git a/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/tracing/DropApplicationUrisBuildItem.java b/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/tracing/DropApplicationUrisBuildItem.java deleted file mode 100644 index 7d06ca4f4c66b..0000000000000 --- a/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/tracing/DropApplicationUrisBuildItem.java +++ /dev/null @@ -1,19 +0,0 @@ -package io.quarkus.opentelemetry.deployment.tracing; - -import io.quarkus.builder.item.MultiBuildItem; - -/** - * Represents an application uri that must be ignored for tracing. - */ -public final class DropApplicationUrisBuildItem extends MultiBuildItem { - - private final String uri; - - public DropApplicationUrisBuildItem(String uri) { - this.uri = uri; - } - - public String uri() { - return uri; - } -} diff --git a/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/tracing/TracerProcessor.java b/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/tracing/TracerProcessor.java index c516f1c20e2f1..c7576783e9d25 100644 --- a/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/tracing/TracerProcessor.java +++ b/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/tracing/TracerProcessor.java @@ -10,7 +10,6 @@ import java.util.Collection; import java.util.HashSet; import java.util.List; -import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.function.BooleanSupplier; @@ -18,10 +17,8 @@ import jakarta.enterprise.inject.spi.EventContext; import jakarta.inject.Singleton; -import org.eclipse.microprofile.config.ConfigProvider; import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.AnnotationTarget; -import org.jboss.jandex.ClassInfo; import org.jboss.jandex.DotName; import org.jboss.jandex.FieldInfo; import org.jboss.jandex.IndexView; @@ -56,7 +53,6 @@ import io.quarkus.opentelemetry.runtime.config.build.OTelBuildConfig; import io.quarkus.opentelemetry.runtime.config.build.OTelBuildConfig.SecurityEvents.SecurityEventType; import io.quarkus.opentelemetry.runtime.tracing.DelayedAttributes; -import io.quarkus.opentelemetry.runtime.tracing.Traceless; import io.quarkus.opentelemetry.runtime.tracing.TracerRecorder; import io.quarkus.opentelemetry.runtime.tracing.cdi.TracerProducer; import io.quarkus.opentelemetry.runtime.tracing.security.EndUserSpanProcessor; @@ -73,7 +69,6 @@ public class TracerProcessor { private static final DotName SPAN_EXPORTER = DotName.createSimple(SpanExporter.class.getName()); private static final DotName SPAN_PROCESSOR = DotName.createSimple(SpanProcessor.class.getName()); private static final DotName TEXT_MAP_PROPAGATOR = DotName.createSimple(TextMapPropagator.class.getName()); - private static final DotName TRACELESS = DotName.createSimple(Traceless.class.getName()); private static final DotName PATH = DotName.createSimple("jakarta.ws.rs.Path"); @BuildStep @@ -137,29 +132,14 @@ UnremovableBeanBuildItem ensureProducersAreRetained( return new UnremovableBeanBuildItem(new UnremovableBeanBuildItem.BeanClassNamesExclusion(retainProducers)); } - @BuildStep - void dropApplicationUris( - CombinedIndexBuildItem combinedIndexBuildItem, - BuildProducer uris) { - String rootPath = ConfigProvider.getConfig().getOptionalValue("quarkus.http.root-path", String.class).orElse("/"); - IndexView index = combinedIndexBuildItem.getIndex(); - Collection annotations = index.getAnnotations(TRACELESS); - Set tracelessUris = generateTracelessUris(annotations.stream().toList(), rootPath); - for (String uri : tracelessUris) { - uris.produce(new DropApplicationUrisBuildItem(uri)); - } - } - @BuildStep void dropNames( Optional frameworkEndpoints, Optional staticResources, BuildProducer dropNonApplicationUris, - BuildProducer dropStaticResources, - List applicationUris) { + BuildProducer dropStaticResources) { - List nonApplicationUris = new ArrayList<>( - applicationUris.stream().map(DropApplicationUrisBuildItem::uri).toList()); + List nonApplicationUris = new ArrayList<>(); // Drop framework paths frameworkEndpoints.ifPresent( @@ -192,77 +172,6 @@ void dropNames( dropStaticResources.produce(new DropStaticResourcesBuildItem(resources)); } - private Set generateTracelessUris(final List annotations, final String rootPath) { - final Set applicationUris = new HashSet<>(); - for (AnnotationInstance annotation : annotations) { - AnnotationTarget.Kind kind = annotation.target().kind(); - - switch (kind) { - case CLASS -> { - AnnotationInstance classAnnotated = annotation.target().asClass().annotations() - .stream().filter(TracerProcessor::isClassAnnotatedWithPath).findFirst().orElse(null); - - if (Objects.isNull(classAnnotated)) { - throw new IllegalStateException( - String.format( - "The class '%s' is annotated with @Traceless but is missing the required @Path annotation. " - + - "Please ensure that the class is properly annotated with @Path annotation.", - annotation.target().asClass().name())); - } - - String classPath = classAnnotated.value().asString(); - String finalPath = combinePaths(rootPath, classPath); - - if (containsPathExpression(finalPath)) { - applicationUris.add(sanitizeForTraceless(finalPath) + "*"); - continue; - } - - applicationUris.add(finalPath + "*"); - applicationUris.add(finalPath); - } - case METHOD -> { - ClassInfo classInfo = annotation.target().asMethod().declaringClass(); - - AnnotationInstance possibleClassAnnotatedWithPath = classInfo.asClass() - .annotations() - .stream() - .filter(TracerProcessor::isClassAnnotatedWithPath) - .findFirst() - .orElse(null); - - if (Objects.isNull(possibleClassAnnotatedWithPath)) { - throw new IllegalStateException( - String.format( - "The class '%s' contains a method annotated with @Traceless but is missing the required @Path annotation. " - + - "Please ensure that the class is properly annotated with @Path annotation.", - classInfo.name())); - } - - String finalPath; - String classPath = possibleClassAnnotatedWithPath.value().asString(); - AnnotationInstance possibleMethodAnnotatedWithPath = annotation.target().annotation(PATH); - if (possibleMethodAnnotatedWithPath != null) { - String methodValue = possibleMethodAnnotatedWithPath.value().asString(); - finalPath = combinePaths(rootPath, combinePaths(classPath, methodValue)); - } else { - finalPath = combinePaths(rootPath, classPath); - } - - if (containsPathExpression(finalPath)) { - applicationUris.add(sanitizeForTraceless(finalPath) + "*"); - continue; - } - - applicationUris.add(finalPath); - } - } - } - return applicationUris; - } - @BuildStep @Record(ExecutionTime.STATIC_INIT) SyntheticBeanBuildItem setupDelayedAttribute(TracerRecorder recorder, ApplicationInfoBuildItem appInfo) { diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryTracelessOnMethodWithoutPathTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryTracelessOnMethodWithoutPathTest.java deleted file mode 100644 index fadb21d0a5690..0000000000000 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryTracelessOnMethodWithoutPathTest.java +++ /dev/null @@ -1,45 +0,0 @@ -package io.quarkus.opentelemetry.deployment; - -import static org.assertj.core.api.Assertions.assertThat; - -import jakarta.ws.rs.core.Response; - -import org.assertj.core.api.Assertions; -import org.jboss.shrinkwrap.api.ShrinkWrap; -import org.jboss.shrinkwrap.api.asset.StringAsset; -import org.jboss.shrinkwrap.api.spec.JavaArchive; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; - -import io.quarkus.opentelemetry.deployment.common.exporter.InMemoryExporter; -import io.quarkus.opentelemetry.runtime.tracing.Traceless; -import io.quarkus.test.QuarkusUnitTest; - -public class OpenTelemetryTracelessOnMethodWithoutPathTest { - @RegisterExtension - static final QuarkusUnitTest TEST = new QuarkusUnitTest().setArchiveProducer( - () -> ShrinkWrap.create(JavaArchive.class) - .addPackage(InMemoryExporter.class.getPackage()) - .addAsResource(new StringAsset( - ""), "application.properties") - .addClasses(TracelessWithoutPath.class)) - .assertException(throwable -> { - assertThat(throwable).isInstanceOf(IllegalStateException.class) - .hasMessageContaining("The class") - .hasMessageContaining( - "contains a method annotated with @Traceless but is missing the required @Path annotation. Please ensure that the class is properly annotated with @Path annotation."); - }); - - @Test - void testClassAnnotatedWithTracelessMissingPath() { - Assertions.fail(); - } - - public static class TracelessWithoutPath { - - @Traceless - public Response hello() { - return Response.ok("hello").build(); - } - } -} diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryTracelessTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryTracelessTest.java deleted file mode 100644 index bd61aacf40a31..0000000000000 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryTracelessTest.java +++ /dev/null @@ -1,141 +0,0 @@ -package io.quarkus.opentelemetry.deployment; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.hamcrest.Matchers.is; - -import java.util.List; - -import jakarta.inject.Inject; - -import org.jboss.shrinkwrap.api.ShrinkWrap; -import org.jboss.shrinkwrap.api.asset.StringAsset; -import org.jboss.shrinkwrap.api.spec.JavaArchive; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; - -import io.opentelemetry.sdk.trace.data.SpanData; -import io.quarkus.opentelemetry.deployment.common.exporter.InMemoryExporter; -import io.quarkus.opentelemetry.deployment.common.exporter.InMemoryMetricExporterProvider; -import io.quarkus.opentelemetry.deployment.common.traces.TraceMeResource; -import io.quarkus.opentelemetry.deployment.common.traces.TracelessClassLevelResource; -import io.quarkus.opentelemetry.deployment.common.traces.TracelessHelloResource; -import io.quarkus.test.QuarkusUnitTest; -import io.restassured.RestAssured; - -public class OpenTelemetryTracelessTest { - @RegisterExtension - static final QuarkusUnitTest TEST = new QuarkusUnitTest().setArchiveProducer( - () -> ShrinkWrap.create(JavaArchive.class) - .addPackage(InMemoryExporter.class.getPackage()) - .addAsResource("resource-config/application.properties", "application.properties") - .addAsResource( - "META-INF/services-config/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider", - "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider") - .addAsResource(new StringAsset(InMemoryMetricExporterProvider.class.getCanonicalName()), - "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider") - .addClasses(TracelessHelloResource.class, TracelessClassLevelResource.class, TraceMeResource.class)); - - @Inject - InMemoryExporter exporter; - - @BeforeEach - void setup() { - exporter.reset(); - } - - @Test - @DisplayName("Should not trace when the method @Path uses @PathParam") - void testingWithPathParam() { - RestAssured.when() - .get("/hello/mask/1").then() - .statusCode(200) - .body(is("mask-1")); - - RestAssured.when() - .get("/trace-me").then() - .statusCode(200) - .body(is("trace-me")); - - List spans = exporter.getSpanExporter().getFinishedSpanItems(1); - - assertThat(spans) - .hasSize(1) - .satisfiesOnlyOnce(span -> assertThat(span.getName()).containsOnlyOnce("trace-me")); - } - - @Test - @DisplayName("Should not trace when the annotation @Traceless is at method level") - void testingTracelessHelloHi() { - - RestAssured.when() - .get("/hello").then() - .statusCode(200) - .body(is("hello")); - - RestAssured.when() - .get("/hello/hi").then() - .statusCode(200) - .body(is("hi")); - - RestAssured.when() - .get("/trace-me").then() - .statusCode(200) - .body(is("trace-me")); - - List spans = exporter.getSpanExporter().getFinishedSpanItems(1); - assertThat(spans) - .hasSize(1) - .satisfiesOnlyOnce(span -> assertThat(span.getName()).containsOnlyOnce("trace-me")); - } - - @Test - @DisplayName("Should not trace when the method @Path is without '/'") - void testingHelloNoSlash() { - RestAssured.when() - .get("/hello/no-slash").then() - .statusCode(200) - .body(is("no-slash")); - - RestAssured.when() - .get("/trace-me").then() - .statusCode(200) - .body(is("trace-me")); - - List spans = exporter.getSpanExporter().getFinishedSpanItems(1); - assertThat(spans) - .hasSize(1) - .satisfiesOnlyOnce(span -> assertThat(span.getName()).containsOnlyOnce("trace-me")); - } - - @Test - @DisplayName("Should not trace when the annotation is at class level") - void testingTracelessAtClassLevel() { - - RestAssured.when() - .get("class-level").then() - .statusCode(200) - .body(is("class-level")); - - RestAssured.when() - .get("/class-level/first-method").then() - .statusCode(200) - .body(is("first-method")); - - RestAssured.when() - .get("/class-level/second-method").then() - .statusCode(200) - .body(is("second-method")); - - RestAssured.when() - .get("/trace-me").then() - .statusCode(200) - .body(is("trace-me")); - - List spans = exporter.getSpanExporter().getFinishedSpanItems(1); - assertThat(spans) - .hasSize(1) - .satisfiesOnlyOnce(span -> assertThat(span.getName()).containsOnlyOnce("trace-me")); - } -} diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryTracelessWithRootPathTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryTracelessWithRootPathTest.java deleted file mode 100644 index 03c442a1e46ed..0000000000000 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryTracelessWithRootPathTest.java +++ /dev/null @@ -1,146 +0,0 @@ -package io.quarkus.opentelemetry.deployment; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.hamcrest.Matchers.is; - -import java.util.List; - -import jakarta.inject.Inject; - -import org.jboss.shrinkwrap.api.ShrinkWrap; -import org.jboss.shrinkwrap.api.asset.StringAsset; -import org.jboss.shrinkwrap.api.spec.JavaArchive; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; - -import io.opentelemetry.sdk.trace.data.SpanData; -import io.quarkus.opentelemetry.deployment.common.exporter.InMemoryExporter; -import io.quarkus.opentelemetry.deployment.common.exporter.InMemoryMetricExporterProvider; -import io.quarkus.opentelemetry.deployment.common.traces.TraceMeResource; -import io.quarkus.opentelemetry.deployment.common.traces.TracelessClassLevelResource; -import io.quarkus.opentelemetry.deployment.common.traces.TracelessHelloResource; -import io.quarkus.test.QuarkusUnitTest; -import io.restassured.RestAssured; - -public class OpenTelemetryTracelessWithRootPathTest { - @RegisterExtension - static final QuarkusUnitTest TEST = new QuarkusUnitTest().setArchiveProducer( - () -> ShrinkWrap.create(JavaArchive.class) - .addPackage(InMemoryExporter.class.getPackage()) - .addAsResource("resource-config/application.properties", "application.properties") - .addAsResource( - "META-INF/services-config/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider", - "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider") - .addAsResource(new StringAsset(InMemoryMetricExporterProvider.class.getCanonicalName()), - "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider") - .addClasses(TracelessHelloResource.class, TracelessClassLevelResource.class, TraceMeResource.class)) - .overrideConfigKey("quarkus.http.root-path", "/api"); - - @Inject - InMemoryExporter exporter; - - @BeforeEach - void setup() { - exporter.reset(); - } - - @Test - @DisplayName("Should not trace when the annotation @Traceless is at method level") - void testingTracelessHelloHi() { - - RestAssured.when() - .get("/hello").then() - .statusCode(200) - .body(is("hello")); - - RestAssured.when() - .get("/hello/hi").then() - .statusCode(200) - .body(is("hi")); - - RestAssured.when() - .get("/trace-me").then() - .statusCode(200) - .body(is("trace-me")); - - List spans = exporter.getSpanExporter().getFinishedSpanItems(1); - - assertThat(spans) - .hasSize(1) - .satisfiesOnlyOnce(span -> assertThat(span.getName()).containsOnlyOnce("trace-me")); - } - - @Test - @DisplayName("Should not trace when the method @Path uses @PathParam") - void testingWithPathParam() { - RestAssured.when() - .get("/hello/mask/1").then() - .statusCode(200) - .body(is("mask-1")); - - RestAssured.when() - .get("/trace-me").then() - .statusCode(200) - .body(is("trace-me")); - - List spans = exporter.getSpanExporter().getFinishedSpanItems(1); - - assertThat(spans) - .hasSize(1) - .satisfiesOnlyOnce(span -> assertThat(span.getName()).containsOnlyOnce("trace-me")); - } - - @Test - @DisplayName("Should not trace when the method @Path is without '/'") - void testingHelloNoSlash() { - RestAssured.when() - .get("/hello/no-slash").then() - .statusCode(200) - .body(is("no-slash")); - - RestAssured.when() - .get("/trace-me").then() - .statusCode(200) - .body(is("trace-me")); - - List spans = exporter.getSpanExporter().getFinishedSpanItems(1); - - assertThat(spans) - .hasSize(1) - .satisfiesOnlyOnce(span -> assertThat(span.getName()).containsOnlyOnce("trace-me")); - } - - @Test - @DisplayName("Should not trace when the annotation is at class level") - void testingTracelessAtClassLevel() { - - RestAssured.when() - .get("class-level").then() - .statusCode(200) - .body(is("class-level")); - - RestAssured.when() - .get("/class-level/first-method").then() - .statusCode(200) - .body(is("first-method")); - - RestAssured.when() - .get("/class-level/second-method").then() - .statusCode(200) - .body(is("second-method")); - - RestAssured.when() - .get("/trace-me").then() - .statusCode(200) - .body(is("trace-me")); - - List spans = exporter.getSpanExporter().getFinishedSpanItems(1); - - assertThat(spans) - .hasSize(1) - .satisfiesOnlyOnce(span -> assertThat(span.getName()).containsOnlyOnce("trace-me")); - } - -} diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryTracelessWithoutPathTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryTracelessWithoutPathTest.java deleted file mode 100644 index 9da23e2199eaf..0000000000000 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryTracelessWithoutPathTest.java +++ /dev/null @@ -1,40 +0,0 @@ -package io.quarkus.opentelemetry.deployment; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.assertj.core.api.Assertions; -import org.jboss.shrinkwrap.api.ShrinkWrap; -import org.jboss.shrinkwrap.api.asset.StringAsset; -import org.jboss.shrinkwrap.api.spec.JavaArchive; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; - -import io.quarkus.opentelemetry.deployment.common.exporter.InMemoryExporter; -import io.quarkus.opentelemetry.runtime.tracing.Traceless; -import io.quarkus.test.QuarkusUnitTest; - -public class OpenTelemetryTracelessWithoutPathTest { - @RegisterExtension - static final QuarkusUnitTest TEST = new QuarkusUnitTest().setArchiveProducer( - () -> ShrinkWrap.create(JavaArchive.class) - .addPackage(InMemoryExporter.class.getPackage()) - .addAsResource(new StringAsset( - ""), "application.properties") - .addClasses(TracelessWithoutPath.class)) - .assertException(throwable -> { - assertThat(throwable).isInstanceOf(IllegalStateException.class) - .hasMessageContaining("The class") - .hasMessageContaining( - "is annotated with @Traceless but is missing the required @Path annotation. Please ensure that the class is properly annotated with @Path annotation."); - }); - - @Test - void testClassAnnotatedWithTracelessMissingPath() { - Assertions.fail(); - } - - @Traceless - public static class TracelessWithoutPath { - - } -} diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/common/traces/TracelessClassLevelResource.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/common/traces/TracelessClassLevelResource.java deleted file mode 100644 index b27e2da3851c9..0000000000000 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/common/traces/TracelessClassLevelResource.java +++ /dev/null @@ -1,36 +0,0 @@ -package io.quarkus.opentelemetry.deployment.common.traces; - -import jakarta.inject.Inject; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.Path; - -import io.opentelemetry.api.metrics.Meter; -import io.quarkus.opentelemetry.runtime.tracing.Traceless; - -@Path("/class-level") -@Traceless -public class TracelessClassLevelResource { - - @Inject - Meter meter; - - @GET - public String classLevel() { - meter.counterBuilder("class-level").build().add(1); - return "class-level"; - } - - @GET - @Path("/first-method") - public String firstMethod() { - meter.counterBuilder("first-method").build().add(1); - return "first-method"; - } - - @Path("/second-method") - @GET - public String secondMethod() { - meter.counterBuilder("second-method").build().add(1); - return "second-method"; - } -} diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/common/traces/TracelessHelloResource.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/common/traces/TracelessHelloResource.java deleted file mode 100644 index f9824313798b4..0000000000000 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/common/traces/TracelessHelloResource.java +++ /dev/null @@ -1,47 +0,0 @@ -package io.quarkus.opentelemetry.deployment.common.traces; - -import jakarta.inject.Inject; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.PathParam; - -import io.opentelemetry.api.metrics.Meter; -import io.quarkus.opentelemetry.runtime.tracing.Traceless; - -@Path("/hello") -public class TracelessHelloResource { - - @Inject - Meter meter; - - @GET - @Traceless - public String hello() { - meter.counterBuilder("hello").build().add(1); - return "hello"; - } - - @Path("/hi") - @GET - @Traceless - public String hi() { - meter.counterBuilder("hi").build().add(1); - return "hi"; - } - - @Path("no-slash") - @GET - @Traceless - public String noSlash() { - meter.counterBuilder("no-slash").build().add(1); - return "no-slash"; - } - - @GET - @Path("/mask/{number}") - @Traceless - public String mask(@PathParam("number") String number) { - meter.counterBuilder("mask").build().add(Integer.parseInt(number)); - return "mask-" + number; - } -} diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/OpenTelemetryTracelessTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/OpenTelemetryTracelessTest.java deleted file mode 100644 index 42f91ca3c7288..0000000000000 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/OpenTelemetryTracelessTest.java +++ /dev/null @@ -1,141 +0,0 @@ -package io.quarkus.opentelemetry.deployment.traces; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.hamcrest.Matchers.is; - -import java.util.List; - -import jakarta.inject.Inject; - -import org.jboss.shrinkwrap.api.ShrinkWrap; -import org.jboss.shrinkwrap.api.asset.StringAsset; -import org.jboss.shrinkwrap.api.spec.JavaArchive; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; - -import io.opentelemetry.sdk.trace.data.SpanData; -import io.quarkus.opentelemetry.deployment.common.exporter.InMemoryExporter; -import io.quarkus.opentelemetry.deployment.common.exporter.InMemoryMetricExporterProvider; -import io.quarkus.opentelemetry.deployment.common.traces.TraceMeResource; -import io.quarkus.opentelemetry.deployment.common.traces.TracelessClassLevelResource; -import io.quarkus.opentelemetry.deployment.common.traces.TracelessHelloResource; -import io.quarkus.test.QuarkusUnitTest; -import io.restassured.RestAssured; - -public class OpenTelemetryTracelessTest { - @RegisterExtension - static final QuarkusUnitTest TEST = new QuarkusUnitTest().setArchiveProducer( - () -> ShrinkWrap.create(JavaArchive.class) - .addPackage(InMemoryExporter.class.getPackage()) - .addAsResource("resource-config/application.properties", "application.properties") - .addAsResource( - "META-INF/services-config/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider", - "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider") - .addAsResource(new StringAsset(InMemoryMetricExporterProvider.class.getCanonicalName()), - "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider") - .addClasses(TracelessHelloResource.class, TracelessClassLevelResource.class, TraceMeResource.class)); - - @Inject - InMemoryExporter exporter; - - @BeforeEach - void setup() { - exporter.reset(); - } - - @Test - @DisplayName("Should not trace when the method @Path uses @PathParam") - void testingWithPathParam() { - RestAssured.when() - .get("/hello/mask/1").then() - .statusCode(200) - .body(is("mask-1")); - - RestAssured.when() - .get("/trace-me").then() - .statusCode(200) - .body(is("trace-me")); - - List spans = exporter.getSpanExporter().getFinishedSpanItems(1); - - assertThat(spans) - .hasSize(1) - .satisfiesOnlyOnce(span -> assertThat(span.getName()).containsOnlyOnce("trace-me")); - } - - @Test - @DisplayName("Should not trace when the annotation @Traceless is at method level") - void testingTracelessHelloHi() { - - RestAssured.when() - .get("/hello").then() - .statusCode(200) - .body(is("hello")); - - RestAssured.when() - .get("/hello/hi").then() - .statusCode(200) - .body(is("hi")); - - RestAssured.when() - .get("/trace-me").then() - .statusCode(200) - .body(is("trace-me")); - - List spans = exporter.getSpanExporter().getFinishedSpanItems(1); - assertThat(spans) - .hasSize(1) - .satisfiesOnlyOnce(span -> assertThat(span.getName()).containsOnlyOnce("trace-me")); - } - - @Test - @DisplayName("Should not trace when the method @Path is without '/'") - void testingHelloNoSlash() { - RestAssured.when() - .get("/hello/no-slash").then() - .statusCode(200) - .body(is("no-slash")); - - RestAssured.when() - .get("/trace-me").then() - .statusCode(200) - .body(is("trace-me")); - - List spans = exporter.getSpanExporter().getFinishedSpanItems(1); - assertThat(spans) - .hasSize(1) - .satisfiesOnlyOnce(span -> assertThat(span.getName()).containsOnlyOnce("trace-me")); - } - - @Test - @DisplayName("Should not trace when the annotation is at class level") - void testingTracelessAtClassLevel() { - - RestAssured.when() - .get("class-level").then() - .statusCode(200) - .body(is("class-level")); - - RestAssured.when() - .get("/class-level/first-method").then() - .statusCode(200) - .body(is("first-method")); - - RestAssured.when() - .get("/class-level/second-method").then() - .statusCode(200) - .body(is("second-method")); - - RestAssured.when() - .get("/trace-me").then() - .statusCode(200) - .body(is("trace-me")); - - List spans = exporter.getSpanExporter().getFinishedSpanItems(1); - assertThat(spans) - .hasSize(1) - .satisfiesOnlyOnce(span -> assertThat(span.getName()).containsOnlyOnce("trace-me")); - } -} diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/OpenTelemetryTracelessWithRootPathTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/OpenTelemetryTracelessWithRootPathTest.java deleted file mode 100644 index a36362a7e1760..0000000000000 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/OpenTelemetryTracelessWithRootPathTest.java +++ /dev/null @@ -1,146 +0,0 @@ -package io.quarkus.opentelemetry.deployment.traces; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.hamcrest.Matchers.is; - -import java.util.List; - -import jakarta.inject.Inject; - -import org.jboss.shrinkwrap.api.ShrinkWrap; -import org.jboss.shrinkwrap.api.asset.StringAsset; -import org.jboss.shrinkwrap.api.spec.JavaArchive; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; - -import io.opentelemetry.sdk.trace.data.SpanData; -import io.quarkus.opentelemetry.deployment.common.exporter.InMemoryExporter; -import io.quarkus.opentelemetry.deployment.common.exporter.InMemoryMetricExporterProvider; -import io.quarkus.opentelemetry.deployment.common.traces.TraceMeResource; -import io.quarkus.opentelemetry.deployment.common.traces.TracelessClassLevelResource; -import io.quarkus.opentelemetry.deployment.common.traces.TracelessHelloResource; -import io.quarkus.test.QuarkusUnitTest; -import io.restassured.RestAssured; - -public class OpenTelemetryTracelessWithRootPathTest { - @RegisterExtension - static final QuarkusUnitTest TEST = new QuarkusUnitTest().setArchiveProducer( - () -> ShrinkWrap.create(JavaArchive.class) - .addPackage(InMemoryExporter.class.getPackage()) - .addAsResource("resource-config/application.properties", "application.properties") - .addAsResource( - "META-INF/services-config/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider", - "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider") - .addAsResource(new StringAsset(InMemoryMetricExporterProvider.class.getCanonicalName()), - "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider") - .addClasses(TracelessHelloResource.class, TracelessClassLevelResource.class, TraceMeResource.class)) - .overrideConfigKey("quarkus.http.root-path", "/api"); - - @Inject - InMemoryExporter exporter; - - @BeforeEach - void setup() { - exporter.reset(); - } - - @Test - @DisplayName("Should not trace when the annotation @Traceless is at method level") - void testingTracelessHelloHi() { - - RestAssured.when() - .get("/hello").then() - .statusCode(200) - .body(is("hello")); - - RestAssured.when() - .get("/hello/hi").then() - .statusCode(200) - .body(is("hi")); - - RestAssured.when() - .get("/trace-me").then() - .statusCode(200) - .body(is("trace-me")); - - List spans = exporter.getSpanExporter().getFinishedSpanItems(1); - - assertThat(spans) - .hasSize(1) - .satisfiesOnlyOnce(span -> assertThat(span.getName()).containsOnlyOnce("trace-me")); - } - - @Test - @DisplayName("Should not trace when the method @Path uses @PathParam") - void testingWithPathParam() { - RestAssured.when() - .get("/hello/mask/1").then() - .statusCode(200) - .body(is("mask-1")); - - RestAssured.when() - .get("/trace-me").then() - .statusCode(200) - .body(is("trace-me")); - - List spans = exporter.getSpanExporter().getFinishedSpanItems(1); - - assertThat(spans) - .hasSize(1) - .satisfiesOnlyOnce(span -> assertThat(span.getName()).containsOnlyOnce("trace-me")); - } - - @Test - @DisplayName("Should not trace when the method @Path is without '/'") - void testingHelloNoSlash() { - RestAssured.when() - .get("/hello/no-slash").then() - .statusCode(200) - .body(is("no-slash")); - - RestAssured.when() - .get("/trace-me").then() - .statusCode(200) - .body(is("trace-me")); - - List spans = exporter.getSpanExporter().getFinishedSpanItems(1); - - assertThat(spans) - .hasSize(1) - .satisfiesOnlyOnce(span -> assertThat(span.getName()).containsOnlyOnce("trace-me")); - } - - @Test - @DisplayName("Should not trace when the annotation is at class level") - void testingTracelessAtClassLevel() { - - RestAssured.when() - .get("class-level").then() - .statusCode(200) - .body(is("class-level")); - - RestAssured.when() - .get("/class-level/first-method").then() - .statusCode(200) - .body(is("first-method")); - - RestAssured.when() - .get("/class-level/second-method").then() - .statusCode(200) - .body(is("second-method")); - - RestAssured.when() - .get("/trace-me").then() - .statusCode(200) - .body(is("trace-me")); - - List spans = exporter.getSpanExporter().getFinishedSpanItems(1); - - assertThat(spans) - .hasSize(1) - .satisfiesOnlyOnce(span -> assertThat(span.getName()).containsOnlyOnce("trace-me")); - } - -} diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/Traceless.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/Traceless.java deleted file mode 100644 index 254022bb8ee36..0000000000000 --- a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/Traceless.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.quarkus.opentelemetry.runtime.tracing; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Identifies that the current path should not be select for tracing. - *

- * Used together with {@code jakarta.ws.rs.Path} annotation. - */ -@Target({ ElementType.TYPE, ElementType.METHOD }) -@Retention(RetentionPolicy.RUNTIME) -public @interface Traceless { -} diff --git a/integration-tests/opentelemetry/src/main/java/io/quarkus/it/opentelemetry/SimpleResource.java b/integration-tests/opentelemetry/src/main/java/io/quarkus/it/opentelemetry/SimpleResource.java deleted file mode 100644 index dcb38b74ea92a..0000000000000 --- a/integration-tests/opentelemetry/src/main/java/io/quarkus/it/opentelemetry/SimpleResource.java +++ /dev/null @@ -1,171 +0,0 @@ -package io.quarkus.it.opentelemetry; - -import jakarta.inject.Inject; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.PathParam; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.core.MediaType; - -import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; -import org.eclipse.microprofile.rest.client.inject.RestClient; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.opentelemetry.api.baggage.Baggage; -import io.opentelemetry.api.common.AttributeKey; -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.api.metrics.Meter; -import io.opentelemetry.context.Scope; -import io.quarkus.opentelemetry.runtime.tracing.Traceless; - -@Path("") -@Produces(MediaType.APPLICATION_JSON) -public class SimpleResource { - - private static final Logger LOG = LoggerFactory.getLogger(SimpleResource.class); - - @RegisterRestClient(configKey = "simple") - public interface SimpleClient { - @Path("") - @GET - TraceData noPath(); - - @Path("/") - @GET - TraceData slashPath(); - - @Path("/from-baggage") - @GET - TraceData fromBaggagePath(); - } - - @Inject - TracedService tracedService; - - @Inject - @RestClient - SimpleClient simpleClient; - - @Inject - Baggage baggage; - - @Inject - Meter meter; - - @GET - public TraceData noPath() { - TraceData data = new TraceData(); - data.message = "No path trace"; - return data; - } - - @GET - @Path("/nopath") - public TraceData noPathClient() { - return simpleClient.noPath(); - } - - @GET - @Path("/slashpath") - public TraceData slashPathClient() { - return simpleClient.slashPath(); - } - - @GET - @Path("/slashpath-baggage") - public TraceData slashPathBaggageClient() { - try (Scope scope = baggage.toBuilder() - .put("baggage-key", "baggage-value") - .build() - .makeCurrent()) { - return simpleClient.fromBaggagePath(); - } - } - - @GET - @Path("/from-baggage") - public TraceData fromBaggageValue() { - TraceData data = new TraceData(); - data.message = baggage.getEntryValue("baggage-key"); - return data; - } - - @GET - @Path("/direct") - public TraceData directTrace() { - LOG.info("directTrace called"); - TraceData data = new TraceData(); - data.message = "Direct trace"; - return data; - } - - @GET - @Path("/direct-metrics") - public TraceData directTraceWithMetrics() { - meter.counterBuilder("direct-trace-counter") - .setUnit("items") - .setDescription("A counter of direct traces") - .build() - .add(1, Attributes.of(AttributeKey.stringKey("key"), "low-cardinality-value")); - TraceData data = new TraceData(); - data.message = "Direct trace"; - return data; - } - - @GET - @Path("/chained") - public TraceData chainedTrace() { - LOG.info("chainedTrace called"); - TraceData data = new TraceData(); - data.message = tracedService.call(); - - return data; - } - - @GET - @Path("/deep/path") - public TraceData deepUrlPathTrace() { - TraceData data = new TraceData(); - data.message = "Deep url path"; - - return data; - } - - @GET - @Path("/param/{paramId}") - public TraceData pathParameters(@PathParam("paramId") String paramId) { - TraceData data = new TraceData(); - data.message = "ParameterId: " + paramId; - - return data; - } - - @GET - @Path("/exception") - public String exception() { - var exception = new RuntimeException("Exception!"); - StackTraceElement[] trimmedStackTrace = new StackTraceElement[2]; - System.arraycopy(exception.getStackTrace(), 0, trimmedStackTrace, 0, trimmedStackTrace.length); - exception.setStackTrace(trimmedStackTrace); - LOG.error("Oh no {}", exception.getMessage(), exception); - return "Oh no! An exception"; - } - - @GET - @Path("/suppress-app-uri") - public TraceData suppressAppUri() { - TraceData traceData = new TraceData(); - traceData.message = "Suppress me!"; - return traceData; - } - - @GET - @Path("/traceless") - @Traceless - public TraceData traceless() { - TraceData traceData = new TraceData(); - traceData.message = "@Traceless"; - return traceData; - } -} diff --git a/integration-tests/opentelemetry/src/test/java/io/quarkus/it/opentelemetry/TracingTest.java b/integration-tests/opentelemetry/src/test/java/io/quarkus/it/opentelemetry/TracingTest.java index 5dea04160a238..c0ed2910b642f 100644 --- a/integration-tests/opentelemetry/src/test/java/io/quarkus/it/opentelemetry/TracingTest.java +++ b/integration-tests/opentelemetry/src/test/java/io/quarkus/it/opentelemetry/TracingTest.java @@ -759,20 +759,6 @@ public void testSuppressAppUri() { }); } - @Test - public void testTracelessResource() { - RestAssured.given() - .when().get("/traceless") - .then() - .statusCode(200) - .body("message", Matchers.is("@Traceless")); - - // should throw because there is no span - assertThrows(ConditionTimeoutException.class, () -> { - await().atMost(5, SECONDS).until(() -> !getSpans().isEmpty()); - }); - } - private void verifyResource(Map spanData) { assertEquals("opentelemetry-integration-test", spanData.get("resource_service.name")); assertEquals("999-SNAPSHOT", spanData.get("resource_service.version"));