追踪
编辑追踪
编辑追踪是一组具有共同根节点的事务和跨度。每个追踪都跟踪单个请求的完整过程。它描述了单个逻辑操作产生的各个操作及其因果关系。
分布式追踪
编辑当追踪经过多个服务时(这在微服务架构中很常见),它被称为分布式追踪。分布式追踪包含跨越多个分布式组件的操作,跨越进程、网络和安全边界。
分布式追踪使您能够通过追踪整个请求(从前端服务的初始 Web 请求一直到后端服务进行的数据库查询)来分析整个微服务架构的性能。
跟踪请求在服务中的传播,可以全面了解应用程序在哪些地方花费时间、错误发生在哪里以及瓶颈在哪里。分布式追踪消除了各个服务的的数据孤岛,并揭示了服务边界之外发生的情况。
对于支持的技术,分布式追踪无需任何额外配置即可开箱即用。
分布式追踪通过将自定义的traceparent
HTTP 头注入到传出请求中来工作。此头包含信息,例如trace-id
(用于标识当前追踪)和parent-id
(用于在传入请求中标识当前跨度的父级,或在传出请求中标识当前跨度)。
当服务处理请求时,它会检查此 HTTP 头是否存在。如果不存在,服务将启动一个新的追踪。如果存在,服务将确保当前操作作为现有追踪的子级添加,并继续传播追踪。
在此示例中,Elastic 的 Ruby 代理与 Elastic 的 Java 代理通信。两者都支持traceparent
头,并且追踪数据成功传播。
在此示例中,Elastic 的 Ruby 代理与 OpenTelemetry 的 Java 代理通信。两者都支持traceparent
头,并且追踪数据成功传播。
在此示例中,追踪遇到一段不传播traceparent
头的中间件。分布式追踪结束,任何进一步的通信都将产生新的追踪。
所有 Elastic 代理现在都支持官方的 W3C 追踪上下文规范和traceparent
头。请参见下表以了解最低所需的代理版本。
代理名称 | 代理版本 |
---|---|
Go 代理 |
≥ |
Java 代理 |
≥ |
.NET 代理 |
≥ |
Node.js 代理 |
≥ |
PHP 代理 |
≥ |
Python 代理 |
≥ |
Ruby 代理 |
≥ |
RUM 代理 |
≥ |
较旧的 Elastic 代理使用唯一的elastic-apm-traceparent
头。为了向后兼容,新版本的 Elastic 代理仍然支持此头。
应用程序 UI 的时间线可视化功能提供了对应用程序每个追踪的深入可视化分析。
Elastic 代理会自动为支持的技术传播分布式追踪上下文。如果您的服务通过不同的、不支持的协议进行通信,您可以使用每个代理的 API 手动将分布式追踪上下文从发送服务传播到接收服务。
traceparent
头发送服务必须向传出请求添加traceparent
头。
不适用。
- 使用
StartTransaction
启动事务或使用StartSpan
启动跨度。 - 获取活动的
TraceContext
。 - 将
TraceContext
发送到接收服务。
示例
代理将自动使用URLSessions
将追踪头注入网络请求,但如果您使用的是非标准网络库,则可能需要手动注入它们。这将使用 OpenTelemetry API 完成。
- 创建一个
Setter
- 根据Open Telemetry 标准为每个请求创建一个
Span
- 将追踪上下文注入头字典
- 按照网络库的步骤完成网络请求。确保在请求成功或失败时调用
span.end()
。
import OpenTelemetryApi import OpenTelemetrySdk struct BasicSetter: Setter { func set(carrier: inout [String: String], key: String, value: String) { carrier[key] = value } } let span : Span = ... let setter = BasicSetter() let propagator = W3CTraceContextPropagator() var headers = [String:String]() propagator.inject(spanContext: span.context, carrier: &headers, setter:setter) let request = URLRequest(...) request.allHTTPHeaderFields = headers ... // make network request span.end()
- 使用
startTransaction
启动事务,或使用startSpan
启动跨度。 - 使用
injectTraceHeaders
将traceparent
头注入请求对象。
手动检测 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(); } }
- 使用
CurrentTransaction
或CurrentSpan
序列化活动事务或跨度的分布式追踪上下文。 - 将序列化的上下文发送到接收服务。
示例
string outgoingDistributedTracingData = (Agent.Tracer.CurrentSpan?.OutgoingDistributedTracingData ?? Agent.Tracer.CurrentTransaction?.OutgoingDistributedTracingData)?.SerializeToString(); // Now send `outgoingDistributedTracingData` to the receiving service
- 使用
apm.startTransaction()
启动事务,或使用apm.startSpan()
启动跨度。 - 使用
currentTraceparent
获取已启动事务/跨度的序列化traceparent
字符串。 - 编码
traceparent
并将其作为常规请求的一部分发送到接收服务。
使用原始 UDP 在两个服务 A 和 B 之间进行通信的示例
- 在客户端(即发送请求的一方)获取当前的分布式追踪上下文。
- 将当前分布式追踪上下文序列化为请求传输支持的格式,并将其发送到服务器端(即接收请求的一方)。
示例
- 使用
begin_transaction()
启动事务。 - 获取活动事务的
trace_parent
。 - 将
trace_parent
发送到接收服务。
示例
- 使用
with_span
启动跨度。 - 获取活动的
TraceContext
。 - 将
TraceContext
发送到接收服务。
traceparent
头接收服务必须解析传入的traceparent
头,并作为接收上下文的子级启动新的事务或跨度。
不适用。
- 使用
ParseTraceparentHeader
或ParseTracestateHeader
解析传入的TraceContext
。 - 使用
StartTransactionOptions
或StartSpanOptions
作为传入事务的子级启动新的事务或跨度。
示例
// Receive incoming TraceContext traceContext, _ := apmhttp.ParseTraceparentHeader(r.Header.Get("Traceparent")) traceContext.State, _ = apmhttp.ParseTracestateHeader(r.Header["Tracestate"]...) opts := apm.TransactionOptions{ TraceContext: traceContext, } transaction := apm.DefaultTracer().StartTransactionOptions("GET /", "request", opts)
不适用。
- 使用
startTransactionWithRemoteParent()
创建一个作为远程父级的子事务。 - 使用
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(); } }
反序列化传入的分布式追踪上下文,并将其传递给任何StartTransaction
或CaptureTransaction
API(所有这些 API 都有一个可选的DistributedTracingData
参数)。这将创建一个作为传入追踪上下文子级的新事务或跨度。
启动新事务的示例
var transaction2 = Agent.Tracer.StartTransaction("Transaction2", "TestTransaction", DistributedTracingData.TryDeserializeFromString(serializedDistributedTracingData));
- 解码并将
traceparent
存储在接收服务中。 - 将
traceparent
作为childOf
选项传入,以手动启动作为接收到的traceparent
子级的新事务,方法是使用apm.startTransaction()
。
通过原始 UDP 接收traceparent
的示例
- 在服务器端接收分布式追踪数据。
- 使用代理的公共 API 开始新的事务。例如,使用
ElasticApm::beginCurrentTransaction
并将接收到的分布式追踪数据(序列化为字符串)作为参数传递。这将创建一个作为传入追踪上下文子项的新事务。 - 不要忘记最终在服务器端结束事务。
示例
$receiverTransaction = ElasticApm::beginCurrentTransaction( 'GET /data-api', 'data-layer', /* timestamp */ null, $distDataAsString );
一旦在接收服务中创建了这个新事务,就可以创建子跨度,或者像往常一样使用任何其他代理 API 方法。
- 从字符串或 HTTP 头创建
TraceParent
对象。 - 通过传入
TraceParent
对象,作为TraceParent
的子项启动一个新事务。
使用 HTTP 头的示例
parent = elasticapm.trace_parent_from_headers(headers_dict) client.begin_transaction('processors', trace_parent=parent)
有关更多示例,请参阅TraceParent
API。
使用with_transaction
或with_span
,作为传入事务或跨度的子项启动一个新事务或跨度。
示例
可能需要一些额外的设置才能正确地将请求与实时用户监控 (RUM) 代理关联。
有关启用跨域请求、设置服务器配置和使用动态生成的 HTML 的信息,请参阅RUM 分布式追踪指南。