The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.

使用OpenTelemetry

This guide explains how your Quarkus application can utilize OpenTelemetry (OTel) to provide distributed tracing for interactive web applications.

OpenTelemetry Metrics and Logging are not yet supported.

  • Quarkus now supports the OpenTelemetry Autoconfiguration for Traces. The configurations match what you can see at OpenTelemetry SDK Autoconfigure with the quarkus.* prefix.

  • Extensions and the libraries they provide, are directly instrumented in Quarkus. The use of the OpenTelemetry Agent is not needed nor recommended due to context propagation issues between imperative and reactive libraries.

先决条件

完成这个指南,你需要:

  • 大概15分钟

  • 编辑器

  • 安装JDK 11以上版本并正确配置了 JAVA_HOME

  • Apache Maven 3.9.1

  • Docker and Docker Compose or Podman, and Docker Compose

  • 如果你愿意的话,还可以选择使用Quarkus CLI

  • 如果你想构建原生可执行程序,可以选择安装Mandrel或者GraalVM,并正确配置(或者使用Docker在容器中进行构建)

架构

在本指南中,我们创建了一个简单的REST应用程序来演示分布式跟踪服务。

解决方案

我们建议您按照下面几节的说明,一步一步地创建应用程序。不过,您可以直接跳到已完成的例子。

克隆 Git 仓库。 git clone https://github.com/quarkusio/quarkus-quickstarts.git ,或者下载一个 存档

该解决方案位于 opentelemetry-quickstart 目录中。

创建Maven项

首先,我们需要一个新的项目。用以下命令创建一个新项目:

CLI
quarkus create app org.acme:opentelemetry-quickstart \
    --extension='resteasy-reactive,quarkus-opentelemetry' \
    --no-code
cd opentelemetry-quickstart

创建Grade项目,请添加 --gradle 或者 --gradle-kotlin-dsl 参数。

关于如何安装并使用Quarkus CLI的更多信息,请参考Quarkus CLI指南

Maven
mvn io.quarkus.platform:quarkus-maven-plugin:3.1.0.Final:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=opentelemetry-quickstart \
    -Dextensions='resteasy-reactive,quarkus-opentelemetry' \
    -DnoCode
cd opentelemetry-quickstart

创建Grade项目,请添加 -DbuildTool=gradle 或者 -DbuildTool=gradle-kotlin-dsl 参数。

This command generates the Maven project and imports the quarkus-opentelemetry extension, which includes the default OpenTelemetry support, and a gRPC span exporter for OTLP.

If you already have your Quarkus project configured, you can add the quarkus-opentelemetry extension to your project by running the following command in your project base directory:

CLI
quarkus extension add 'opentelemetry'
Maven
./mvnw quarkus:add-extension -Dextensions='opentelemetry'
Gradle
./gradlew addExtension --extensions='opentelemetry'

这将在您的构建文件中添加以下内容:

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

Examine the Jakarta REST resource

创建一个 src/main/java/org/acme/opentelemetry/TracedResource.java 文件,内容如下:

package org.acme.opentelemetry;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import org.jboss.logging.Logger;

@Path("/hello")
public class TracedResource {

    private static final Logger LOG = Logger.getLogger(TracedResource.class);

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String hello() {
        LOG.info("hello");
        return "hello";
    }
}

请注意,应用程序中没有包含任何关于追踪的代码。默认情况下,不需要修改任何必要的代码就可以实现对发送到这个节点的请求进行追踪。

创建配置

There are no mandatory configurations for the extension to work.

If you need to change any of the default property values, here is an example on how to configure the default OTLP gRPC Exporter within the application, using the src/main/resources/application.properties file:

quarkus.application.name=myservice (1)
quarkus.otel.exporter.otlp.traces.endpoint=http://localhost:4317 (2)
quarkus.otel.exporter.otlp.traces.headers=authorization=Bearer my_secret (3)
quarkus.log.console.format=%d{HH:mm:ss} %-5p traceId=%X{traceId}, parentId=%X{parentId}, spanId=%X{spanId}, sampled=%X{sampled} [%c{2.}] (%t) %s%e%n  (4)

