我的 OpenTelemetry Tracing 演示包含两个 Spring Boot 组件。其中一个使用 Java 代理,最近我将其从 v1.x 升级到 v2.x 时注意到了不同的行为。在另一个中,我使用 Micrometer Tracing,因为我编译为 GraalVM 原生,它无法处理 Java 代理。 我的 OpenTelemetry 演示 在这篇文章中,我想比较这三种方法:Java agent v1、Java agent v2 和 Micrometer Tracing。 基础应用程序及其基础设施 我将使用相同的基础应用程序:一个用 Kotlin 编码的简单 Spring Boot 应用程序。它提供单个端点。 端点之外的函数名为 entry() 它调用另一个名为 的函数 intermediate() 后者使用 实例( 的替代品)来调用上述端点 WebClient RestTemplate 为了避免无限循环,我传递了一个自定义请求标头:如果 函数找到它,它就不会继续执行 entry() 翻译过来就是下面的代码: @SpringBootApplication class Agent1xApplication @RestController class MicrometerController { private val logger = LoggerFactory.getLogger(MicrometerController::class.java) @GetMapping("/{message}") fun entry(@PathVariable message: String, @RequestHeader("X-done") done: String?) { logger.info("entry: $message") if (done == null) intermediate() } fun intermediate() { logger.info("intermediate") RestClient.builder() .baseUrl("http://localhost:8080/done") .build() .get() .header("X-done", "true") .retrieve() .toBodilessEntity() } } 对于每个设置,我都会检查两个阶段:启用 OpenTelemetry 的主要阶段和创建额外内部跨度的自定义阶段。 微米追踪 Micrometer Tracing 源自 ,一种“与供应商无关的应用程序可观察性外观”。 Micrometer Micrometer Tracing 为最流行的跟踪器库提供了一个简单的外观,让您可以对基于 JVM 的应用程序代码进行插桩,而无需锁定供应商。它旨在几乎不增加跟踪收集活动的开销,同时最大限度地提高跟踪工作的可移植性。 -- 微米追踪站点 要开始使用 Micrometer Tracing,需要添加一些依赖项: Spring Boot 执行器, org.springframework.boot:spring-boot-starter-actuator Micrometer Tracing 本身, io.micrometer:micrometer-tracing 通往目标跟踪后端 API 的“桥梁”。在我的例子中,它是 OpenTelemetry,因此是 io.micrometer:micrometer-tracing-bridge-otel 后端的具体导出器, io.opentelemetry:opentelemetry-exporter-otlp 我们不需要 BOM,因为版本已经在 Spring Boot 父级中定义。 然而,我们需要两个运行时配置参数:跟踪应该发送到哪里,以及组件的名称是什么。它们由 和 变量控制。 MANAGEMENT_OTLP_TRACING_ENDPOINT SPRING_APPLICATION_NAME services: jaeger: image: jaegertracing/all-in-one:1.55 environment: - COLLECTOR_OTLP_ENABLED=true #1 ports: - "16686:16686" micrometer-tracing: build: dockerfile: Dockerfile-micrometer environment: MANAGEMENT_OTLP_TRACING_ENDPOINT: http://jaeger:4318/v1/traces #2 SPRING_APPLICATION_NAME: micrometer-tracing #3 为 Jaeger 启用 OpenTelemetry 收集器 Jaeger OpenTelemetry gRPC 端点的完整 URL 设置 OpenTelemetry 的服务名称 结果如下: 无需任何定制,Micrometer 在接收和发送 HTTP 请求时会创建跨度。 框架需要为 注入魔力才能进行发送。我们必须让前者实例化后者: RestClient @SpringBootApplication class MicrometerTracingApplication { @Bean fun restClient(builder: RestClient.Builder) = builder.baseUrl("http://localhost:8080/done").build() } 我们可以通过多种方式创建手动跨度,其中一种是通过 OpenTelemetry API 本身。但是,设置需要大量样板代码。最直接的方法是 Micrometer 的 。它的主要好处是使用一个 API 来管理 和 。 Observation API 指标 跟踪 以下是更新后的代码: class MicrometerController( private val restClient: RestClient, private val registry: ObservationRegistry ) { @GetMapping("/{message}") fun entry(@PathVariable message: String, @RequestHeader("X-done") done: String?) { logger.info("entry: $message") val observation = Observation.start("entry", registry) if (done == null) intermediate(observation) observation.stop() } fun intermediate(parent: Observation) { logger.info("intermediate") val observation = Observation.createNotStarted("intermediate", registry) .parentObservation(parent) .start() restClient.get() .header("X-done", "true") .retrieve() .toBodilessEntity() observation.stop() } } 添加的观察调用反映了生成的跟踪: OpenTelemetry 代理 v1 Micrometer Tracing 的替代方案是通用的 。 它的主要优点是它既不影响代码也不影响开发人员;代理是一个纯粹的运行时范围关注点。 OpenTelemetry Java Agent java -javaagent:opentelemetry-javaagent.jar agent-one-1.0-SNAPSHOT.jar 代理遵守 OpenTelemetry 的环境变量配置: services: agent-1x: build: dockerfile: Dockerfile-agent1 environment: OTEL_EXPORTER_OTLP_ENDPOINT: http://jaeger:4317 #1 OTEL_RESOURCE_ATTRIBUTES: service.name=agent-1x #2 OTEL_METRICS_EXPORTER: none #3 OTEL_LOGS_EXPORTER: none #4 ports: - "8081:8080" 设置协议、域和端口。库附加 /v1/traces 设置 OpenTelemetry 的服务名称 不导出指标也不导出日志 无需进行更多配置,我们得到以下跟踪: 代理会自动跟踪接收和发送的请求 。根据调用堆栈,跟踪正确地嵌套在一起。要跟踪其他函数,我们需要在代码库中添加依赖项 。现在,我们可以使用 注释注释以前未跟踪的函数。 以及标有 Spring 相关注释的函数 io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations @WithSpan 部分控制跟踪的标签,而 则转换为 属性。如果将值设置为空字符串(默认值),则会输出函数的名称。就我的目的而言,默认值就足够了。 value() kind span.kind @WithSpan fun intermediate() { logger.info("intermediate") RestClient.builder() .baseUrl("http://localhost:8080/done") .build() .get() .header("X-done", "true") .retrieve() .toBodilessEntity() } 它产生了预期的新的 轨迹: intermediate() OpenTelemetry 代理 v2 OpenTelemetry 于今年 1 月发布了该代理的新主要版本。我用它更新了我的演示;现在只有在应用程序接收和发送请求时才会创建跟踪。 与之前的版本一样,我们可以使用 注释添加跟踪。唯一的区别是我们还必须注释 函数。默认情况下不会跟踪它。 @WithSpan entry() 讨论 Spring 之所以成功,有两个原因:它简化了复杂的解决方案, EJB 2,并提供了一个超越竞争库的抽象层。Micrometer Tracing 最初是 Zipkin 和 Jaeger 上的抽象层,这完全是合理的。由于 OpenTelemetry 得到了大多数跨编程语言和跟踪收集器的库的支持,因此这一论点变得毫无意义。Observation API 仍然是 Micrometer Tracing 的一个相当大的优势,因为它使用了一个超越 Metrics 和 Traces 的 API。 即 在 Java 代理方面,OpenTelemetry 配置在所有技术堆栈和库中都是相似的 - 环境变量。当我从 v1 升级到 v2 时,我有点失望,因为新代理不支持 Spring:默认情况下不会跟踪带 Spring 注释的函数。 最后,这是一个明智的决定。明确说明您想要的跨度比删除一些您不想看到的跨度要好得多。 。 感谢 的帮助和评论 Jonatan Ivanov 本文的完整源代码可以在 GitHub 上找到: https://github.com/ajavageek/boot-tracing?embedable=true 进一步来说: OpenTelemetry 跟踪演示 微米追踪 OpenTelemetry 跟踪 OpenTelemetry Java 集成 OpenTelemetry Java 示例 使用 Spring Boot 3 进行分布式跟踪 - Micrometer 与 OpenTelemetry 使用 Spring Boot 3 实现可观察性 最初于 2024 年 8 月 3 日在 上发布 A Java Geek