使用OpenTelemetry
本指南解释了您的Quarkus应用程序如何利用 OpenTelemetry 来为交互式Web应用提供分布式跟踪服务。
先决条件
要完成这个指南,你需要:
-
大概15分钟
-
编辑器
-
安装JDK 11以上版本并正确配置了
JAVA_HOME -
Apache Maven 3.8.6
-
Docker and Docker Compose or Podman, and Docker Compose
-
如果你愿意的话,还可以选择使用Quarkus CLI
-
如果你想构建原生可执行程序,可以选择安装Mandrel或者GraalVM,并正确配置(或者使用Docker在容器中进行构建)
解决方案
我们建议您按照下面几节的说明,一步一步地创建应用程序。不过,您可以直接跳到已完成的例子。
克隆 Git 仓库: git clone https://github.com/quarkusio/quarkus-quickstarts.git ,或下载一个 存档 。
该解决方案位于 opentelemetry-quickstart 目录中。
创建Maven项目
首先,我们需要一个新的项目。使用以下命令创建一个新的项目:
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:
quarkus extension add 'opentelemetry'
./mvnw quarkus:add-extension -Dextensions='opentelemetry'
./gradlew addExtension --extensions='opentelemetry'
这将在您的构建文件中添加以下内容:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-opentelemetry</artifactId>
</dependency>
implementation("io.quarkus:quarkus-opentelemetry")
检查JAX-RS资源
创建一个 src/main/java/org/acme/opentelemetry/TracedResource.java 文件,内容如下:
package org.acme.opentelemetry;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.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 two ways to configure the default OTLP gRPC Exporter within the application.
第一种方法是通过在 src/main/resources/application.properties 文件内提供配置:
quarkus.application.name=myservice (1)
quarkus.opentelemetry.enabled=true (2)
quarkus.opentelemetry.tracer.exporter.otlp.endpoint=http://localhost:4317 (3)
quarkus.opentelemetry.tracer.exporter.otlp.headers=Authorization=Bearer my_secret (4)
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 (5)
# Alternative to the console log
quarkus.http.access-log.pattern="...traceId=%{X,traceId} spanId=%{X,spanId}" (6)
| 1 | 所有从应用程序创建的spans将包含一个OpenTelemetry Resource ,表明该span是由 myservice 应用程序创建。如果没有设置,它将默认为artifact id。 |
| 2 | OpenTelemetry是否已启用。默认是 true ,但放在这里是用来展示如何禁用它 |
| 3 | 用于发送spans的gRPC节点 |
| 4 | 可选的gRPC消息头,通常用于认证 |
| 5 | Add tracing information into log message. |
| 6 | 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. |
运行应用程序
第一步是配置和启动 OpenTelemetry Collector ,以接收、处理和输出勘测数据到 Jaeger 来显示捕获的traces。
|
Jaeger supports the OTel protocols out of the box since version 1.35. In this case you do not need to install the collector but can directly send the trace data to Jaeger (after enabling OTLP receivers there, see e.g. this blog entry). |
通过创建一个 otel-collector-config.yaml 文件来配置OpenTelemetry Collector:
receivers:
otlp:
protocols:
grpc:
endpoint: otel-collector:4317
exporters:
jaeger:
endpoint: jaeger-all-in-one:14250
tls:
insecure: true
processors:
batch:
extensions:
health_check:
service:
extensions: [health_check]
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [jaeger]
您可以通过 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"
- "14268:14268"
- "14250:14250"
# Collector
otel-collector:
image: otel/opentelemetry-collector:latest
command: ["--config=/etc/otel-collector-config.yaml"]
volumes:
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml:Z
ports:
- "13133:13133" # Health_check extension
- "4317:4317" # OTLP gRPC receiver
depends_on:
- jaeger-all-in-one
现在我们准备运行我们的应用程序。如果使用 application.properties 来配置tracer:
quarkus dev
./mvnw quarkus:dev
./gradlew --console=plain quarkusDev
或者如果通过JVM参数配置OTLP gRPC节点:
quarkus dev -Djvm.args="-Dquarkus.opentelemetry.tracer.exporter.otlp.endpoint=http://localhost:4317"
./mvnw quarkus:dev -Djvm.args="-Dquarkus.opentelemetry.tracer.exporter.otlp.endpoint=http://localhost:4317"
./gradlew --console=plain quarkusDev -Djvm.args="-Dquarkus.opentelemetry.tracer.exporter.otlp.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界面 来查看追踪信息。
使用 CTRL+C 来停止应用程序。
JDBC
通过JDBC instrumentation 可以为您的应用程序的每个JDBC查询添加一个span。要启用它,请在您的构建文件中添加以下依赖:
<dependency>
<groupId>io.opentelemetry.instrumentation</groupId>
<artifactId>opentelemetry-jdbc</artifactId>
</dependency>
implementation("io.opentelemetry.instrumentation:opentelemetry-jdbc")
由于它使用专用的JDBC驱动,您必须配置您的数据源和Hibernate ORM来使用它。
quarkus.datasource.db-kind=postgresql
# add ':otel' to your database URL
quarkus.datasource.jdbc.url=jdbc:otel:postgresql://localhost:5432/mydatabase
# use the 'OpenTelemetryDriver' instead of the one for your database
quarkus.datasource.jdbc.driver=io.opentelemetry.instrumentation.jdbc.OpenTelemetryDriver
其他配置
有些情况下需要对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.
默认情况下,OpenTelemetry扩展启用了 W3C Trace Context 和 W3C Baggage propagators,但是您可以通过设置 [参考配置] 中描述的 propagators 配置来选择任何支持的OpenTelemetry propagators。
|
pom.xml
build.gradle
pom.xml
build.gradle
|
资源
Opentelemetry 资源 是产生telemetry的实体的表示,它向输出的trace中添加属性,用以描述谁在产生trace。
You can add attributes by setting the resource-attributes tracer config that is described in the OpenTelemetry参考配置.
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样本数量来控制噪音和开销。
如果您需要使用一个自定义的采样器或使用一个由 OpenTelemetry SDK扩展 提供的采样器,您可以创建一个采样器生成器。OpenTelemetry扩展将检测到 Sampler CDI bean,并在配置tracer生成器时使用它。
@Singleton
public class CustomConfiguration {
/** Creates a custom sampler for OpenTelemetry */
@Produces
@Singleton
public Sampler sampler() {
return JaegerRemoteSampler.builder()
.setServiceName("my-service")
.build();
}
}
其他的植入(instrumentation)
一些Quarkus扩展需要额外的代码来确保traces被传播到后续执行中。以下章节将展示跨越进程边界传播traces的必要条件。
本节中用到的植入方式已经过Quarkus测试,并且在标准和本地模式下都能工作。
CDI
在任何CDI可感知的Bean中用 io.opentelemetry.extension.annotations.WithSpan 注解来注释一个方法将会创建一个新的Span,并与当前Trace上下文建立所需要的任何关系。
方法参数可以使用 io.opentelemetry.extension.annotations.SpanAttribute 注解进行注释,来表示哪些方法参数是包含在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参考配置
Configuration property fixed at build time - All other configuration properties are overridable at runtime
类型 |
默认 |
|
|---|---|---|
OpenTelemetry support. OpenTelemetry support is enabled by default. Environment variable: Show more |
boolean |
|
Comma separated list of OpenTelemetry propagators which must be supported.
Valid values are Environment variable: Show more |
list of string |
|
Support for tracing with OpenTelemetry. Support for tracing will be enabled if OpenTelemetry support is enabled and either this value is true, or this value is unset. Environment variable: Show more |
boolean |
|
A comma separated list of name=value resource attributes that represents the entity producing telemetry (eg. Environment variable: Show more |
list of string |
|
The sampler to use for tracing.
Valid values are Environment variable: Show more |
string |
|
Environment variable: |
double |
|
If the sampler to use for tracing is parent based.
Valid values are Environment variable: Show more |
boolean |
|
Suppress non-application uris from trace collection. This will suppress tracing of Environment variable: Show more |
boolean |
|
Include static resources from trace collection. Include static resources is disabled by default. Environment variable: Show more |
boolean |
|
OTLP SpanExporter support. OTLP SpanExporter support is enabled by default. Environment variable: Show more |
boolean |
|
The OTLP endpoint to connect to. The endpoint must start with either http:// or https://. Environment variable: Show more |
string |
|
Key-value pairs to be used as headers associated with gRPC requests. The format is similar to the Environment variable: Show more |
list of string |
|
The maximum amount of time to wait for the collector to process exported spans before an exception is thrown. A value of Environment variable: Show more |
|
|
Compression method to be used by exporter to compress the payload. See Configuration Options for the supported compression methods. Environment variable: Show more |
string |
|
About the Duration format
持续时间的格式使用标准的 您还可以提供以数字开头的持续时间值。 在这种情况下,如果该值仅包含一个数字,则转换器将该值视为秒。 否则, |