# Alternative to the console log
quarkus.http.access-log.pattern="...traceId=%{X,traceId} spanId=%{X,spanId}" (5)
1 所有从应用程序创建的spans将包含一个OpenTelemetry Resource ,表明该span是由 myservice 应用程序创建。如果没有设置,它将默认为artifact id。
2 gRPC endpoint to send spans. If not set, it will default to http://localhost:4317.
3 可选的gRPC消息头,通常用于认证
4 Add tracing information into log messages.
5 You can also only put the trace info into the access log. In this case you must omit the info in the console log format.

All configurations have been updated from quarkus.opentelemetry.*quarkus.otel.*

The legacy configurations are now deprecated but will still work during a transition period.

运行应用程序

第一步是配置和启动 OpenTelemetry Collector ,以接收、处理和输出勘测数据到 Jaeger 来显示捕获的traces。

Jaeger-all-in-one includes the Jaeger agent, an OTel collector, and the query service/UI. You do not need to install a separated collector. You can directly send the trace data to Jaeger (after enabling OTLP receivers there, see e.g. this blog entry for details).

您可以通过 docker-compose up -d 命令和下面的 docker-compose.yml 文件来启动OpenTelemetry Collector和Jaeger系统:

version: "2"
services:

  # Jaeger
  jaeger-all-in-one:
    image: jaegertracing/all-in-one:latest
    ports:
      - "16686:16686" # Jaeger UI
      - "14268:14268" # Receive legacy OpenTracing traces, optional
      - "4317:4317"   # OTLP gRPC receiver
      - "4318:4318"   # OTLP HTTP receiver, not yet used by Quarkus, optional
      - "14250:14250" # Receive from external otel-collector, optional
    environment:
      - COLLECTOR_OTLP_ENABLED=true

You should remove the optional ports you don’t need them.

现在我们准备运行我们的应用程序。如果使用 application.properties 来配置tracer:

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

或者如果通过JVM参数配置OTLP gRPC节点:

CLI
quarkus dev -Djvm.args="-Dquarkus.otel.exporter.otlp.traces.endpoint=http://localhost:4317"
Maven
./mvnw quarkus:dev -Djvm.args="-Dquarkus.otel.exporter.otlp.traces.endpoint=http://localhost:4317"
Gradle
./gradlew --console=plain quarkusDev -Djvm.args="-Dquarkus.otel.exporter.otlp.traces.endpoint=http://localhost:4317"

With the OpenTelemetry Collector, the Jaeger system and the application running, you can make a request to the provided endpoint:

$ curl http://localhost:8080/hello
hello

When the first request has been submitted, you will be able to see the tracing information in the logs:

10:49:02 INFO  traceId=, parentId=, spanId=, sampled= [io.quarkus] (main) Installed features: [cdi, opentelemetry, rest-client, resteasy, smallrye-context-propagation, vertx]
10:49:03 INFO  traceId=17ceb8429b9f25b0b879fa1503259456, parentId=3125c8bee75b7ad6, spanId=58ce77c86dd23457, sampled=true [or.ac.op.TracedResource] (executor-thread-1) hello
10:49:03 INFO  traceId=ad23acd6d9a4ed3d1de07866a52fa2df, parentId=, spanId=df13f5b45cf4d1e2, sampled=true [or.ac.op.TracedResource] (executor-thread-0) hello

然后访问 Jaeger界面 来查看追踪信息。

Hit CTRL+C or type q to stop the application.

JDBC

通过JDBC instrumentation 可以为您的应用程序的每个JDBC查询添加一个span。要启用它,请在您的构建文件中添加以下依赖:

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

As it uses a dedicated JDBC datasource wrapper, you must enable telemetry for your datasource:

# enable tracing
quarkus.datasource.jdbc.telemetry=true

# configure datasource
quarkus.datasource.db-kind=postgresql
quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/mydatabase

