diff --git a/.github/workflows/develocity-publish-build-scans.yml b/.github/workflows/develocity-publish-build-scans.yml index b269a77a6c5603..a285e66a8445be 100644 --- a/.github/workflows/develocity-publish-build-scans.yml +++ b/.github/workflows/develocity-publish-build-scans.yml @@ -22,6 +22,7 @@ jobs: run: | echo "preapproved-developpers<> $GITHUB_OUTPUT cat .github/develocity-preapproved-developers.json >> $GITHUB_OUTPUT + echo >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT - name: Publish Maven Build Scans uses: gradle/github-actions/maven-build-scan/publish@v1-beta @@ -31,6 +32,7 @@ jobs: develocity-access-key: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} skip-comment: true - name: Push to summary + if: ${{ contains(fromJson(steps.extract-preapproved-developers.outputs.preapproved-developpers).preapproved-developers, github.event.workflow_run.actor.login) }} run: | echo -n "Pull request: " >> ${GITHUB_STEP_SUMMARY} cat pr-number.out >> ${GITHUB_STEP_SUMMARY} diff --git a/bom/application/pom.xml b/bom/application/pom.xml index a05b6e0257fc22..12ba92c88e127e 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -116,7 +116,7 @@ 2.2.1.Final 2.0.6 2.0.0.Final - 1.5.4.Final-format-001 + 1.7.0.Final 1.0.1.Final 2.2.2.Final 3.5.1.Final diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java index c4a367b89fceeb..77ad5a2dc78ebd 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java @@ -911,8 +911,10 @@ public NativeImageInvokerInfo build() { if (nativeConfig.autoServiceLoaderRegistration()) { addExperimentalVMOption(nativeImageArgs, "-H:+UseServiceLoaderFeature"); - //When enabling, at least print what exactly is being added: - nativeImageArgs.add("-H:+TraceServiceLoaderFeature"); + if (graalVMVersion.compareTo(GraalVM.Version.VERSION_23_1_0) < 0) { + // When enabling, at least print what exactly is being added. Only possible in <23.1.0 + nativeImageArgs.add("-H:+TraceServiceLoaderFeature"); + } } else { addExperimentalVMOption(nativeImageArgs, "-H:-UseServiceLoaderFeature"); } diff --git a/docs/src/main/asciidoc/images/ot-to-otel-1.png b/docs/src/main/asciidoc/images/ot-to-otel-1.png new file mode 100644 index 00000000000000..56bc6307500575 Binary files /dev/null and b/docs/src/main/asciidoc/images/ot-to-otel-1.png differ diff --git a/docs/src/main/asciidoc/images/ot-to-otel-2.png b/docs/src/main/asciidoc/images/ot-to-otel-2.png new file mode 100644 index 00000000000000..c3afba21eb51b7 Binary files /dev/null and b/docs/src/main/asciidoc/images/ot-to-otel-2.png differ diff --git a/docs/src/main/asciidoc/images/ot-to-otel-3.png b/docs/src/main/asciidoc/images/ot-to-otel-3.png new file mode 100644 index 00000000000000..86c79a69282e42 Binary files /dev/null and b/docs/src/main/asciidoc/images/ot-to-otel-3.png differ diff --git a/docs/src/main/asciidoc/opentelemetry.adoc b/docs/src/main/asciidoc/opentelemetry.adoc index 899a1ed320a618..d5ca44ee979599 100644 --- a/docs/src/main/asciidoc/opentelemetry.adoc +++ b/docs/src/main/asciidoc/opentelemetry.adoc @@ -13,15 +13,14 @@ include::_attributes.adoc[] This guide explains how your Quarkus application can utilize https://opentelemetry.io/[OpenTelemetry] (OTel) to provide distributed tracing for interactive web applications. -OpenTelemetry Metrics and Logging are not yet supported. - [NOTE] ==== +- OpenTelemetry Metrics and Logging are not yet supported. - Quarkus now supports the OpenTelemetry Autoconfiguration for Traces. The configurations match what you can see at https://github.com/open-telemetry/opentelemetry-java/blob/main/sdk-extensions/autoconfigure/README.md[OpenTelemetry SDK Autoconfigure] with the `quarkus.*` prefix. - - Extensions and the libraries they provide, are directly instrumented in Quarkus. The *use of the https://opentelemetry.io/docs/instrumentation/java/automatic/[OpenTelemetry Agent] is not needed nor recommended* due to context propagation issues between imperative and reactive libraries. +- If you come from the legacy OpenTracing extension, there is a xref:telemetry-opentracing-to-otel-tutorial.adoc[guide to help with the migration]. ==== == Prerequisites @@ -138,6 +137,33 @@ All configurations have been updated from `quarkus.opentelemetry.\*` -> `quarkus The legacy configurations are now deprecated but will still work during a transition period. ==== +=== Disable all or parts of the OpenTelemetry extension + +Once you add the dependency, the extension will be enabled by default but there are a few ways to disable the OpenTelemetry extension globally or partially. + +|=== +|Property name |Default value |Description + +|`quarkus.otel.enabled` +|true +|If false, disable the OpenTelemetry usage at *build* time. + +|`quarkus.otel.sdk.disabled` +|false +|Comes from the OpenTelemetry autoconfiguration. If true, will disable the OpenTelemetry SDK usage at *runtime*. + +|`quarkus.otel.traces.enabled` +|true +|If false, disable the OpenTelemetry tracing usage at *build* time. + +|`quarkus.otel.exporter.otlp.enabled` +|true +|If false will disable the default OTLP exporter at *build* time. +|=== + +If you need to enable or disable the exporter at runtime, you can use the <> because it has the ability to filter out all the spans if needed. + + == Run the application The first step is to configure and start the https://opentelemetry.io/docs/collector/[OpenTelemetry Collector] to receive, process and export telemetry data to https://www.jaegertracing.io/[Jaeger] that will display the captured traces. @@ -359,6 +385,7 @@ public class CustomConfiguration { By setting `quarkus.otel.traces.eusp.enabled=true` you can add information about the user related to each span. The user's ID and roles will be added to the span attributes, if available. +[[sampler]] === Sampler A https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk.md#sampling[sampler] decides whether a trace should be discarded or forwarded, effectively managing noise and reducing overhead by limiting the number of collected traces sent to the collector. diff --git a/docs/src/main/asciidoc/opentracing.adoc b/docs/src/main/asciidoc/opentracing.adoc index 88fb99d143a77e..c089dc6847c940 100644 --- a/docs/src/main/asciidoc/opentracing.adoc +++ b/docs/src/main/asciidoc/opentracing.adoc @@ -16,7 +16,7 @@ interactive web applications. [IMPORTANT] ==== -xref:opentelemetry.adoc[OpenTelemetry] is the recommended approach to tracing and telemetry for Quarkus. +xref:opentelemetry.adoc[OpenTelemetry] is the recommended approach to tracing and telemetry for Quarkus and xref:telemetry-opentracing-to-otel-tutorial.adoc[a guide to help with the migration] is available. When Quarkus will upgrade to Eclipse MicroProfile 6, the SmallRye OpenTracing support will be discontinued. ==== diff --git a/docs/src/main/asciidoc/telemetry-opentracing-to-otel-tutorial.adoc b/docs/src/main/asciidoc/telemetry-opentracing-to-otel-tutorial.adoc new file mode 100644 index 00000000000000..63348b95ea7f13 --- /dev/null +++ b/docs/src/main/asciidoc/telemetry-opentracing-to-otel-tutorial.adoc @@ -0,0 +1,544 @@ +//// +This tutorial is maintained in the main Quarkus repository +and pull requests should be submitted there: +https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc +//// +[id="telemetry-opentracing-to-otel-tutorial"] += Migrate from OpenTracing to OpenTelemetry tracing +:categories: observability +:diataxis-type: tutorial +include::_attributes.adoc[] +:topics: observability,opentracing,opentelemetry,tracing,migration +:extensions: io.quarkus:quarkus-smallrye-opentracing,io.quarkus:quarkus-opentelemetry + +Migrate an application from xref:opentracing.adoc[OpenTracing] to xref:opentelemetry.adoc[OpenTelemetry tracing] in Quarkus 3.x. + +The legacy OpenTracing framework has been deprecated in favor of the new OpenTelemetry tracing framework. We announced the https://quarkus.io/blog/quarkus-observability-roadmap-2023/#opentracing-archived[OpenTracing deprecation on November 2022], and we are dropping the extension from Quarkus core repository and moving it to the Quarkiverse Hub. + +It is now time to migrate your application to OpenTelemetry tracing if you haven’t done it yet. + +If you need to migrate from Quarkus 2.16.x please beware that configuration properties are different and you should check the older Quarkus OpenTelemetry guide version, https://quarkus.io/version/2.16/guides/opentelemetry#configuration-reference[here]. + +== Prerequisites + +include::{includes}/prerequisites.adoc[] + +== Summary + +The demo has 5 parts. Please read the summary and then jump to the section that best fits your use case. + +1 - The *starting point* presents the quickstart app that uses OpenTracing + +2 - The first part is good for anyone performing a *big bang change* of OpenTracing when you don't have any manual instrumentation + +3 - This is the *big bang replacement* of OpenTracing when you have manually instrumented the code. We explain the main differences between OpenTracing and OpenTelemetry + +4 - The last part uses the *OpenTracing shim*. This is useful if you have a large application with manually instrumented code. It can help performing the migration step by step because it allows the use of the legacy OpenTracing API on top of new OpenTelemetry API + +5 - Conclusion and additional resources + +The tasks described below fall into 3 categories: + +* Dependencies +* Configuration +* Code + +[[starting-point]] +== Starting point + +This tutorial is built on top of the `opentracing-quickstart` legacy project. + +=== Generate the legacy project + +Create the legacy project by executing the following command: + +:create-app-artifact-id: opentracing-quickstart +:create-app-extensions: resteasy-reactive,quarkus-smallrye-opentracing +:create-app-code: +include::{includes}/devtools/create-app.adoc[] + +This command generates the Maven structure importing the `smallrye-opentracing` extension, which +includes the OpenTracing support and the default https://www.jaegertracing.io/[Jaeger] tracer. + +=== Check out the existing legacy project + +For convenience there is a project in github with all the steps from the tutorial. You can clone it with the following command: + +[source,bash] +---- +git clone git@github.com:quarkusio/opentracing-quickstart-migration.git +---- + +For convenience, https://github.com/quarkusio/opentracing-quickstart-migration[the repository] containing the app to migrate, includes several branches with commits mimicking the migration steps described in this tutorial. You can check out the `main` branch to start from the beginning. + +=== The application + +The Quarkus project has a single endpoint and the related class looks like this: + +[source,java] +---- +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +@Path("/hello") +public class GreetingResource { + + @GET + @Produces(MediaType.TEXT_PLAIN) + public String hello() { + return "Hello from RESTEasy Reactive"; + } +} +---- + +There is no OpenTracing specific code in the generated project, but the `smallrye-opentracing` extension is present and enabled by default, and it will automatically instrument the code. + +Let's start the Jaeger-all-in-one Docker image, where we will retrieve and see the captured traces: + +[source,bash] +---- +docker run -e COLLECTOR_OTLP_ENABLED=true -p 6831:6831/udp -p 6832:6832/udp -p 5778:5778 -p 16686:16686 -p 4317:4317 -p 4318:4318 -p 14250:14250 -p 14268:14268 -p 14269:14269 -p 9411:9411 jaegertracing/all-in-one:latest +---- + +At this point you can run the application with Quarkus dev mode: + +include::{includes}/devtools/dev.adoc[] + +If you call the http://localhost:8080/hello[`/hello` endpoint] the related traces can be retrieved in the Jaeger UI at this address: http://localhost:16686/ + +They will look like this: + +image::ot-to-otel-1.png[alt=OpenTracing span,role="center"] + +== Big bang change from OpenTracing to OpenTelemetry + +This is the happiest path, in this case there is no manual instrumentation. We can do a big bang change from OpenTracing to OpenTelemetry without side effects. + +=== Change dependencies + +To migrate between the two frameworks, you must drop the old `quarkus-smallrye-opentracing` extension and replace it by the `quarkus-opentelemetry` extension in the build file: + +The legacy extension is removed from the project: + +[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"] +.pom.xml +---- + + io.quarkus + quarkus-smallrye-opentracing + +---- + +[source,gradle,role="secondary asciidoc-tabs-target-sync-gradle"] +.build.gradle +---- +implementation("io.quarkus:quarkus-smallrye-opentracing") +---- + +The new one is added: + +[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"] +.pom.xml +---- + + io.quarkus + quarkus-opentelemetry + +---- + +[source,gradle,role="secondary asciidoc-tabs-target-sync-gradle"] +.build.gradle +---- +implementation("io.quarkus:quarkus-opentelemetry") +---- + +=== Application properties + +You should remove the old OpenTracing properties, starting with `quarkus.jaeger.*` from the `application.properties` file, like in this example: + +[source,application.properties] +---- +#Legacy OpenTracing properties to be removed +quarkus.jaeger.service-name=legume +quarkus.jaeger.sampler-type=const +quarkus.jaeger.sampler-param=1 +quarkus.jaeger.endpoint=http://localhost:14268/api/traces +quarkus.jaeger.log-trace-context=true +---- + +If you use the default values in the OpenTelemetry properties, there is no necessity to include anything in the `application.properties` file. + +Some common properties to migrate are: + +|=== +|Legacy OpenTracing property | New OpenTelemetry property + +|`quarkus.jaeger.service-name=legume` +|`quarkus.application.name=legume` + +|`quarkus.jaeger.endpoint=http://localhost:14268/api/traces` +|`quarkus.otel.exporter.otlp.traces.endpoint=http://localhost:4317` + +|`quarkus.jaeger.auth-token` +|`quarkus.otel.exporter.otlp.traces.headers` + +|`quarkus.jaeger.sampler-type` +|`quarkus.otel.traces.sampler` + +|`quarkus.jaeger.sampler-param` +|`quarkus.otel.traces.sampler.arg` + +|`quarkus.jaeger.tags` +|`quarkus.otel.resource.attributes` + +|`quarkus.jaeger.propagation` +|`quarkus.otel.propagators` +|=== + +The way the extensions can be enabled and disabled is very different. The OpenTelemetry extension is enabled by default and you can disable all or parts of it by checking xref:opentelemetry.adoc#disable-all-or-parts-of-the-opentelemetry-extension[this section of the OpenTelemetry guide]. + +All the OpenTelemetry properties and their defaults can be found in the xref:opentelemetry.adoc#configuration-reference[OpenTelemetry configuration reference]. + +=== Run the application + +Restarting Quarkus is not needed, auto-reload should have kicked in and you now can call the http://localhost:8080/hello[`/hello` endpoint] and then see the traces in the Jaeger UI: http://localhost:16686/ + +However, you can now see spans produced by the OpenTelemetry's auto-instrumentation instead of the OpenTracing one: + +image::ot-to-otel-2.png[alt=OpenTelemetry span,role="center"] + +If you don't have any manual instrumentation of your own, you are done! + +== The big bang replacement, when you have manual instrumentation + +Let's say instead of the `GreetingResource` class from above, you have something more complex. You will need additional work on top of the changes from the <>. + +This class now uses the `@Traced` annotation and creates a "manual" programmatic span. + +Copy/paste that code for the `GreetingResource` class in the quickstart project: + +[[greeting-resource-starting-point]] +=== The GreetingsResource with OpenTracing manual instrumentation + +[source,java] +---- +package org.acme; + +import io.opentracing.Scope; +import io.opentracing.Span; +import io.opentracing.tag.Tags; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import org.eclipse.microprofile.opentracing.Traced; + +@Path("/hello") +@ApplicationScoped +public class GreetingResource { + + @Inject + io.opentracing.Tracer legacyTracer; <1> + + @GET + @Produces(MediaType.TEXT_PLAIN) + @Traced(operationName = "Not needed, will change the current span name") <2> + public String hello() { + // Add a tag to the active span + legacyTracer.activeSpan().setTag(Tags.COMPONENT, "GreetingResource"); <3> + + // Create a manual inner span + Span innerSpan = legacyTracer.buildSpan("Count response chars").start(); + + try (Scope dbScope = legacyTracer.scopeManager().activate(innerSpan)) { + String response = "Hello from RESTEasy Reactive"; + innerSpan.setTag("response-chars-count", response.length()); + return response; + } catch (Exception e) { + innerSpan.setTag("error", true); <4> + innerSpan.setTag("error.message", e.getMessage()); + throw e; + } finally { + innerSpan.finish(); + } + } +} +---- + +<1> The legacy OpenTracing tracer, must be replaced by the new OpenTelemetry tracer. +<2> The `@Traced` annotation is replaced by the `@WithSpan` annotation but beware that this new annotation will always create a new Span. You shouldn't use it on JAX-RS endpoints because they are already instrumented. +<3> The `Tag` class is replaced by the `Attribute` class. `Tags` is replaced by the `SemanticAttributes` class, which should be used whenever possible, to keep attribute names consistent with the specification. +<4> There are new methods to handle errors in OpenTelemetry. + +The OpenTelemetry tracer is not compatible with the OpenTracing API. The main changes are summarized in the following table: + +|=== +|Note |MicroProfile OpenTracing v3 |OpenTelemetry + +|1 +|`@Inject io.opentracing.Tracer legacyTracer;` +|`@Injectio.opentelemetry.api.trace.Tracer otelTracer;` + +|2 +|`@Traced` +|`@WithSpan` + +|3 +|Tag +|Attribute + +|3 +|Tags +|SemanticAttributes + +|4 +|```innerSpan.setTag("error", true); +innerSpan.setTag("error.message", e.getMessage());``` +|```innerSpan.setStatus(ERROR); +innerSpan.recordException(e);``` + +|- +|Baggage carried by SpanContext in the Span | Baggage is an independent signal propagated in parallel with the OTel Context +|Baggage is an independent signal propagated in parallel with the OTel Context, it's not part of it. +|=== + +Once the dependencies have been updated, the above class will break the build because the quickstart project is now running with OpenTelemetry. Errors like this will show up in the logs: + +[source,bash] +---- +2023-10-27 16:11:12,454 ERROR [io.qua.dep.dev.IsolatedDevModeMain] (main) Failed to start quarkus: java.lang.RuntimeException: io.quarkus.builder.BuildException: Build failure: Build failed due to errors + [error]: Build step io.quarkus.arc.deployment.ArcProcessor#validate threw an exception: jakarta.enterprise.inject.spi.DeploymentException: jakarta.enterprise.inject.UnsatisfiedResolutionException: Unsatisfied dependency for type io.opentracing.Tracer and qualifiers [@Default] +... +---- + +The new OpenTelemetry API must be used instead. This is one way to migrate the code: + +=== GreetingsResource with OpenTelemetry manual instrumentation + +[source,java] +---- +package org.acme; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.StatusCode; +import io.opentelemetry.context.Scope; +import io.opentelemetry.instrumentation.annotations.WithSpan; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +import static io.opentelemetry.api.trace.StatusCode.*; + +@Path("/hello") +@ApplicationScoped +public class GreetingResource { + + @Inject + io.opentelemetry.api.trace.Tracer otelTracer; + + @GET + @Produces(MediaType.TEXT_PLAIN) + @WithSpan(value = "Not needed, will create a new span, child of the automatic JAX-RS span") + public String hello() { + // Add a tag to the active span + Span incomingSpan = Span.current(); + incomingSpan.setAttribute(SemanticAttributes.CODE_NAMESPACE, "GreetingResource"); + + // Create a manual inner span + Span innerSpan = otelTracer.spanBuilder("Count response chars").startSpan(); + try (Scope scope = innerSpan.makeCurrent()) { + final String response = "Hello from RESTEasy Reactive"; + innerSpan.setAttribute("response-chars-count", response.length()); + return response; + } catch (Exception e) { + innerSpan.setStatus(ERROR); + innerSpan.recordException(e); + throw e; + } finally { + innerSpan.end(); + } + } +} + +---- + +Once you remove all the OpenTracing dependencies the code will build. Don't forget to double check if the traces contain the right spans. You can see them in the Jaeger UI: http://localhost:16686/. + +== The OpenTracing shim + +In this section, we present an OpenTelemetry library that can smooth the transition by providing access to the legacy OpenTracing API. This can help with the migration of large applications with many manual instrumentation points. + +To proceed with this section, the code project must be its <>. If you have changes related to the previous sections, please revert them or re-generate the project according to the <> instructions before proceeding. + +=== The dependencies + +Remove the `quarkus-smallrye-opentracing` extension and add the `quarkus-opentelemetry` extension and the `opentelemetry-opentracing-shim` library to the build file: + +The legacy extension is removed from the project: + +[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"] +.pom.xml +---- + + io.quarkus + quarkus-smallrye-opentracing + +---- + +[source,gradle,role="secondary asciidoc-tabs-target-sync-gradle"] +.build.gradle +---- +implementation("io.quarkus:quarkus-smallrye-opentracing") +---- + +The new one is added: + +[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"] +.pom.xml +---- + + io.quarkus + quarkus-opentelemetry + + + io.opentelemetry + opentelemetry-opentracing-shim + + +---- + +[source,gradle,role="secondary asciidoc-tabs-target-sync-gradle"] +.build.gradle +---- +implementation("io.quarkus:quarkus-opentelemetry") +implementation("io.quarkus:opentelemetry-opentracing-shim") +---- + +=== The code changes + +Remembering the initial version of the `GreetingResource` class from the <>: +[source, java] +---- +package org.acme; + +import io.opentracing.Scope; +import io.opentracing.Span; +import io.opentracing.tag.Tags; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import org.eclipse.microprofile.opentracing.Traced; + +@Path("/hello") +@ApplicationScoped +public class GreetingResource { + + @Inject + io.opentracing.Tracer legacyTracer; <1> + + @GET + @Produces(MediaType.TEXT_PLAIN) + @Traced(operationName = "Not needed, will change the current span name") <2> + public String hello() { + // Add a tag to the active span + legacyTracer.activeSpan().setTag(Tags.COMPONENT, "GreetingResource"); <3> + + // Create a manual inner span + Span innerSpan = legacyTracer.buildSpan("Count response chars").start(); + + try (Scope dbScope = legacyTracer.scopeManager().activate(innerSpan)) { + String response = "Hello from RESTEasy Reactive"; + innerSpan.setTag("response-chars-count", response.length()); + return response; + } catch (Exception e) { + innerSpan.setTag("error", true); + innerSpan.setTag("error.message", e.getMessage()); + throw e; + } finally { + innerSpan.finish(); + } + } +} +---- + +<1> The `Tracer` annotation must be removed and instead, we need to inject the OpenTelemetry SDK. We will need it in <3>. +<2> The `@Traced` annotation is replaced by the `@WithSpan` annotation but beware that this new annotation will always create a new Span. You shouldn't use it on JAX-RS endpoints and we only have it here for demonstration purposes. +<3> We must obtain an instance of the `legacyTracer`. The Shim includes a utility class for this purpose: `Tracer legacyTracer = OpenTracingShim.createTracerShim(openTelemetry);` + +After the changes, the code will compile and you will be able to use both the OpenTracing and OpenTelemetry APIs at the same time: + +[source,java] +---- +package org.acme; + +import io.opentelemetry.instrumentation.annotations.WithSpan; +import io.opentelemetry.opentracingshim.OpenTracingShim; +import io.opentracing.Scope; +import io.opentracing.Span; +import io.opentracing.Tracer; +import io.opentracing.tag.Tags; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +@Path("/hello") +@ApplicationScoped +public class GreetingResource { + + @Inject + io.opentelemetry.api.OpenTelemetry openTelemetry; + + @GET + @Produces(MediaType.TEXT_PLAIN) + @WithSpan(value = "Not needed, will create a new span, child of the automatic JAX-RS span") + public String hello() { + // Add a tag to the active span + Tracer legacyTracer = OpenTracingShim.createTracerShim(openTelemetry); + legacyTracer.activeSpan().setTag(Tags.COMPONENT, "GreetingResource"); + + // Create a manual inner span + Span innerSpan = legacyTracer.buildSpan("Count response chars").start(); + + try (Scope dbScope = legacyTracer.scopeManager().activate(innerSpan)) { + String response = "Hello from RESTEasy Reactive"; + innerSpan.setTag("response-chars-count", response.length()); + return response; + } catch (Exception e) { + innerSpan.setTag("error", true); + innerSpan.setTag("error.message", e.getMessage()); + throw e; + } finally { + innerSpan.finish(); + } + } +} +---- + +[IMPORTANT] +==== +It's advised not to utilize the shim for a permanent solution but solely as a tool to smooth the migration. +==== + +== Conclusion and additional resources + +This tutorial showed how to migrate an application from OpenTracing to OpenTelemetry tracing in Quarkus 3.x. + +You can find more information about the migration to OpenTelemetry at: + +* https://github.com/quarkusio/opentracing-quickstart-migration[The companion GitHub repository for this tutorial] +* https://opentelemetry.io/docs/migration/opentracing/[Migrating from OpenTracing] +* https://opentelemetry.io/docs/specs/otel/compatibility/opentracing/[OpenTracing compatibility with OpenTelemetry] diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryResourceTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryResourceTest.java index c1e2c9bfef298e..92c919c44fe800 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryResourceTest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryResourceTest.java @@ -4,6 +4,7 @@ import static io.quarkus.opentelemetry.deployment.common.TestSpanExporter.getSpanByKindAndParentId; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import java.util.List; @@ -54,6 +55,7 @@ void resource() { assertEquals("authservice", server.getResource().getAttribute(AttributeKey.stringKey("service.name"))); assertEquals(config.getRawValue("quarkus.uuid"), server.getResource().getAttribute(AttributeKey.stringKey("service.instance.id"))); + assertNotNull(server.getResource().getAttribute(AttributeKey.stringKey("host.name"))); } @Path("/hello") diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/AutoConfiguredOpenTelemetrySdkBuilderCustomizer.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/AutoConfiguredOpenTelemetrySdkBuilderCustomizer.java index cc9666624cd97f..de7e1ee998d961 100644 --- a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/AutoConfiguredOpenTelemetrySdkBuilderCustomizer.java +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/AutoConfiguredOpenTelemetrySdkBuilderCustomizer.java @@ -3,6 +3,8 @@ import static java.lang.Boolean.TRUE; import static java.util.Collections.emptyList; +import java.net.InetAddress; +import java.net.UnknownHostException; import java.util.ArrayList; import java.util.List; import java.util.function.BiFunction; @@ -71,12 +73,21 @@ public Resource apply(Resource existingResource, ConfigProperties configProperti .filter(sn -> !sn.equals(appConfig.name.orElse("unset"))) .orElse(null); + // must be resolved at startup, once. + String hostname = null; + try { + hostname = InetAddress.getLocalHost().getHostName(); + } catch (UnknownHostException e) { + hostname = "unknown"; + } + // Merge resource instances with env attributes Resource resource = resources.stream() .reduce(Resource.empty(), Resource::merge) .merge(TracerUtil.mapResourceAttributes( oTelRuntimeConfig.resourceAttributes().orElse(emptyList()), - serviceName)); // from properties + serviceName, // from properties + hostname)); return consolidatedResource.merge(resource); } else { return Resource.builder().build(); diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/TracerUtil.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/TracerUtil.java index 84ebd239996fa2..9c8e915904a04d 100644 --- a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/TracerUtil.java +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/TracerUtil.java @@ -1,5 +1,6 @@ package io.quarkus.opentelemetry.runtime.tracing; +import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.HOST_NAME; import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.SERVICE_NAME; import java.util.List; @@ -14,7 +15,7 @@ public class TracerUtil { private TracerUtil() { } - public static Resource mapResourceAttributes(List resourceAttributes, String serviceName) { + public static Resource mapResourceAttributes(List resourceAttributes, String serviceName, String hostname) { final AttributesBuilder attributesBuilder = Attributes.builder(); if (!resourceAttributes.isEmpty()) { @@ -27,6 +28,10 @@ public static Resource mapResourceAttributes(List resourceAttributes, St attributesBuilder.put(SERVICE_NAME.getKey(), serviceName); } + if (hostname != null) { + attributesBuilder.put(HOST_NAME, hostname); + } + return Resource.create(attributesBuilder.build()); } } diff --git a/extensions/opentelemetry/runtime/src/test/java/io/quarkus/opentelemetry/runtime/tracing/TracerUtilTest.java b/extensions/opentelemetry/runtime/src/test/java/io/quarkus/opentelemetry/runtime/tracing/TracerUtilTest.java index c3b27767970ef7..99c005ce5ed91a 100644 --- a/extensions/opentelemetry/runtime/src/test/java/io/quarkus/opentelemetry/runtime/tracing/TracerUtilTest.java +++ b/extensions/opentelemetry/runtime/src/test/java/io/quarkus/opentelemetry/runtime/tracing/TracerUtilTest.java @@ -19,7 +19,7 @@ public void testMapResourceAttributes() { "service.namespace=mynamespace", "service.version=1.0", "deployment.environment=production"); - Resource resource = TracerUtil.mapResourceAttributes(resourceAttributes, null); + Resource resource = TracerUtil.mapResourceAttributes(resourceAttributes, null, null); Attributes attributes = resource.getAttributes(); Assertions.assertThat(attributes.size()).isEqualTo(4); Assertions.assertThat(attributes.get(ResourceAttributes.SERVICE_NAME)).isEqualTo("myservice");