链路
Elastic Stack Serverless
链路是具有公共根的 事务 和 span 的集合。 每个链路跟踪单个请求的完整性。 它描述了单个逻辑操作产生的各个操作及其因果关系。
当链路通过多个服务时(这在微服务架构中很常见),它被称为**分布式链路**。 分布式链路包含跨多个分布式组件的操作,跨越进程、网络和安全边界。
分布式链路追踪使您能够通过跟踪整个请求(从前端服务上的初始 Web 请求到后端服务上发出的数据库查询)来分析整个微服务架构中的性能。
跟踪请求在服务中的传播过程,可以全面了解应用程序花费时间的位置、发生错误的位置以及形成瓶颈的位置。 分布式链路追踪消除了各个服务的数据孤岛,并揭示了服务边界之外发生的事情。
对于受支持的技术,分布式链路追踪开箱即用,无需额外的配置。
分布式链路追踪的工作原理是将自定义 traceparent
HTTP 标头注入到传出请求中。 此标头包含诸如 trace-id
之类的信息,用于标识当前链路;以及 parent-id
,用于标识传入请求上的当前 span 的父级,或传出请求上的当前 span。
当服务处理请求时,它会检查是否存在此 HTTP 标头。 如果缺少该标头,则服务会启动新的链路。 如果存在,则服务会确保将当前操作添加为现有链路的子级,并继续传播该链路。
在此示例中,Elastic 的 Ruby 代理与 Elastic 的 Java 代理通信。 两者都支持 traceparent
标头,并且链路数据已成功传播。

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

在此示例中,链路遇到一个不传播 traceparent
标头的中间件。 分布式链路结束,任何进一步的通信都将导致新的链路。

所有 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 代理仍然支持此标头。
APM 的时间线可视化提供了对应用程序的每个链路的视觉深度探索