其他配置

有些情况下需要对OpenTelemetry进行自定义配置。以下章节将展示如何对一些必要的属性进行正确配置。

ID生成器(ID Generator)

OpenTelemetry扩展将默认使用一个随机的 ID发生器 来创建trace和span标识符。

一些供应商特定协议需要一个自定义的ID生成器,您可以通过创建一个自定义生成器来覆盖默认的ID。OpenTelemetry扩展将检测 IdGenerator CDI Bean,并在配置tracer生成器时使用它。

@Singleton
public class CustomConfiguration {

    /** Creates a custom IdGenerator for OpenTelemetry */
    @Produces
    @Singleton
    public IdGenerator idGenerator() {
        return AwsXrayIdGenerator.getInstance();
    }
}

Propagators

OpenTelemetry propagates cross-cutting concerns through propagators that will share an underlying Context for storing state and accessing data across the lifespan of a distributed transaction.

By default, the OpenTelemetry extension enables the W3C Trace Context and the W3C Baggage propagators, you can however choose any of the supported OpenTelemetry propagators by setting the propagators config that is described in the OpenTelemetry Configuration Reference.

Additional Propagators

  • b3 , b3multi , jaegerottrace propatagors 需要将 trace-propagators 扩展作为一个依赖添加到您的项目中。

pom.xml
<dependency>
    <groupId>io.opentelemetry</groupId>
    <artifactId>opentelemetry-extension-trace-propagators</artifactId>
</dependency>
build.gradle
implementation("io.opentelemetry:opentelemetry-extension-trace-propagators")
  • The xray propagator will need the aws extension to be added as a dependency to your project.

pom.xml
<dependency>
    <groupId>io.opentelemetry.contrib</groupId>
    <artifactId>opentelemetry-aws-xray-propagator</artifactId>
</dependency>
build.gradle
implementation("io.opentelemetry.contrib:opentelemetry-aws-xray-propagator")

资源

Opentelemetry 资源 是产生telemetry的实体的表示,它向输出的trace中添加属性,用以描述谁在产生trace。

You can add attributes by setting the resource-attributes tracer config that is described in the OpenTelemetry Configuration Reference. Since this property can be overridden at runtime, the OpenTelemetry extension will pick up its value following the order of precedence that is described in the Quarkus Configuration Reference.

如果通过您需要的任何方式来使用自定义的资源或由 OpenTelemetry SDK扩展 之一提供的资源,您可以创建多个资源生产者。OpenTelemetry扩展将检测 Resource CDI beans,并在配置tracer生成器时将其合并。

@ApplicationScoped
public class CustomConfiguration {

    @Produces
    @ApplicationScoped
    public Resource osResource() {
        return OsResource.get();
    }

    @Produces
    @ApplicationScoped
    public Resource ecsResource() {
        return EcsResource.get();
    }
}

采样器(Sampler)

Opentelemetry 采样器 决定了是否应该对一个trace进行采样和输出,通过减少收集和发送至exporter的trace样本数量来控制噪音和开销。

You can set a built-in sampler simply by setting the desired sampler config described in the OpenTelemetry Configuration Reference.

Quarkus 3.0 introduced breaking changes on the configuration.

Sampler related property names and values change to comply with the latest Java OpenTelemetry SDK. During a transition period it will be possible to set the new configuration values in the old property because we are mapping quarkus.opentelemetry.tracer.samplerquarkus.otel.traces.sampler.

If the sampler is parent based, there is no need to set, the now dropped property, quarkus.opentelemetry.tracer.sampler.parent-based.

The values you need to set on quarkus.opentelemetry.tracer.sampler are now:

Old Sampler config value New Sampler config value New Sampler config value (Parent based)

on

always_on

parentbased_always_on

off

always_off

parentbased_always_off

ratio

traceidratio

parentbased_traceidratio

If you need to use a custom sampler there are now 2 different ways:

Sampler CDI Producer

