排查一次支付故障,找到报错日志只是开始。还要沿着调用链看时间花在了哪里,再确认失败的请求涉及哪些订单。如果日志存在 Elasticsearch 或 Loki,链路存在 Jaeger 或 Tempo,指标由 Prometheus 管理,这些信息就需要在不同系统之间核对。进一步分析业务影响时,数据工程师还得把可观测数据与订单数据关联起来,增加了数据导出、字段整理和拼表的工作。
OpenTelemetry(下文简称 OTel)为日志、链路和指标提供了统一的采集与传输规范。应用通过 SDK 或自动埋点产生数据,经由 OTLP 协议发送给 Collector,再由 Collector 处理和导出。数据最终存在哪里、怎样跨信号查询,以及如何关联业务表,仍需要后端来解决。
湖仓和分析数据库也在沿着这个方向接入可观测数据。对已经使用 Databend 做数据分析的团队来说,把 OTel 数据接入现有湖仓,就能沿用熟悉的 SQL 查询方式,并在业务表已入仓的前提下,直接分析故障与订单之间的关系。
Databend 提供的 OpenTelemetry Collector exporter 负责这一步接入,将 logs、traces、metrics 写成三张普通表。下面用 Databend Cloud 上的一次实际演示,看看这条路径如何跑通。本文截图、动图和视频均来自这次演示。应用与订单数据由模拟程序生成,文中的查询结果来自实际运行。
接入原理与数据结构

数据从应用到查询,经过以下三个步骤:
-
应用使用 OTel SDK 或自动埋点,通过 OTLP 发给 Collector;
-
Collector 里的
exporter 把每批数据编码成 NDJSON,通过 Databend 原生的 HTTP Streaming Load 接口写入;databend -
数据落在
、otel_logs、otel_traces三张表里,在 Worksheet、BendSQL、BI 工具或 Grafana 里用 SQL 查询。otel_metrics
exporter 在写入方式和数据保留上做了以下设计:
**通过 Databend 原生接口写入。**exporter 直接调用 Databend
/v1/streaming_load
**支持三类信号和五种指标类型。**gauge、sum、histogram、exponential histogram、summary 均可写入,各类型中的 exemplar、bucket 边界、min/max 和分位数等信息也会保留。
数据原样保留,不做有损压平。 每行是一条日志、一个 span 或一个指标数据点。常用的过滤字段(时间、服务名、trace_id、span_id、名称、指标类型)单独成列,完整的 OTLP 对象放在
VARIANT
record
时间戳精确到纳秒。 Databend 的 TIMESTAMP 使用微秒精度,因此这里用
UInt64
用 Collector 标准能力做可靠性。 发送队列(可配持久化存储)、重试退避、超时、TLS、认证扩展都直接复用 Collector 的 exporterhelper 和 confighttp,配置方式和其他 exporter 一样。
支持自动建表,也可按最小权限配置。 默认
create_schema: true
schema.sql
create_schema: false
跑通 Demo,将三类数据写入 Databend Cloud
演示代码、完整配置和复现步骤见 Demo 说明。下面按连接、编译、启动和发送数据的顺序操作,也可以先看这段约 50 秒的终端演示:

第 1 步:获取连接信息
登录 app.databend.com,点首页右上角的 Connect。exporter 需要的是 Host(形如
<tenant>.gw.<region>.default.databend.com

截图中的租户信息已打码。
建议给 exporter 单独建一个用户,不要用管理员账号。在 Worksheet 中执行以下 SQL:
CREATE ROLE otel_exporter_demo_role;
-- 只有 create_schema: true 时需要
GRANT CREATE DATABASE ON *.* TO ROLE otel_exporter_demo_role;
CREATE USER otel_exporter_demo IDENTIFIED BY '<强密码>'
WITH DEFAULT_ROLE = 'otel_exporter_demo_role';
GRANT ROLE otel_exporter_demo_role TO otel_exporter_demo;
第 2 步:编译包含 Databend exporter 的 Collector
本次演示使用的 exporter 尚未纳入 OpenTelemetry Collector Contrib 官方发行版,需要通过 OpenTelemetry Collector Builder(ocb)构建自定义 Collector。仓库已提供 builder 配置:
git clone https://github.com/databendlabs/opentelemetry-exporter-databend
cd opentelemetry-exporter-databend
go run go.opentelemetry.io/collector/cmd/builder@v0.164.0 \
--config examples/builder-config.yaml
## 产物:./_build/otelcol-databend
需要 Go 1.26 或更高版本。如果你已经有自己的 Collector 发行版,把 exporter 加到 builder 配置的
exporters
第 3 步:配置并启动 Collector
以下列出 exporter 和数据管道的主要配置,完整配置见文档 https://github.com/databendlabs/opentelemetry-exporter-databend/tree/main/examples:
exporters:
databend:
endpoint: ${env:DATABEND_ENDPOINT} # https://<Host>
username: ${env:DATABEND_USERNAME}
password: ${env:DATABEND_PASSWORD}
headers:
X-Databend-Warehouse: ${env:DATABEND_WAREHOUSE}
database: otel_demo
create_schema: true
sending_queue:
enabled: true
retry_on_failure:
enabled: true
service:
pipelines:
logs: { receivers: [otlp], processors: [memory_limiter, batch], exporters: [databend] }
traces: { receivers: [otlp], processors: [memory_limiter, batch], exporters: [databend] }
metrics: { receivers: [otlp], processors: [memory_limiter, batch], exporters: [databend] }
凭据通过环境变量注入,无需写入配置文件:
export DATABEND_ENDPOINT=https://<tenant>.gw.aws-us-east-2.default.databend.com
export DATABEND_USERNAME=otel_exporter_demo
export DATABEND_PASSWORD='<强密码>'
export DATABEND_WAREHOUSE=default
./_build/otelcol-databend --config collector-cloud.yaml
本次演示中,出现
Everything is ready. Begin running and processing data.

第 4 步:发送数据并确认入库
可以使用支持 OTLP 的程序发送数据。本地接收地址为
127.0.0.1:4317
127.0.0.1:4318
OTEL_EXPORTER_OTLP_ENDPOINT
演示使用一个模拟电商应用,一次结账请求依次经过 frontend、checkout、inventory、payment 四个服务。大约 8% 的请求会在支付环节失败(
card declined

回到 Worksheet,可以看到三张表中已经写入数据:

这次一共模拟了 2,100 次结账,写入 6,300 条日志、13,628 个 span、693 个指标数据点。本次运行中,Collector 未出现 error 或 warn 日志。
用 SQL 排查支付故障,分析订单影响
数据写入后,就可以在 Worksheet 中查询。先从报错日志入手,再通过
trace_id

从日志中定位支付错误
SELECT to_timestamp((time_unix_nano // 1000)::Int64) AS ts, service_name,
record:body:stringValue::STRING AS message,
record:attributes[0]:value:stringValue::STRING AS error,
trace_id
FROM otel_demo.otel_logs
WHERE record:severityText::STRING = 'ERROR'
ORDER BY time_unix_nano DESC LIMIT 20;

报错集中在 payment 服务,原因是
card declined: insufficient funds
trace_id
用 trace_id 还原请求调用链
从日志中选取一个
trace_id
repeat()
SELECT service_name, name AS span_name, offset_ms, duration_ms,
repeat('·', floor(offset_ms / 40)::INT)
|| repeat('█', greatest(1, ceil(duration_ms / 40))::INT) AS timeline,
status
FROM (SELECT service_name, name, time_unix_nano,
record:status:message::STRING AS status,
round((time_unix_nano - min(time_unix_nano) OVER ()) / 1e6, 1) AS offset_ms,
round((record:endTimeUnixNano::UInt64
- record:startTimeUnixNano::UInt64) / 1e6, 1) AS duration_ms
FROM otel_demo.otel_traces WHERE trace_id = '<trace_id>')
ORDER BY time_unix_nano;

这次请求总耗时 482 ms,库存检查用了 20 ms,
PaymentService/Charge
统计各服务的 P95 延迟与错误数
可以直接根据 span 计算延迟和错误数,无需为这次查询预先定义指标:
SELECT service_name, name AS span_name, count(*) AS spans,
round(avg(dur_ms), 1) AS avg_ms,
round(quantile_cont(0.95)(dur_ms), 1) AS p95_ms,
sum(if(record:status:code::INT = 2, 1, 0)) AS errors
FROM (SELECT *, (record:endTimeUnixNano::UInt64
- record:startTimeUnixNano::UInt64) / 1e6 AS dur_ms
FROM otel_demo.otel_traces)
GROUP BY service_name, span_name
ORDER BY p95_ms DESC;

结账接口的 P95 延迟为 450.9 ms,支付环节的 P95 为 430.2 ms,inventory 的 P95 仅为 37.4 ms。
关联日志与链路,统计失败订单金额
接下来可以把技术故障与订单影响放在一起分析。本例的日志记录了订单金额,span 记录了处理耗时,通过
trace_id
span_id
SELECT l.service_name,
l.record:attributes[0]:value:stringValue::STRING AS error,
count(*) AS failed_orders,
round(avg((t.record:endTimeUnixNano::UInt64
- t.record:startTimeUnixNano::UInt64) / 1e6), 1) AS avg_charge_ms,
round(sum(l.record:attributes[1]:value:doubleValue::DOUBLE), 2) AS lost_revenue_usd
FROM otel_demo.otel_logs l
JOIN otel_demo.otel_traces t ON l.trace_id = t.trace_id AND l.span_id = t.span_id
WHERE l.record:severityText::STRING = 'ERROR'
GROUP BY ALL;

本次模拟中,177 笔订单因扣款失败未成交,涉及金额 36,058.96 美元。日志与链路存入 Databend Cloud 后,一次 JOIN 就能将失败原因、订单金额和支付耗时放在同一份结果中。如果订单表也在 Databend Cloud 中,还可以继续按用户、渠道或商品分析影响。
查看直方图指标与关联链路
指标表的每一行对应一个数据点,保留了直方图的 bucket 计数、边界和 min/max。exemplar 中的
trace_id

将查询结果转为请求量趋势图
Worksheet 支持将查询结果切换为图表视图。下图展示了结账接口按 10 秒窗口统计的请求量:

把可观测数据纳入湖仓,能带来什么?
Databend 是云原生湖仓,基于对象存储保存数据,将计算与存储分离,并提供 SQL 分析能力。结构化的业务表和半结构化的日志、链路可以在同一平台中查询。Databend Cloud 提供托管服务,让团队按工作负载配置计算资源。这样的架构为可观测数据的长期保留和关联分析提供了基础。
结合上面的 Demo,用户往往更看重的是数据写入之后能做什么。排查支付故障时,我们已经能在同一条查询中看到错误原因、处理耗时和失败订单金额。如果业务数据也在 Databend Cloud 中,后续就可以继续分析故障影响了哪些用户、哪些渠道,而无需先把日志导出到另一个分析系统。
对数据工程师来说,可观测数据可以和业务数据一起纳入日常分析。要让这样的分析方便开展,既需要统一的查询方式,也需要考虑数据保留、字段变化和现有工具的接入。Databend Cloud 在这些方面提供了相应支持:
三类信号使用同一套 SQL。 日志、链路和指标写入同一引擎后,可以通过
trace_id
span_id
与已有业务表直接关联。 当订单、用户和账单数据已经在 Databend Cloud 中,并具备可用的关联字段时,可以直接用 SQL 分析业务影响,无需为一次排查额外导出和搬运数据。分析也可以从单次故障延伸到一段时间内的失败分布。
按保留周期和查询负载分别配置资源。 对象存储承载历史数据,计算资源按写入和查询需求配置。团队可以根据复盘、容量规划或审计的需要决定日志与链路的保留时长,避免仅因历史数据增加就同步扩大计算规模。实际成本仍取决于数据量、保留周期和读写负载。
保留动态属性,按分析需要提取字段。 OTel 属性会随服务和版本变化,
VARIANT
record:attributes
record:status:code
沿用已有的 OTel 采集方式。 对已经通过 OTLP 向 Collector 发送数据的应用,接入 Databend Cloud 主要是在 Collector 中增加 exporter 和管道配置。需要同时写入其他后端时,也可以配置多个 exporter,保留已有的监控流程。
继续使用熟悉的分析工具。 数据以普通表的形式存储,可以通过支持 Databend 的 BI 工具查询。Databend 的 Grafana 数据源插件也支持用 SQL 配置看板和告警。需要由 AI Agent 辅助查询时,同样可以复用这些表和 SQL 接口。
组件状态与使用边界
本文演示使用的 exporter 是开发阶段的独立组件,尚未进入 OpenTelemetry Collector Contrib 官方发行版。团队计划在收集社区反馈后推动捐赠,使用时需了解以下边界:
-
投递语义是 at-least-once:如果写入已经提交但连接在返回前断了,重试可能产生重复行,目前没有去重;
-
队列溢出、重试耗尽或进程异常退出(未开持久化队列时)可能丢数据,生产环境建议开启持久化队列,并配置 batch 和 memory_limiter;
-
表结构变更由运维负责,
不会迁移已有表;没有自动 TTL;CREATE TABLE IF NOT EXISTS -
暂不支持 Profiles 信号;
本次演示对应版本的单元测试包含 race 检测,覆盖率为 90.4%;CI 会启动真实的 Databend 实例运行端到端测试,校验五种指标类型以及纳秒时间戳和整数精度。本文 Demo 已在 Databend Cloud(v1.2.952)上完整运行。
获取代码与演示资料
代码和文档:github.com/databendlabs/opentelemetry-exporter-databend
本文的演示代码、全部 SQL 和 Collector 配置位于仓库的
目录,欢迎试用,并通过 Issue 或 PR 反馈问题。如果已经在生产环境接入,也欢迎分享数据规模和使用场景,帮助我们确定后续的优化方向。demo/
订阅我们的新闻简报
及时了解功能发布、产品规划、支持服务和云服务的最新信息!