Elastic 代理会自动传播受支持技术的分布式链路追踪上下文。 如果您的服务通过不同的、不受支持的协议进行通信,您可以手动将分布式链路追踪上下文从发送服务传播到接收服务,使用每个代理的 API。
发送服务必须将 traceparent
标头添加到传出请求。
不适用。
- 使用
StartTransaction
启动事务,或使用StartSpan
启动 span。 - 获取活动的
TraceContext
。 - 将
TraceContext
发送到接收服务。
示例
transaction := apm.DefaultTracer().StartTransaction("GET /", "request")
traceContext := transaction.TraceContext()
// Send TraceContext to receiving service
traceparent := apmhttp.FormatTraceparentHeader(traceContext)
tracestate := traceContext.State.String()
- 启动事务
- 从当前事务获取
TraceContext
- 将
TraceContext
或tracestate
格式化为traceparent
标头。
该代理将自动将链路标头注入到使用 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
...
span.end()
- 使用
startTransaction
启动事务,或使用startSpan
启动 span。 - 使用
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();
}
}
- 创建一个表示外部调用的 span
- 将
traceparent
标头注入到请求对象中 - 结束 span
- 使用
CurrentTransaction
或CurrentSpan
序列化活动事务或 span 的分布式链路追踪上下文。 - 将序列化的上下文发送到接收服务。
示例
string outgoingDistributedTracingData =
(Agent.Tracer.CurrentSpan?.OutgoingDistributedTracingData
?? Agent.Tracer.CurrentTransaction?.OutgoingDistributedTracingData)?.SerializeToString();
// Now send `outgoingDistributedTracingData` to the receiving service
- 使用
apm.startTransaction()
启动事务,或使用apm.startSpan()
启动 span。 - 使用
currentTraceparent
获取启动的事务/span 的序列化traceparent
字符串。 - 对
traceparent
进行编码,并在常规请求中将其发送到接收服务。
使用原始 UDP 在两个服务 A 和 B 之间进行通信的示例
agent.startTransaction('my-service-a-transaction');
const traceparent = agent.currentTraceparent;
sendMetadata(`traceparent: ${traceparent}\n`);
- 启动事务
- 获取当前的
traceparent
- 将
traceparent
作为标头发送到服务 B。
- 在客户端(即发送请求的一方)获取当前分布式链路追踪上下文。
- 将当前分布式链路追踪上下文序列化为请求传输支持的格式,并将其发送到服务器端(即接收请求的一方)。
示例
$distDataAsString = ElasticApm::getSerializedCurrentDistributedTracingData();
- 获取序列化为字符串的当前分布式链路追踪数据
- 使用
begin_transaction()
启动事务。 - 获取活动事务的
trace_parent
。 - 将
trace_parent
发送到接收服务。
示例
client.begin_transaction('new-transaction')<1>
elasticapm.get_trace_parent_header('new-transaction')
# Send `trace_parent_str` to another service
- 启动新事务
- 返回当前事务的
TraceParent
对象的字符串表示形式
- 使用
with_span
启动 span。 - 获取活动的
TraceContext
。 - 将
TraceContext
发送到接收服务。
ElasticAPM.with_span "Name" do |span|
header = span.trace_context.traceparent.to_header
# send the TraceContext Header to a receiving service...
end
- 启动 span
- 获取
TraceContext
接收服务必须解析传入的 traceparent
标头,并启动新的事务或 span 作为接收到的上下文的子级。
不适用。
- 使用
ParseTraceparentHeader
或ParseTracestateHeader
解析传入的TraceContext
。 - 使用
StartTransactionOptions
或StartSpanOptions
,启动一个新的事务或 span 作为传入事务的子项。
示例
// 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)
- 解析
TraceParent
标头 - 解析
Tracestate
标头 - 设置父跟踪上下文
- 启动一个新的事务,作为接收到的
TraceContext
的子项
不适用。
- 创建一个事务,作为传入事务的子项,使用
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 — 它们都有一个可选的 DistributedTracingData
参数。 这将创建一个新的事务或 span 作为传入跟踪上下文的子项。
启动新事务的示例
var transaction2 = Agent.Tracer.StartTransaction("Transaction2", "TestTransaction",
DistributedTracingData.TryDeserializeFromString(serializedDistributedTracingData));
- 在接收服务中解码并存储
traceparent
。 - 将
traceparent
作为childOf
选项传递,以手动启动一个新事务,作为接收到的traceparent
的子项,使用apm.startTransaction()
。
通过原始 UDP 接收 traceparent
的示例
const traceparent = readTraceparentFromUDPPacket()
agent.startTransaction('my-service-b-transaction', { childOf: traceparent })
- 从传入的请求中读取
traceparent
。 - 使用
traceparent
初始化一个新事务,该事务是原始traceparent
的子项。
- 在服务器端接收分布式跟踪数据。
- 使用代理的公共 API 开始一个新事务。 例如,使用
ElasticApm::beginCurrentTransaction
并将接收到的分布式跟踪数据(序列化为字符串)作为参数传递。 这将创建一个新的事务,作为传入跟踪上下文的子项。 - 不要忘记最终在服务器端结束事务。
示例
$receiverTransaction = ElasticApm::beginCurrentTransaction(
'GET /data-api',
'data-layer',
/* timestamp */ null,
$distDataAsString
);
- 启动新事务
- 传入接收到的分布式跟踪数据(序列化为字符串)
在接收服务中创建了新的事务后,您可以创建子 span,或者使用您通常使用的任何其他代理 API 方法。
- 从字符串或 HTTP 标头创建
TraceParent
对象。 - 通过传入
TraceParent
对象,启动一个新的事务,作为TraceParent
的子项。
使用 HTTP 标头的示例
parent = elasticapm.trace_parent_from_headers(headers_dict)
client.begin_transaction('processors', trace_parent=parent)
- 从形成字典的 HTTP 标头创建
TraceParent
对象 - 开始一个新事务,作为接收到的
TraceParent
的子项
有关其他示例,请参见 TraceParent
API。
使用 with_transaction
或 with_span
,启动一个新的事务或 span 作为传入事务或 span 的子项。
示例
# env being a Rack env
context = ElasticAPM::TraceContext.parse(env: env)
ElasticAPM.with_transaction("Do things", trace_context: context) do
ElasticAPM.with_span("Do nested thing", trace_context: context) do
end
end
- 解析传入的
TraceContext
- 创建一个事务,作为传入的
TraceContext
的子项 - 创建 span 作为新创建的事务的子项。 在这里,
trace_context
是可选的,因为 span 在没有传递trace_context
的情况下,将自动创建为父事务的TraceContext
的子项。
可能需要进行一些额外的设置,才能将请求与真实用户监控 (RUM) 代理正确关联。
有关启用跨域请求、设置服务器配置以及使用动态生成的 HTML 的信息,请参阅 RUM 分布式跟踪指南。