You can create a sampler CDI producer. The Quarkus OpenTelemetry extension will detect the Sampler CDI bean and will use it when configuring the Tracer.

@Singleton
public class CustomConfiguration {

    /** Creates a custom sampler for OpenTelemetry */
    @Produces
    @Singleton
    public Sampler sampler() {
        return JaegerRemoteSampler.builder()
        .setServiceName("my-service")
        .build();
    }
}

OTel Sampler SPI

This will use the SPI hooks available with the OTel Autoconfiguration. You can create a simple Sampler class:

public class CustomSPISampler implements Sampler {
    @Override
    public SamplingResult shouldSample(Context context,
            String s,
            String s1,
            SpanKind spanKind,
            Attributes attributes,
            List<LinkData> list) {
        // Do some sampling here
        return Sampler.alwaysOn().shouldSample(context, s, s1, spanKind, attributes, list);
    }

    @Override
    public String getDescription() {
        return "custom-spi-sampler-description";
    }
}

Then a Sampler Provider:

public class CustomSPISamplerProvider implements ConfigurableSamplerProvider {
    @Override
    public Sampler createSampler(ConfigProperties configProperties) {
        return new CustomSPISampler();
    }

    @Override
    public String getName() {
        return "custom-spi-sampler";
    }
}

Write the SPI loader text file at resources/META-INF/services with name io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSamplerProvider containing the full qualified name of the CustomSPISamplerProvider class.

Then activate on the configuration:

quarkus.otel.traces.sampler=custom-spi-sampler

As you can see, CDI is much simpler to work with.

其他的植入(instrumentation)

一些Quarkus扩展需要额外的代码来确保traces被传播到后续执行中。以下章节将展示跨越进程边界传播traces的必要条件。

本节中用到的植入方式已经过Quarkus测试,并且在标准和本地模式下都能工作。

CDI

Annotating a method in any CDI aware bean with the io.opentelemetry.instrumentation.annotations.WithSpan annotation will create a new Span and establish any required relationships with the current Trace context.

Method parameters can be annotated with the io.opentelemetry.instrumentation.annotations.SpanAttribute annotation to indicate which method parameters should be part of the Trace.

例如:

@ApplicationScoped
class SpanBean {
    @WithSpan
    void span() {

    }

    @WithSpan("name")
    void spanName() {

    }

    @WithSpan(kind = SERVER)
    void spanKind() {

    }

    @WithSpan
    void spanArgs(@SpanAttribute(value = "arg") String arg) {

    }
}

Available OpenTelemetry CDI injections

As per MicroProfile Telemetry Tracing specification, Quarkus supports the CDI injections of the following classes:

  • io.opentelemetry.api.OpenTelemetry

  • io.opentelemetry.api.trace.Tracer

  • io.opentelemetry.api.trace.Span

  • io.opentelemetry.api.baggage.Baggage

You can inject these classes in any CDI enabled bean. For instance, the Tracer is particularly useful to start custom spans:

@Inject
Tracer tracer;

...

public void tracedWork() {
    Span span = tracer.spanBuilder("My custom span")
        .setAttribute("attr", "attr.value")
        .setParent(Context.current().with(Span.current()))
        .setSpanKind(SpanKind.INTERNAL)
        .startSpan();

    // traced work

    span.end();
}

SmallRye Reactive Messaging - Kafka

当使用 SmallRye Reactive Messaging 的 Kafka 扩展时,我们可以向Kafka记录中传播span,通过:

TracingMetadata tm = TracingMetadata.withPrevious(Context.current());
Message out = Message.of(...).withMetadata(tm);

The above creates a TracingMetadata object we can add to the Message being produced, which retrieves the OpenTelemetry Context to extract the current span for propagation.

Exporters

Quarkus OpenTelemetry defaults to the standard OTLP exporter defined in OpenTelemetry.

Additional exporters will be available in the Quarkiverse quarkus-opentelemetry-exporter project.

OpenTelemetry参考配置

Quarkus supports the OpenTelemetry Autoconfiguration for Traces. The configurations match what you can see at OpenTelemetry SDK Autoconfigure adding the usual quarkus.* prefix.

