OpenTelemetry 快速入门

OpenTelemetry可用于测量采集遥测数据的代码。有关更多详细信息,请访问OpenTelemetry Website

希望使用OpenTelemetry导出遥测数据的必须依赖opentelemetry-api包,并且绝不可以配置或依赖 OpenTelemetry SDK。 SDK配置必须由“应用程序”提供,而该应用程序取决于opentelemetry-sdk软件包,或任何其他OpenTelemetry API的实现。这样, 库只有用户应用程序做相应配置的情况下才会获得真正的实现。有关更多详细信息,请查阅Library Guidelines

追踪(Tracing)

我们介绍如何使用OpenTelemetry API追踪代码。注意:永远不要调用OpenTelemetry SDK的方法。

首先必须获取一个Tracer,该Tracer负责创建spans和并与上下文交互。Tracer是通过使用OpenTelemetry API来获取的,该API指定了检测要监控的检查库或应用程序的库的名称和版本。可在规范章节 Obtaining a Tracer中获得更多信息。

  1. Tracer tracer =
  2. OpenTelemetry.getTracer("instrumentation-library-name","semver:1.0.0");

创建基础Span

要创建基础Span,只需指定Span的名称。 Span的开始和结束时间由OpenTelemetry SDK自动设置。

  1. Span span = tracer.spanBuilder("my span").startSpan();
  2. try (Scope scope = tracer.withSpan(span)) {
  3. // your use case
  4. ...
  5. } catch (Throwable t) {
  6. Status status = Status.UNKNOWN.withDescription("Change it to your error message");
  7. span.setStatus(status);
  8. } finally {
  9. span.end(); // closing the scope does not end the span, this has to be done manually
  10. }

创建嵌套Span

很多时候我们希望为嵌套操作关联span。OpenTelemetry支持在进程内和跨远程进程进行追踪。更多关于如何在远程进程间共享上下文的详细信息,请查看上下文传播

对于方法a调用方法b,可以通过以下方式手动链接span:

  1. void a() {
  2. Span parentSpan = tracer.spanBuilder("a")
  3. .startSpan();
  4. b(parentSpan);
  5. parentSpan.end();
  6. }
  7. void b(Span parentSpan) {
  8. Span childSpan = tracer.spanBuilder("b")
  9. .setParent(parentSpan)
  10. .startSpan();
  11. // do stuff
  12. childSpan.end();
  13. }

OpenTelemetry API还提供了一种自动方法来传播parentSpan:

  1. void a() {
  2. Span parentSpan = tracer.spanBuilder("a").startSpan();
  3. try(Scope scope = tracer.withSpan(parentSpan)){
  4. b();
  5. } finally {
  6. parentSpan.end();
  7. }
  8. }
  9. void b() {
  10. Span childSpan = tracer.spanBuilder("b")
  11. // NOTE: setParent(parentSpan) is not required anymore,
  12. // `tracer.getCurrentSpan()` is automatically added as parent
  13. .startSpan();
  14. // do stuff
  15. childSpan.end();
  16. }

链接来自远程进程的span,只需将Remote Context设置为父级即可。

  1. Span childRemoteParent = tracer.spanBuilder("Child").setParent(remoteContext).startSpan();

Span属性

在OpenTelemetry中,可以自由创建Span,由实现者使用特定于所表示操作的属性对其进行注释。属性在Span上提供有关它追踪的特定操作的附加上下文,比如结果或操作属性。

  1. Span span = tracer.spanBuilder("/resource/path").setSpanKind(Span.Kind.CLIENT).startSpan();
  2. span.setAttribute("http.method", "GET");
  3. span.setAttribute("http.url", url.toString());

其中一些操作表示使用众所周知的协议(如HTTP或数据库调用)的调用。 对于这些,OpenTelemetry需要设置特定的属性。完整的属性列表在跨语言规范的Semantic Conventions中提供。

创建带事件的Span

Span 可以携带零个或多个Span属性的命名事件进行注释,每一个事件都是一个key:value键值对并自动携带相应的时间戳。

  1. span.addEvent("Init");
  2. ...
  3. span.addEvent("End");
  1. Attributes eventAttributes = Attributes.of(
  2. "key", AttributeValue.stringAttributeValue("value"),
  3. "result", AttributeValue.longAttributeValue(0L));
  4. span.addEvent("End Computation", eventAttributes);

创建带链接Span

一个Span可以连接一个或多个因果相关的其他Span。链接可用于表示批处理操作,其中一个Span的初始化由多个Span初始化构成,其中每个Span表示批处理中处理的单个输入项。

  1. Link link1 = SpanData.Link.create(parentSpan1.getContext());
  2. Link link2 = SpanData.Link.create(parentSpan2.getContext());
  3. Span child = tracer.spanBuilder("childWithLink")
  4. .addLink(link1)
  5. .addLink(link2)
  6. .addLink(parentSpan3.getContext())
  7. .addLink(remoteContext)
  8. .startSpan();

