追踪

编辑

追踪是一组具有共同根节点的事务跨度。每个追踪都跟踪单个请求的完整过程。它描述了单个逻辑操作产生的各个操作及其因果关系。

分布式追踪
编辑

当追踪经过多个服务时(这在微服务架构中很常见),它被称为分布式追踪。分布式追踪包含跨越多个分布式组件的操作,跨越进程、网络和安全边界。

Diagram illustrating the relationship between spans
为什么分布式追踪很重要?编辑

分布式追踪使您能够通过追踪整个请求(从前端服务的初始 Web 请求一直到后端服务进行的数据库查询)来分析整个微服务架构的性能。

跟踪请求在服务中的传播,可以全面了解应用程序在哪些地方花费时间、错误发生在哪里以及瓶颈在哪里。分布式追踪消除了各个服务的的数据孤岛,并揭示了服务边界之外发生的情况。

对于支持的技术,分布式追踪无需任何额外配置即可开箱即用。

分布式追踪的工作原理编辑

分布式追踪通过将自定义的traceparent HTTP 头注入到传出请求中来工作。此头包含信息,例如trace-id(用于标识当前追踪)和parent-id(用于在传入请求中标识当前跨度的父级,或在传出请求中标识当前跨度)。

当服务处理请求时,它会检查此 HTTP 头是否存在。如果不存在,服务将启动一个新的追踪。如果存在,服务将确保当前操作作为现有追踪的子级添加,并继续传播追踪。

追踪传播示例编辑

在此示例中,Elastic 的 Ruby 代理与 Elastic 的 Java 代理通信。两者都支持traceparent头,并且追踪数据成功传播。

How traceparent propagation works

在此示例中,Elastic 的 Ruby 代理与 OpenTelemetry 的 Java 代理通信。两者都支持traceparent头,并且追踪数据成功传播。

How traceparent propagation works

在此示例中,追踪遇到一段不传播traceparent头的中间件。分布式追踪结束,任何进一步的通信都将产生新的追踪。

How traceparent propagation works
W3C 追踪上下文规范编辑

所有 Elastic 代理现在都支持官方的 W3C 追踪上下文规范和traceparent头。请参见下表以了解最低所需的代理版本。

代理名称 代理版本

Go 代理

1.6

Java 代理

1.14

.NET 代理

1.3

Node.js 代理

3.4

PHP 代理

1.0

Python 代理

5.4

Ruby 代理

3.5

RUM 代理

5.0

较旧的 Elastic 代理使用唯一的elastic-apm-traceparent头。为了向后兼容,新版本的 Elastic 代理仍然支持此头。

可视化分布式追踪编辑

应用程序 UI 的时间线可视化功能提供了对应用程序每个追踪的深入可视化分析。

Distributed tracing in the Applications UI
手动分布式追踪编辑

Elastic 代理会自动为支持的技术传播分布式追踪上下文。如果您的服务通过不同的、不支持的协议进行通信,您可以使用每个代理的 API 手动将分布式追踪上下文从发送服务传播到接收服务。

向传出请求添加traceparent编辑

发送服务必须向传出请求添加traceparent头。

  1. 使用startTransaction启动事务,或使用startSpan启动跨度。
  2. 使用injectTraceHeaderstraceparent头注入请求对象。

手动检测 RPC 框架的示例

// Hook into a callback provided by the RPC framework that is called on outgoing requests
public Response onOutgoingRequest(Request request) throws Exception {
  Span span = ElasticApm.currentSpan() 
          .startSpan("external", "http", null)
          .setName(request.getMethod() + " " + request.getHost());
  try (final Scope scope = transaction.activate()) {
      span.injectTraceHeaders((name, value) -> request.addHeader(name, value)); 
      return request.execute();
  } catch (Exception e) {
      span.captureException(e);
      throw e;
  } finally {
      span.end(); 
  }
}

创建一个表示外部调用的跨度

traceparent头注入请求对象

结束跨度

在传入请求上解析traceparent编辑

接收服务必须解析传入的traceparent头,并作为接收上下文的子级启动新的事务或跨度。

  1. 使用startTransactionWithRemoteParent()创建一个作为远程父级的子事务。
  2. 使用activate()setName()启动并命名事务。

示例

// Hook into a callback provided by the framework that is called on incoming requests
public Response onIncomingRequest(Request request) throws Exception {
    // creates a transaction representing the server-side handling of the request
    Transaction transaction = ElasticApm.startTransactionWithRemoteParent(request::getHeader, request::getHeaders); 
    try (final Scope scope = transaction.activate()) { 
        String name = "a useful name like ClassName#methodName where the request is handled";
        transaction.setName(name); 
        transaction.setType(Transaction.TYPE_REQUEST); 
        return request.handle();
    } catch (Exception e) {
        transaction.captureException(e);
        throw e;
    } finally {
        transaction.end(); 
    }
}

创建一个作为远程父级子级的交易

激活交易

命名交易

添加交易类型

最终,结束交易

与 RUM 的分布式追踪编辑

可能需要一些额外的设置才能正确地将请求与实时用户监控 (RUM) 代理关联。

有关启用跨域请求、设置服务器配置和使用动态生成的 HTML 的信息,请参阅RUM 分布式追踪指南