Quarkus OpenTelemetry configuration properties now have the quarkus.otel.* prefix.

The legacy properties with prefix quarkus.opentelemetry.* are currently being mapped to the new ones as a default, during a transition period. See Default column in the details below.

Configuration property fixed at build time - All other configuration properties are overridable at runtime

Configuration property

类型

默认

List of exporters supported by Quarkus. List of exporters to be used for tracing, separated by commas. Has one of the values on ExporterType otlp, cdi, none or the full qualified name of a class implementing io.opentelemetry.sdk.trace.export.SpanExporter Default on Quarkus is ExporterType.Constants#CDI_VALUE.

Environment variable: QUARKUS_OTEL_TRACES_EXPORTER

list of string

cdi

The sampler to use for tracing. Has one of the values on SamplerType always_on, always_off, traceidratio, parentbased_always_on, parentbased_always_off, parentbased_traceidratio or the Sampler SPI name. This will use the OTel SPI hooks for the io.opentelemetry.sdk.trace.samplers.Sampler implementation set in the provider: io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSamplerProvider. Fallbacks to the legacy property quarkus.opentelemetry.tracer.sampler.sampler.name or defaults to SamplerType.Constants#PARENT_BASED_ALWAYS_ON.

Environment variable: QUARKUS_OTEL_TRACES_SAMPLER

string

parentbased_always_on

No Metrics exporter for now

Environment variable: QUARKUS_OTEL_METRICS_EXPORTER

list of string

none

No Log exporter for now.

Environment variable: QUARKUS_OTEL_LOGS_EXPORTER

list of string

none

The propagators to be used. Use a comma-separated list for multiple propagators. Has values from PropagatorType or the full qualified name of a class implementing io.opentelemetry.context.propagation.TextMapPropagator. Default is PropagatorType.Constants#TRACE_CONTEXT,PropagatorType.Constants#BAGGAGE (W3C).

Environment variable: QUARKUS_OTEL_PROPAGATORS

list of string

tracecontext,baggage

If true, disable the OpenTelemetry SDK. Runtime configuration. Defaults to false.

Environment variable: QUARKUS_OTEL_SDK_DISABLED

boolean

false

Suppress non-application uris from trace collection. This will suppress tracing of /q endpoints. Providing a custom io.opentelemetry.sdk.trace.samplers.Sampler CDI Bean will ignore this setting. This is a Quarkus specific property. Suppressing non-application uris is enabled by default. Fallbacks to the legacy property quarkus.opentelemetry.tracer.suppress-non-application-uris or defaults to true.

Environment variable: QUARKUS_OTEL_TRACES_SUPPRESS_NON_APPLICATION_URIS

boolean

true

Include static resources from trace collection. This is a Quarkus specific property. Include static resources is disabled by default. Providing a custom io.opentelemetry.sdk.trace.samplers.Sampler CDI Bean will ignore this setting. Fallbacks to the legacy property quarkus.opentelemetry.tracer.include-static-resources or defaults to false.

Environment variable: QUARKUS_OTEL_TRACES_INCLUDE_STATIC_RESOURCES

boolean

false

An argument to the configured tracer if supported, for example a ratio. Fallbacks to the legacy property quarkus.opentelemetry.tracer.sampler.ratio or defaults to 1.0.

Environment variable: QUARKUS_OTEL_TRACES_SAMPLER_ARG

double

1.0d

The maximum length of attribute values. Applies to spans and logs. By default, there is no limit.

Environment variable: QUARKUS_OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT

string

The maximum number of attributes. Applies to spans, span events, span links, and logs. Default is 128.

Environment variable: QUARKUS_OTEL_ATTRIBUTE_COUNT_LIMIT

int

128

The maximum length of span attribute values. Takes precedence over otel.attribute.value.length.limit. By default, there is no limit.

Environment variable: QUARKUS_OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT

int

The maximum number of attributes per span. Takes precedence over otel.attribute.count.limit. Default is 128.

Environment variable: QUARKUS_OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT

int

128

The maximum number of events per span. Default is 128.

Environment variable: QUARKUS_OTEL_SPAN_EVENT_COUNT_LIMIT

int

128

The maximum number of links per span. Default is 128.

Environment variable: QUARKUS_OTEL_SPAN_LINK_COUNT_LIMIT

int

128

The interval, in milliseconds, between two consecutive exports. Default is 5000.

Environment variable: QUARKUS_OTEL_BSP_SCHEDULE_DELAY

Duration

5S

The maximum queue size. Default is 2048.

Environment variable: QUARKUS_OTEL_BSP_MAX_QUEUE_SIZE

int

2048

The maximum batch size. Default is 512.

Environment variable: QUARKUS_OTEL_BSP_MAX_EXPORT_BATCH_SIZE

int

512

The maximum allowed time, in milliseconds, to export data. Default is 30s.

Environment variable: QUARKUS_OTEL_BSP_EXPORT_TIMEOUT

Duration

30S

Specify resource attributes in the following format: key1=val1,key2=val2,key3=val3.

Environment variable: QUARKUS_OTEL_RESOURCE_ATTRIBUTES

list of string

Specify logical service name. Takes precedence over service.name defined with otel.resource.attributes and from quarkus.application.name. Defaults to quarkus.application.name.

Environment variable: QUARKUS_OTEL_SERVICE_NAME

string

${quarkus.application.name:unset}

Specify resource attribute keys that are filtered.

Environment variable: QUARKUS_OTEL_EXPERIMENTAL_RESOURCE_DISABLED_KEYS

list of string

Sets the OTLP endpoint to connect to. If unset, defaults to OtlpExporterRuntimeConfig#DEFAULT_GRPC_BASE_URI. We are currently using just the traces, therefore quarkus.otel.exporter.otlp.traces.endpoint is recommended.

Environment variable: QUARKUS_OTEL_EXPORTER_OTLP_ENDPOINT

string

http://localhost:4317/

OTLP Exporter specific. Will override otel.exporter.otlp.endpoint, if set. Fallbacks to the legacy property quarkus.opentelemetry.tracer.exporter.otlp.endpoint< or defaults to OtlpExporterRuntimeConfig#DEFAULT_GRPC_BASE_URI.

Environment variable: QUARKUS_OTEL_EXPORTER_OTLP_TRACES_ENDPOINT

string

http://localhost:4317/

Key-value pairs to be used as headers associated with gRPC requests. The format is similar to the OTEL_EXPORTER_OTLP_HEADERS environment variable, a list of key-value pairs separated by the "=" character. i.e.: key1=value1,key2=value2

Environment variable: QUARKUS_OTEL_EXPORTER_OTLP_TRACES_HEADERS

list of string

Sets the method used to compress payloads. If unset, compression is disabled. Currently supported compression methods include gzip and none.

Environment variable: QUARKUS_OTEL_EXPORTER_OTLP_TRACES_COMPRESSION

gzip, none

Sets the maximum time to wait for the collector to process an exported batch of spans. If unset, defaults to `OtlpExporterRuntimeConfig#DEFAULT_TIMEOUT_SECS`s.

Environment variable: QUARKUS_OTEL_EXPORTER_OTLP_TRACES_TIMEOUT

Duration

10S

OTLP defines the encoding of telemetry data and the protocol used to exchange data between the client and the server. Depending on the exporter, the available protocols will be different.

Environment variable: QUARKUS_OTEL_EXPORTER_OTLP_TRACES_PROTOCOL

string

http/protobuf

About the Duration format

持续时间的格式使用标准的 java.time.Duration 格式您可以在 Duration#parse() javadoc 中了解更多信息。

您还可以提供以数字开头的持续时间值。 在这种情况下,如果该值仅包含一个数字,则转换器将该值视为秒。 否则,PT 会隐式添加到值的前面,以获得标准的 java.time.Duration 格式。