有关如何从远程进程中读取上下文的更多详细信息,请参见上下文传播

上下文传播

进程内传播依靠 gRPC Context, 一个完善的上下文传播库,包含在一个小构件中,它不依赖于整个gRPC引擎。

OpenTelemetry提供了一种基于文本的方法,可以使用 W3C Trace Context HTTP标头。 以下是使用HttpURLConnection发出的HTTP请求的示例。

  1. // Tell OpenTelemetry to inject the context in the HTTP headers
  2. HttpTextFormat.Setter<HttpURLConnection> setter =
  3. new HttpTextFormat.Setter<HttpURLConnection>() {
  4. @Override
  5. public void put(HttpURLConnection carrier, String key, String value) {
  6. // Insert the context as Header
  7. carrier.setRequestProperty(key, value);
  8. }
  9. };
  10. URL url = new URL("http://127.0.0.1:8080/resource");
  11. Span outGoing = tracer.spanBuilder("/resource").setSpanKind(Span.Kind.CLIENT).startSpan();
  12. try (Scope scope = tracer.withSpan(outGoing)) {
  13. // Semantic Convention.
  14. // (Observe that to set these, Span does not *need* to be the current instance.)
  15. outGoing.setAttribute("http.method", "GET");
  16. outGoing.setAttribute("http.url", url.toString());
  17. HttpURLConnection transportLayer = (HttpURLConnection) url.openConnection();
  18. // Inject the request with the *current* Context, which contains our current Span.
  19. OpenTelemetry.getPropagators().getHttpTextFormat().inject(Context.current(), transportLayer, setter);
  20. // Make outgoing call
  21. } finally {
  22. outGoing.end();
  23. }
  24. ...

类似的基于文本的方法可用于从传入请求中读取W3C追踪上下文。 下面提供了使用以下命令处理传入HTTP请求的示例 HttpExchange

  1. HttpTextFormat.Getter<HttpExchange> getter =
  2. new HttpTextFormat.Getter<HttpExchange>() {
  3. @Override
  4. public String get(HttpExchange carrier, String key) {
  5. if (carrier.getRequestHeaders().containsKey(key)) {
  6. return carrier.getRequestHeaders().get(key).get(0);
  7. }
  8. return null;
  9. }
  10. };
  11. ...
  12. public void handle(HttpExchange httpExchange) {
  13. // Extract the SpanContext and other elements from the request.
  14. Context extractedContext = OpenTelemetry.getPropagators().getHttpTextFormat()
  15. .extract(Context.current(), httpExchange, getter);
  16. Span serverSpan = null;
  17. try (Scope scope = ContextUtils.withScopedContext(extractedContext)) {
  18. // Automatically use the extracted SpanContext as parent.
  19. serverSpan = tracer.spanBuilder("/resource").setSpanKind(Span.Kind.SERVER)
  20. .startSpan();
  21. // Add the attributes defined in the Semantic Conventions
  22. serverSpan.setAttribute("http.method", "GET");
  23. serverSpan.setAttribute("http.scheme", "http");
  24. serverSpan.setAttribute("http.host", "localhost:8080");
  25. serverSpan.setAttribute("http.target", "/resource");
  26. // Serve the request
  27. ...
  28. } finally {
  29. if (serverSpan != null) {
  30. serverSpan.end();
  31. }
  32. }
  33. }

指标(Metrics)

Span是获取应用程序正在执行的操作的详细信息的好方法,但是,对于更多聚合的场景哪?OpenTelemetry为指标提供支持, 一个时序数据可能表示诸如CPU利用率,HTTP服务器的请求计数或业务指标,例如交易。

所有指标都可以用标签进行注释:附加的限定有助于描述度量指标的细分。

以下是计数器用法的示例:

  1. // Gets or creates a named meter instance
  2. Meter meter = OpenTelemetry.getMeter("instrumentation-library-name","semver:1.0.0");
  3. // Build counter e.g. LongCounter
  4. LongCounter counter = meter
  5. .longCounterBuilder("processed_jobs")
  6. .setDescription("Processed jobs")
  7. .setUnit("1")
  8. .build();
  9. // It is recommended that the API user keep a reference to a Bound Counter for the entire time or
  10. // call unbind when no-longer needed.
  11. BoundLongCounter someWorkCounter = counter.bind("Key", "SomeWork");
  12. // Record data
  13. someWorkCounter.add(123);
  14. // Alternatively, the user can use the unbounded counter and explicitly
  15. // specify the labels set at call-time:
  16. counter.add(123, "Key", "SomeWork");

Observer是支持异步API并按需收集度量数据的附加工具,按间隔收集数据。

