编辑此页面

从 OpenTracing 迁移到 OpenTelemetry 跟踪

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

旧的 OpenTracing 框架已被弃用,取而代之的是新的 OpenTelemetry 追踪框架。我们已于 2022 年 11 月宣布弃用 OpenTracing,并且正在从 Quarkus 核心存储库中删除该扩展,并将其移至 Quarkiverse Hub。

如果您尚未迁移,现在是将应用程序迁移到 OpenTelemetry 追踪的时候了。

如果您需要从 Quarkus 2.16.x 迁移,请注意配置属性有所不同,您应该查看旧版本的 Quarkus OpenTelemetry 指南,此处

先决条件

要完成本指南,您需要

  • 大约 15 分钟

  • 一个 IDE

  • 已安装 JDK 17+ 并正确配置了 JAVA_HOME

  • Apache Maven 3.9.9

  • 如果您想使用它,可以选择 Quarkus CLI

  • 如果您想构建本机可执行文件(或者如果您使用本机容器构建,则为 Docker),可以选择安装 Mandrel 或 GraalVM 并进行适当的配置

总结

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

1 - 起点 介绍了使用 OpenTracing 的 quickstart 应用程序

2 - 第一部分适用于在没有任何手动检测的情况下对 OpenTracing 进行大爆炸式更改的任何人

3 - 这是在您手动检测代码时对 OpenTracing 进行的大爆炸式替换。我们解释了 OpenTracing 和 OpenTelemetry 之间的主要区别

4 - 最后一部分使用 OpenTracing shim。如果您有一个具有手动检测代码的大型应用程序,这将非常有用。它可以帮助您逐步执行迁移,因为它允许在新的 OpenTelemetry API 之上使用旧的 OpenTracing API

5 - 结论和其他资源

下面描述的任务分为 3 类

  • 依赖项

  • 配置

  • 代码

起点

本教程基于旧的 opentracing-quickstart 项目构建。

生成旧项目

通过执行以下命令创建旧项目

CLI
quarkus create app org.acme:opentracing-quickstart \
    --extension='rest,quarkus-smallrye-opentracing'
cd opentracing-quickstart

要创建 Gradle 项目,请添加 --gradle--gradle-kotlin-dsl 选项。

有关如何安装和使用 Quarkus CLI 的更多信息,请参阅 Quarkus CLI 指南。

Maven
mvn io.quarkus.platform:quarkus-maven-plugin:3.24.4:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=opentracing-quickstart \
    -Dextensions='rest,quarkus-smallrye-opentracing'
cd opentracing-quickstart

要创建 Gradle 项目,请添加 -DbuildTool=gradle-DbuildTool=gradle-kotlin-dsl 选项。

对于 Windows 用户

  • 如果使用 cmd,(不要使用反斜杠 \ 并将所有内容放在同一行上)

  • 如果使用 Powershell,请将 -D 参数用双引号引起来,例如 "-DprojectArtifactId=opentracing-quickstart"

此命令生成 Maven 结构,导入 smallrye-opentracing 扩展,其中包括 OpenTracing 支持和默认的 Jaeger 追踪器。

检出现有的旧项目

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

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

为方便起见,包含要迁移的应用程序的 存储库 包含多个分支,这些分支带有模拟本教程中描述的迁移步骤的提交。您可以检出 main 分支以从头开始。

应用程序

Quarkus 项目具有单个端点,相关类如下所示

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 扩展存在并启用,它将自动检测代码。

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

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 开发模式运行应用程序

CLI
quarkus dev
Maven
./mvnw quarkus:dev
Gradle
./gradlew --console=plain quarkusDev

如果您调用 /hello 端点,则可以在此地址的 Jaeger UI 中检索相关追踪:https://:16686/

它们看起来像这样

OpenTracing span

从 OpenTracing 到 OpenTelemetry 的大爆炸式更改

这是最幸福的路径,在这种情况下,没有任何手动检测。我们可以从 OpenTracing 到 OpenTelemetry 进行大爆炸式更改,而不会产生副作用。

更改依赖项

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

旧扩展已从项目中删除

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

添加了新的扩展

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

应用程序属性

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

#Legacy OpenTracing properties to be removed
quarkus.jaeger.service-name=legume
quarkus.jaeger.sampler-type=const
quarkus.jaeger.sampler-param=1
quarkus.jaeger.endpoint=https://:14268/api/traces
quarkus.jaeger.log-trace-context=true

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

一些常见的迁移属性是

旧的 OpenTracing 属性 新的 OpenTelemetry 属性

quarkus.jaeger.service-name=legume

quarkus.application.name=legume

quarkus.jaeger.endpoint=https://:14268/api/traces

quarkus.otel.exporter.otlp.traces.endpoint=https://: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 扩展,您可以通过查看 OpenTelemetry 指南的此部分 来禁用它的全部或部分。

所有 OpenTelemetry 属性及其默认值都可以在 OpenTelemetry 配置参考 中找到。

运行应用程序

无需重新启动 Quarkus,自动重新加载应该已经启动,现在您可以调用 /hello 端点,然后在 Jaeger UI 中查看追踪:https://:16686/

但是,您现在可以看到由 OpenTelemetry 的自动检测生成的 span,而不是 OpenTracing 的自动检测生成的 span

OpenTelemetry span

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

大爆炸式替换,当您有手动检测时

假设您有比上面的 GreetingResource 类更复杂的东西。您需要在 起点 中的更改之上进行额外的工作。

此类现在使用 @Traced 注释并创建一个“手动”程序化 span。

将该代码复制/粘贴到 quickstart 项目中的 GreetingResource

带有 OpenTracing 手动检测的 GreetingsResource

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 必须将旧的 OpenTracing 追踪器替换为新的 OpenTelemetry 追踪器。
2 @Traced 注释被 @WithSpan 注释替换,但请注意,此新注释将始终创建一个新的 Span。您不应该在 JAX-RS 端点上使用它,因为它们已经被检测过了。
3 Tag 类被 Attribute 类替换。TagsSemanticAttributes 类替换,应尽可能使用它,以使属性名称与规范保持一致。
4 OpenTelemetry 中有新的方法来处理错误。

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

注意 MicroProfile OpenTracing v3 OpenTelemetry

1

@Inject io.opentracing.Tracer legacyTracer;

@Inject io.opentelemetry.api.trace.Tracer otelTracer;

2

@Traced

@WithSpan

3

Tag

属性

3

标签

SemanticAttributes

4

innerSpan.setTag("error", true); innerSpan.setTag("error.message", e.getMessage());

innerSpan.setStatus(ERROR); innerSpan.recordException(e);

-

Span 中由 SpanContext 携带的 Baggage

Baggage 是与 OTel Context 并行传播的独立信号,它不是 OTel Context 的一部分。

一旦依赖项更新后,上面的类将破坏构建,因为 quickstart 项目现在正在使用 OpenTelemetry 运行。这样的错误将显示在日志中

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。这是一种迁移代码的方法

带有 OpenTelemetry 手动检测的 GreetingsResource

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 依赖项,代码将构建。不要忘记仔细检查追踪是否包含正确的 span。您可以在 Jaeger UI 中看到它们:https://:16686/

OpenTracing shim

在本节中,我们将介绍一个 OpenTelemetry 库,该库可以通过提供对旧 OpenTracing API 的访问来平滑过渡。这可以帮助迁移具有许多手动检测点的大型应用程序。

要继续执行本节,代码项目必须是其 起点。如果您有与先前部分相关的更改,请恢复它们或按照 起点 说明重新生成项目,然后再继续。

依赖项

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

旧扩展已从项目中删除

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

添加了新的扩展

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")

代码更改

记住 带有 OpenTracing 手动检测的 GreetingsResourceGreetingResource 类的初始版本

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 必须删除 Tracer 注释,而是我们需要注入 OpenTelemetry SDK。我们将在 <3> 中需要它。
2 @Traced 注释被 @WithSpan 注释替换,但请注意,此新注释将始终创建一个新的 Span。您不应该在 JAX-RS 端点上使用它,我们仅在此处将其用于演示目的。
3 我们必须获取 legacyTracer 的实例。Shim 包括为此目的的实用程序类:Tracer legacyTracer = OpenTracingShim.createTracerShim(openTelemetry);

更改后,代码将编译,您将能够同时使用 OpenTracing 和 OpenTelemetry API

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 用作永久解决方案,而仅用作平滑迁移的工具。

结论和其他资源

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

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

相关内容