Migrate from OpenTracing to OpenTelemetry tracing

在 Quarkus 3.x 中将应用程序从 OpenTracing迁移到 OpenTelemetry tracing

Migrate an application from OpenTracing to OpenTelemetry tracing in Quarkus 3.x.

现有的 OpenTracing 框架已不再被支持,取而代之的是新的 OpenTelemetry 追踪框架。我们宣布了 OpenTracing deprecation on November 2022,并且正在从 Quarkus 核心存储库中删除扩展并将其移至 Quarkiverse Hub。

The legacy OpenTracing framework has been deprecated in favor of the new OpenTelemetry tracing framework. We announced the OpenTracing deprecation on November 2022, and we are dropping the extension from Quarkus core repository and moving it to the Quarkiverse Hub.

如果您尚未将您的应用程序迁移至 OpenTelemetry 追踪,现在是时候执行此操作了。

It is now time to migrate your application to OpenTelemetry tracing if you haven’t done it yet.

如果您需要从 Quarkus 2.16.x 进行迁移,请注意配置属性有所不同,您需要检查旧的 Quarkus OpenTelemetry 指南版本 here

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, here.

Prerequisites

Unresolved directive in telemetry-opentracing-to-otel-tutorial.adoc - include::{includes}/prerequisites.adoc[]

Summary

演示分为 5 个部分。请先阅读摘要,然后跳转至最符合您的用例的部分。

The demo has 5 parts. Please read the summary and then jump to the section that best fits your use case.

1 - *starting point*展示了使用 OpenTracing 的 quickstart 应用

1 - The starting point presents the quickstart app that uses OpenTracing

2 - 如果您在没有任何手动检测的情况下执行 *big bang change*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 - 这是在手动检测代码时 *big bang replacement*OpenTracing。我们解释了 OpenTracing 和 OpenTelemetry 之间的主要区别

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 - 最后的部分使用了 OpenTracing shim。如果您有一个手动检测代码的大型应用程序,这将非常有用。它可以通过允许在新的 OpenTelemetry API 之上使用旧的 OpenTracing API 帮助逐步执行迁移

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 - 结论和额外资源

5 - Conclusion and additional resources

下面描述的任务分为 3 类:

The tasks described below fall into 3 categories:

  • Dependencies

  • Configuration

  • Code

Starting point

本教程建立在 `opentracing-quickstart`传统项目之上。

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:

Unresolved directive in telemetry-opentracing-to-otel-tutorial.adoc - include::{includes}/devtools/create-app.adoc[]

此命令将生成一个导入 `smallrye-opentracing`扩展(其中包括 OpenTracing 支持和默认 Jaeger跟踪器)的 Maven 结构。

This command generates the Maven structure importing the smallrye-opentracing extension, which includes the OpenTracing support and the default Jaeger tracer.

Check out the existing legacy project

为了方便起见,github 中有一个包含教程所有步骤的项目。您可以使用以下命令对其进行克隆:

For convenience there is a project in github with all the steps from the tutorial. You can clone it with the following command:

git clone git@github.com:quarkusio/opentracing-quickstart-migration.git

为了方便起见, the repository包含要迁移的应用程序,其中包括几个分支,这些分支包含模仿本教程中描述的迁移步骤的提交。您可以签出 `main`分支从头开始。

For convenience, 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

Quarkus 项目有一个端点,相关的类如下所示:

The Quarkus project has a single endpoint and the related class looks like this:

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 Quarkus REST";
    }
}

在生成的项目中没有明确的 OpenTracing 代码,但是 `smallrye-opentracing`扩展默认情况下已存在并启用,并且它会自动检测代码。

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.

我们启动 Jaeger-all-in-one Docker 镜像,我们将在其中检索并查看捕获的跟踪:

Let’s start the Jaeger-all-in-one Docker image, where we will retrieve and see the captured traces:

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

此时,您可以使用 Quarkus 开发模式运行应用程序:

At this point you can run the application with Quarkus dev mode:

Unresolved directive in telemetry-opentracing-to-otel-tutorial.adoc - include::{includes}/devtools/dev.adoc[]

如果您调用 /hello endpoint,则可以在此地址的 Jaeger UI 中检索相关跟踪:[role="bare"][role="bare"]http://localhost:16686/

If you call the /hello endpoint the related traces can be retrieved in the Jaeger UI at this address: [role="bare"]http://localhost:16686/

它们将如下所示:

They will look like this:

ot to otel 1

Big bang change from OpenTracing to OpenTelemetry

这是最简单的路径,这种情况不需要手工检测。我们可以在没有副作用的情况下,将 OpenTracing 大幅度更改为 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

要在两个框架之间迁移,必须删除旧的 quarkus-smallrye-opentracing 扩展,并用构建文件中的 quarkus-opentelemetry 扩展替换它:

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:

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-smallrye-opentracing</artifactId>
</dependency>
build.gradle
implementation("io.quarkus:quarkus-smallrye-opentracing")

添加新的扩展:

The new one is added:

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-opentelemetry</artifactId>
</dependency>
build.gradle
implementation("io.quarkus:quarkus-opentelemetry")

Application properties

您应该从 application.properties 文件中删除旧的 OpenTracing 属性,以 quarkus.jaeger.* 开头,如下例所示:

You should remove the old OpenTracing properties, starting with quarkus.jaeger.* from the application.properties file, like in this example:

#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

如果您在 OpenTelemetry 属性中使用默认值,则不必在 application.properties 文件中包含任何内容。

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

启用和禁用扩展的方式截然不同。默认情况下启用 OpenTelemetry 扩展,您可以通过选中 this section of the OpenTelemetry guide 来禁用全部或部分内容。

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 this section of the OpenTelemetry guide.

所有 OpenTelemetry 属性及其默认值都可以在 OpenTelemetry configuration reference 中找到。

All the OpenTelemetry properties and their defaults can be found in the OpenTelemetry configuration reference.

Run the application

无需重新启动 Quarkus,自动重新加载应已启动,您现在可以调用 /hello endpoint,然后在 Jaeger UI 中查看跟踪:[role="bare"][role="bare"]http://localhost:16686/

Restarting Quarkus is not needed, auto-reload should have kicked in and you now can call the /hello endpoint and then see the traces in the Jaeger UI: [role="bare"]http://localhost:16686/

但是,您现在可以看到 OpenTelemetry 自动检测产生的跨度,而不是 OpenTracing 产生的跨度:

However, you can now see spans produced by the OpenTelemetry’s auto-instrumentation instead of the OpenTracing one:

ot to otel 2

如果您没有自己的任何手工检测,就完成了!

If you don’t have any manual instrumentation of your own, you are done!

The big bang replacement, when you have manual instrumentation

假设不是上述 GreetingResource 类,而是更复杂的内容。除了 Starting point 的更改之外,还需要执行其他工作。

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 Starting point.

此类现在使用 @Traced 注释并创建"手动"编程跨度。

This class now uses the @Traced annotation and creates a "manual" programmatic span.

将该代码复制/粘贴到快速入门项目中的 GreetingResource 类:

Copy/paste that code for the GreetingResource class in the quickstart project:

The GreetingsResource with OpenTracing manual instrumentation

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 Quarkus REST";
            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.

OpenTelemetry 跟踪器与 OpenTracing API 不兼容。主要更改总结在以下表格中:

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, it’s not part of it.

在更新依赖项后,由于 quickstart 项目现在使用 OpenTelemetry 运行,上述类将导致构建中断。此类错误将显示在日志中:

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:

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]
...

必须使用新的 OpenTelemetry API。这是迁移代码的一种方法:

The new OpenTelemetry API must be used instead. This is one way to migrate the code:

GreetingsResource with OpenTelemetry manual instrumentation

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 Quarkus REST";
            innerSpan.setAttribute("response-chars-count", response.length());
            return response;
        } catch (Exception e) {
            innerSpan.setStatus(ERROR);
            innerSpan.recordException(e);
            throw e;
        } finally {
            innerSpan.end();
        }
    }
}

删除所有 OpenTracing 依赖项后,代码将生成。不要忘记仔细检查跟踪是否包含正确的跨度。你可以在 Jaeger UI 中看到它们:[role="bare"][role="bare"]http://localhost:16686/。

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: [role="bare"]http://localhost:16686/.

The OpenTracing shim

在本节中,我们展示了一个 OpenTelemetry 库,它可以通过提供访问旧版本的 OpenTracing API 来平滑过渡。这可以帮助迁移具有许多手动检测点的应用程序。

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.

要继续本节,代码项目必须是 Starting point。如果你有与上一节相关的更改,请在继续之前撤消更改或根据 Starting point 的说明重新生成项目。

To proceed with this section, the code project must be its Starting point. If you have changes related to the previous sections, please revert them or re-generate the project according to the Starting point instructions before proceeding.

The dependencies

删除 quarkus-smallrye-opentracing 扩展,然后将 quarkus-opentelemetry 扩展和 opentelemetry-opentracing-shim 库添加到构建文件中:

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:

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-smallrye-opentracing</artifactId>
</dependency>
build.gradle
implementation("io.quarkus:quarkus-smallrye-opentracing")

添加新的扩展:

The new one is added:

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-opentelemetry</artifactId>
</dependency>
<dependency>
  <groupId>io.opentelemetry</groupId>
  <artifactId>opentelemetry-opentracing-shim</artifactId>
  <!-- No need to declare the version -->
</dependency>
build.gradle
implementation("io.quarkus:quarkus-opentelemetry")
implementation("io.quarkus:opentelemetry-opentracing-shim")

The code changes

记住 The GreetingsResource with OpenTracing manual instrumentationGreetingResource 类的初始版本:

Remembering the initial version of the GreetingResource class from the The GreetingsResource with OpenTracing manual instrumentation:

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 Quarkus REST";
            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);

更改后,代码将编译,你将同时使用 OpenTracing 和 OpenTelemetry API:

After the changes, the code will compile and you will be able to use both the OpenTracing and OpenTelemetry APIs at the same time:

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 Quarkus REST";
            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();
        }
    }
}

建议不要将 shim 用于永久解决方案,而只将其用作平滑迁移的工具。

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

本教程展示了如何将应用程序从 OpenTracing 迁移到 Quarkus 3.x 中的 OpenTelemetry 跟踪。

This tutorial showed how to migrate an application from OpenTracing to OpenTelemetry tracing in Quarkus 3.x.

你可以在以下位置找到有关迁移到 OpenTelemetry 的更多信息:

You can find more information about the migration to OpenTelemetry at: