从 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
项目构建。
生成旧项目
通过执行以下命令创建旧项目
对于 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 开发模式运行应用程序
quarkus dev
./mvnw quarkus:dev
./gradlew --console=plain quarkusDev
如果您调用 /hello
端点,则可以在此地址的 Jaeger UI 中检索相关追踪:https://:16686/
它们看起来像这样

从 OpenTracing 到 OpenTelemetry 的大爆炸式更改
这是最幸福的路径,在这种情况下,没有任何手动检测。我们可以从 OpenTracing 到 OpenTelemetry 进行大爆炸式更改,而不会产生副作用。
更改依赖项
要在两个框架之间迁移,您必须删除旧的 quarkus-smallrye-opentracing
扩展,并将其替换为构建文件中的 quarkus-opentelemetry
扩展
旧扩展已从项目中删除
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-opentracing</artifactId>
</dependency>
implementation("io.quarkus:quarkus-smallrye-opentracing")
添加了新的扩展
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-opentelemetry</artifactId>
</dependency>
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 属性 |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
启用和禁用扩展的方式非常不同。默认情况下启用 OpenTelemetry 扩展,您可以通过查看 OpenTelemetry 指南的此部分 来禁用它的全部或部分。
所有 OpenTelemetry 属性及其默认值都可以在 OpenTelemetry 配置参考 中找到。
运行应用程序
无需重新启动 Quarkus,自动重新加载应该已经启动,现在您可以调用 /hello
端点,然后在 Jaeger UI 中查看追踪:https://:16686/
但是,您现在可以看到由 OpenTelemetry 的自动检测生成的 span,而不是 OpenTracing 的自动检测生成的 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 类替换。Tags 被 SemanticAttributes 类替换,应尽可能使用它,以使属性名称与规范保持一致。 |
4 | OpenTelemetry 中有新的方法来处理错误。 |
OpenTelemetry 追踪器与 OpenTracing API 不兼容。主要更改总结在下表中
注意 | MicroProfile OpenTracing v3 | OpenTelemetry |
---|---|---|
1 |
|
|
2 |
|
|
3 |
Tag |
属性 |
3 |
标签 |
SemanticAttributes |
4 |
|
|
- |
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
库添加到构建文件中
旧扩展已从项目中删除
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-opentracing</artifactId>
</dependency>
implementation("io.quarkus:quarkus-smallrye-opentracing")
添加了新的扩展
<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>
implementation("io.quarkus:quarkus-opentelemetry")
implementation("io.quarkus:opentelemetry-opentracing-shim")
代码更改
记住 带有 OpenTracing 手动检测的 GreetingsResource 中 GreetingResource
类的初始版本
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 用作永久解决方案,而仅用作平滑迁移的工具。 |