以下是观察者用法的示例:

  1. // Build observer e.g. LongObserver
  2. LongObserver observer = meter
  3. .observerLongBuilder("cpu_usage")
  4. .setDescription("CPU Usage")
  5. .setUnit("ms")
  6. .build();
  7. observer.setCallback(
  8. new LongObserver.Callback<LongObserver.ResultLongObserver>() {
  9. @Override
  10. public void update(ResultLongObserver result) {
  11. // long getCpuUsage()
  12. result.observe(getCpuUsage(), "Key", "SomeWork");
  13. }
  14. });

Tracing SDK配置

本文档中的配置示例仅适用于opentelemetry-sdk提供的SDK。API的其他实现可能提供不同的配置机制。

该应用程序必须安装带有输出器的span处理器,并且可以自定义OpenTelemetry SDK的行为。

比如一个基本配置实例化了SDK追踪器注册表,并设置为将追踪数据导出到日志记录流。

  1. // Get the tracer
  2. TracerSdkProvider tracerProvider = OpenTelemetrySdk.getTracerProvider();
  3. // Set to export the traces to a logging stream
  4. tracerProvider.addSpanProcessor(
  5. SimpleSpanProcessor.newBuilder(
  6. new LoggingSpanExporter()
  7. ).build());

采样器(Sampler)

追踪和导出应用程序中的每个用户请求并不总是可行的。 为了在可观察性和占用资源之间取得平衡,可以对追踪数据进行采样。 OpenTelemetry SDK提供了三个开箱即用的采样器:

  • AlwaysOnSampler不管上游采样决定如何,都对每个追踪进行采样。
  • AlwaysOffSampler不会对任何追踪进行采样,无论上游采样决定如何。
  • Probability 对上游的任何采样的追踪数据,根据配置的百分比进行采样。

可以通过实现io.opentelemetry.sdk.trace.Sampler接口来提供其他采样器。

  1. TraceConfig alwaysOn = TraceConfig.getDefault().toBuilder().setSampler(
  2. Samplers.alwaysOn()
  3. ).build();
  4. TraceConfig alwaysOff = TraceConfig.getDefault().toBuilder().setSampler(
  5. Samplers.alwaysOff()
  6. ).build();
  7. TraceConfig half = TraceConfig.getDefault().toBuilder().setSampler(
  8. Samplers.probability(0.5)
  9. ).build();
  10. // Configure the sampler to use
  11. tracerProvider.updateActiveTraceConfig(
  12. half
  13. );

Span处理器

OpenTelemetry提供了不同的Span处理器。SimpleSpanProcessor立即将结束的Span转发到导出器,而BatchSpanProcessor对它们进行批处理并批量发送它们。MultiSpanProcessor将多个Span处理器配置为同时处于活动状态。

  1. tracerProvider.addSpanProcessor(
  2. SimpleSpanProcessor.newBuilder(new LoggingSpanExporter()).build()
  3. );
  4. tracerProvider.addSpanProcessor(
  5. BatchSpanProcessor.newBuilder(new LoggingSpanExporter()).build()
  6. );
  7. tracerProvider.addSpanProcessor(MultiSpanProcessor.create(Arrays.asList(
  8. SimpleSpanProcessor.newBuilder(new LoggingSpanExporter()).build(),
  9. BatchSpanProcessor.newBuilder(new LoggingSpanExporter()).build()
  10. )));

导出器(Exporter)

Span处理器由导出器初始化,该导出器负责将遥测数据发送到特定的后端。OpenTelemetry提供了四种开箱即用的导出器:

  • In-Memory: 数据保存在内存中,在debug时使用。
  • Jaeger Exporter: 准备收集的遥测数据并将其通过gRPC发送到Jaeger后端。
  • Zipkin Exporter: 准备收集的遥测数据,并通过Zipkin API将其发送到Zipkin后端。
  • Logging Exporter: 将遥测数据保存到日志流中。
  • OpenTelemetry Exporter: 将数据发送到OpenTelemetry Collector (还未实现)。

其他导出器可以在OpenTelemetry Registry中找到。

  1. tracerProvider.addSpanProcessor(
  2. SimpleSpanProcessor.newBuilder(InMemorySpanExporter.create()).build());
  3. tracerProvider.addSpanProcessor(
  4. SimpleSpanProcessor.newBuilder(new LoggingSpanExporter()).build());
  5. ManagedChannel jaegerChannel =
  6. ManagedChannelBuilder.forAddress([ip:String], [port:int]).usePlaintext().build();
  7. JaegerGrpcSpanExporter jaegerExporter = JaegerGrpcSpanExporter.newBuilder()
  8. .setServiceName("example").setChannel(jaegerChannel).setDeadline(30000)
  9. .build();
  10. tracerProvider.addSpanProcessor(BatchSpanProcessor.newBuilder(
  11. jaegerExporter
  12. ).build());

英文原文:https://github.com/open-telemetry/opentelemetry-java/blob/master/QUICKSTART.md