Grafana Tempo 自动日志(Automatic Logging):通过日志实现 Trace 发现与定位
【免费下载链接】tempoGrafana Tempo is a high volume, minimal dependency distributed tracing backend.项目地址: https://gitcode.com/GitHub_Trending/tempo1/tempo
当你运行一个已接入分布式追踪(instrumented)的系统时,最大的难题之一往往是"如何知道系统中存在哪些 Trace"。Grafana Tempo 生态中的Automatic logging(自动日志)功能,通过让 Grafana Alloy 为流经追踪管道的每个 span、root span 或 process 生成格式良好的日志行,把 Trace ID 直接写进日志,从而让你在 Loki 中按键值对检索 Trace,并从一条日志一键跳转到 Grafana 中的对应 Trace 视图。读完本文,你将掌握自动日志的完整配置方法(OTLP 端点与 Loki 两种目标)、默认日志字段与自定义属性、Loki 中的 LogQL 查询技巧,以及用 TraceQL 实现等价检索的原生替代方案。
Automatic logging 与 TraceQL 的定位差异
自动日志解决的是"Trace 可发现性"问题:在没有自动日志时,日志与 Trace 彼此割裂,排查问题时往往要从海量日志里手工翻找 Trace ID。而 Automatic logging 则让 Alloy 在追踪管道中自动生成包含 Trace ID 的日志行,写入 Loki,使日志成为进入 Trace 世界的入口。
不过,Tempo 的原生 TraceQL 检索现在已经能提供与自动日志等价的 Trace 发现能力,而且不需要部署 Loki、也不会额外产生日志数据量。TraceQL 可以按服务名(service name)、span 名、持续时间、状态码以及自定义属性检索 Trace;如果需要可视化、免写查询的探索方式,也可以使用 Grafana 的 Traces Drilldown。
自动日志仍然有它的适用场景:如果你的工作流以 Loki 为中心(例如排障时习惯先看应用日志),自动日志会把 Trace ID 和 span 元数据与你的应用日志放在一起,让你在调查日志数据的同时发现 Trace,而不必切换到独立的 Trace 检索界面。这正是"日志优先(logs-first)"工作流的典型诉求。
开始前的前置条件
使用 Automatic logging 需要以下组件就绪:
- Grafana Alloy:已安装并正在从你的应用接收 Trace。Alloy 是 Tempo 文档中推荐的采集端,兼容 OpenTelemetry Collector 与 Prometheus Agent,其配置使用 Alloy 配置语法(见 Grafana Alloy 章节)。
- Loki 实例:用于存储自动日志生成的日志数据。
- Tempo 实例:用于存储 Trace,以便从日志导航到 Trace。
- Grafana:配置好 [Loki 数据源] 与 Tempo 数据源,二者缺一不可。
配置 Automatic logging
Automatic logging 的核心是otelcol.connector.spanlogs这个 connector 组件:它接收 Trace 并生成日志行,但不会把原始 Trace 继续向下游转发。因此配置时必须把 Trace 同时发给spanlogsconnector 与你的 Trace 后端,否则 Trace 数据会丢失。
对于高吞吐系统,如果对每个 span 都写日志,日志量可能过大。此时应改为按 root span 或 process 粒度记录。connector 会在 span 或 resource 属性中检索一组配置的键,并将其作为日志中的键值对输出,随后你就可以在 Loki 中按这些键值对进行检索。
将 root span 日志发送到 OTLP 端点
下面的示例记录 root span 的日志,并把生成的日志发送到 OTLP 端点。同时,Trace 也被转发到同一端点,确保 Trace 数据不丢失:
otelcol.receiver.otlp "default" { grpc {} http {} output { traces = [ otelcol.connector.spanlogs.default.input, otelcol.exporter.otlp.default.input, ] } } otelcol.connector.spanlogs "default" { roots = true output { logs = [otelcol.exporter.otlp.default.input] } } otelcol.exporter.otlp "default" { client { endpoint = env("OTLP_ENDPOINT") } }这个配置体现了 Alloy 管道"接收器 → 处理器/连接器 → 导出器"的架构(可参考 Alloy 追踪管道架构说明 中的alloy-pipeline-architecture.svg图示):otelcol.receiver.otlp的 output 是一个数组,把 Trace 同时分发(fan-out)给 spanlogs connector 与 OTLP exporter,这正是上文"不能丢失 Trace"的关键写法。roots = true表示只对 root span 生成日志,适合高吞吐场景控制日志量。
将带自定义属性的 root span 日志发送到 Loki
下面的示例记录 root span,并额外携带http.method与http.target属性,随后把生成的日志发送到 Loki 实例。Trace 则单独转发到 Tempo 实例。
由于otelcol.exporter.loki默认不会把日志属性提升为 Loki 标签,你必须使用otelcol.processor.attributes添加一个loki.attribute.labels提示,把traces属性提升为 Loki 标签,这样就能按日志类型过滤:
otelcol.receiver.otlp "default" { grpc {} http {} output { traces = [ otelcol.connector.spanlogs.default.input, otelcol.exporter.otlp.tempo.input, ] } } otelcol.connector.spanlogs "default" { roots = true span_attributes = ["http.method", "http.target"] output { logs = [otelcol.processor.attributes.default.input] } } otelcol.processor.attributes "default" { action { key = "loki.attribute.labels" action = "insert" value = "traces" } output { logs = [otelcol.exporter.loki.default.input] } } otelcol.exporter.loki "default" { forward_to = [loki.write.local.receiver] } loki.write "local" { endpoint { url = "http://loki:3100/loki/api/v1/push" } } otelcol.exporter.otlp "tempo" { client { endpoint = "tempo:4317" } }要点拆解:
span_attributes = ["http.method", "http.target"]:指定要写入日志的 span 属性键,它们会成为日志行中可检索的键值对。otelcol.processor.attributes中action = "insert"、key = "loki.attribute.labels"、value = "traces":这是让 Loki exporter 把traces属性提升为标签的关键步骤,否则 LogQL 中无法用{traces="root"}这种标签选择器过滤。otelcol.exporter.loki通过forward_to指向loki.write.local.receiver,由loki.write组件最终把日志推送到 Loki 的 push API。- Trace 出口与日志出口分离:Trace 走
otelcol.exporter.otlp.tempo(端点tempo:4317,即 Tempo 的 OTLP gRPC 端口),日志走 Loki,各司其职。
从仓库示例可以看到,这种 Alloy 配置与 example/docker-compose/distributed/config.alloy 中的 OTLP receiver/exporter 写法一脉相承(该示例中 receiver 同样同时开启grpc与http端点),可以直接作为搭建实验环境时对照的蓝本。
预期输出:默认日志字段与格式
启用自动日志后,connector 会根据你开启的选项,为每个 span、root 或 process 生成一行日志。每行日志使用logfmt 风格的正文,默认键如下:
| Key | Description |
|---|---|
svc | span 所属 resource 中的服务名(service name)。 |
span | span 的名称。 |
dur | span 的持续时间(纳秒),例如150200000ns。 |
tid | Trace ID。 |
status | span 的状态。仅在状态被显式设置(非STATUS_CODE_UNSET)时出现。取值为STATUS_CODE_OK或STATUS_CODE_ERROR。 |
你可以通过配置span_attributes、process_attributes或event_attributes来增加更多键,也可以用overrides块自定义所有键名。
例如,一条 root span 日志可能长这样:
span="HTTP GET" dur=150200000ns http.method=GET http.target=/api/v1/query svc=my-service tid=7bba9f33312b3dbb8b2c2c62bb7abe2d除了上述键值对,每条日志还带有一个traces属性,用来标识日志类型:span、root、process或event。如果你按上文 Loki 示例配置了loki.attribute.labels提示,这个属性就会变成 Loki 标签,可以直接在 LogQL 中过滤。
在 Loki 中查询自动日志数据
在 Grafana Explore 中使用 LogQL 查询自动日志。
查找所有 root span 日志:
{traces="root"}过滤某个服务的慢请求(时长超过 2 秒):
{traces="root"} | logfmt | dur > 2s and svc="my-service"这里| logfmt解析器把 logfmt 格式的日志正文解析为字段,之后就能直接对dur(纳秒)与svc做比较运算。
TraceQL 等价查询
下面这些 TraceQL 查询提供了与上述 LogQL 查询相同的 Trace 发现能力,且不需要自动日志,也不需要 Loki 实例。
查找某个服务的全部 Trace:
{ resource.service.name = "my-service" }查找某个服务的慢 Trace(持续时间超过 2 秒):
{ resource.service.name = "my-service" && span:duration > 2s }查找错误 Trace:
{ status = error }按特定属性检索:
{ span.http.method = "GET" && span.http.target = "/api/v1/query" }需要说明的是,TraceQL 中的span:duration表示单个 span 的起止时间差(end - start),而非整条 Trace 端到端的trace:duration,二者语义不同(详见 构造 TraceQL 查询 中关于 duration 的说明)。此外,status = error这种按状态过滤的写法,以及resource.service.name、span.http.method这类属性选择器,都在 construct-traceql-queries.md 的示例中有对应的完整用法(如{resource.service.name = "frontend" && name = "POST /api/orders" && status = error})。TraceQL 是 Tempo 的原生查询语言,其语法与语义与 PromQL、LogQL 相似(见 TraceQL 总览),如果你已熟悉日志查询,上手成本很低。
从日志跳转到 Trace
要把日志行与其 Tempo 中的 Trace 直接关联起来,需要在 Grafana 的 Loki 数据源上配置derived fields(派生字段):
- 进入Connections>Data sources,选择你的 Loki 数据源。
- 在Derived fields中添加新字段,设置如下:
- Name:
TraceID - Type:Label
- Match field name:
tid - 勾选Internal link,并指向你的 Tempo 数据源
- Name:
- 保存数据源配置。
配置完成后,Explore 中的日志结果会在tid字段旁边显示一个链接,点击即可直接跳转到对应 Trace 的瀑布视图。Tempo 首页也提到,借助 Grafana 的 derived fields 支持,可以在 LogQL 过滤出关心的请求后一键跳转到 Trace(参见 Tempo 项目首页)。
总结
Automatic logging 通过otelcol.connector.spanlogs组件把 Trace 元数据自动落成日志,适合以 Loki 为中心的排障工作流:日志行默认携带svc、span、dur、tid、status键,可扩展自定义 span/process/event 属性,配合loki.attribute.labels提示实现按traces标签过滤,再通过 derived fields 实现"日志 → Trace"的一键跳转。若你不需要 Loki 且希望减少日志量,Tempo 原生的 TraceQL 提供了完全等价且更轻量的检索路径,两种方案互为补充,你可以根据团队现有的观测技术栈选择。
【免费下载链接】tempoGrafana Tempo is a high volume, minimal dependency distributed tracing backend.项目地址: https://gitcode.com/GitHub_Trending/tempo1/tempo
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考