把可观测性数据送进 LLM 追踪平台
本文基于实际部署经验整理,实验环境为 Windows + WSL2 + Docker。
核心目标:让 OpenTelemetry Demo 采集 Github Copilot 的 traces,在不改动任何业务代码的前提下,额外转发一份到自托管的 Langfuse,实现 LLM 调用的全链路追踪。
背景
OpenTelemetry(OTel)是目前最主流的可观测性标准,覆盖 traces、metrics、logs 三类信号。很多团队已经用 OTel Demo 跑通了从应用到 Prometheus + Grafana 的链路。
这次的具体需求:我想看看 GitHub Copilot 具体的行为模式:它在代码补全、对话、Edits 等场景下分别发起了哪些 LLM 调用、调用了什么模型、耗时多少、token 用量如何。OTel Demo 已经在采集这些gen_ai.*语义约定的 traces,但 Prometheus + Grafana 只能看聚合指标,看不到单次调用的输入输出细节,也做不了多次调用的关联分析。
我们需要的是一个能把每一次 LLM 调用都当成一个可检索、可回放的事件来存储和分析的平台。
Langfuse 是专为 LLM 应用设计的可观测平台,支持标准 OTLP 协议接入,这意味着:只需改一行 OTel Collector 配置,就能把现有 traces 同时打到 Langfuse,零业务代码改动。
采集 LLM 交互信息的意义
采集并保存每一次 LLM 调用的输入/输出、模型、耗时、token 用量、调用上下文等信息,对团队和产品有多方面的价值:
- 可观测性与故障定位:将单次调用做为可检索事件,可以精确重现异常请求、定位模型返回错误或延迟的根因(如模型、网络、超参或输入质量问题)。
- 成本与计费归因:通过记录每次调用的 token 与耗时,能按功能、用户、产品线精确核算成本,支持异常扣费警报与成本优化决策。
- 性能优化与容量规划:按模型/接口统计延迟与吞吐趋势,识别瓶颈(带宽、显存、模型选择),指导量化、缓存或分层路由等优化策略。
- 模型行为分析与质量监控:可进行误答率、鲁棒性、偏差检测与回归测试,帮助评估不同模型/版本在真实流量下的表现。
- 产品与 UX 迭代:分析用户输入与模型输出的模式,发现常见失败场景或交互阻力,驱动提示词设计、候选排序或对话策略优化。
- A/B 实验与模型选择:将调用视为事件可用于分组实验(模型 A/B/n),量化改动对业务指标与体验的影响。
- 审计、合规与可回放:对敏感或合规场景,保留可回放记录支持审计、事后追踪与法律合规(需配合访问控制与保留策略)。
- 训练数据与持续学习:筛选高价值的真实交互作为微调或强化学习的候选样本,帮助持续改进模型质量。
注意事项:采集 LLM 交互数据同时伴随隐私与合规风险,实践中应至少做到:
- 最小化与脱敏:仅收集必要字段,对可能包含 PII 的文本做脱敏或加密;对高风险请求建立明确白名单/黑名单策略。
- 访问与保留策略:制定细粒度访问控制、审计日志与数据留存期限,避免长期存储不必要的敏感数据。
- 合规与告知:在需要的法律/监管场景(如金融、医疗)明确用户告知与同意,并与合规团队对接审查数据处理流程。
将这些实践与 Langfuse 的事件化 trace 能力结合,既能实现工程与业务层面的度量与改进,也能把可回放、审计与实验能力带入 LLM 产品化的常规流程。
整体架构
这里仅以我的验证环境为例,实际部署时需细致规划。
关键点:两套 Docker 栈运行在不同的 WSL 实例中,通过 WSL 的内部网络互通,OTel Collector 以 HTTP 协议将 traces 推送给 Langfuse 的 OTLP 入口。
部署概况
部署细节略,不是本文重点,仅简略说明。
- OTel Demo:官方 Docker Compose 一键启动,包含 20+ 微服务 + otel-collector + prometheus + jaeger + grafana
- Langfuse:官方 Docker Compose 一键启动,包含 web、worker、postgres、clickhouse、redis、minio
两套栈分别在不同 WSL 实例内运行,天然网络隔离。
排坑记录
实际操作中遇到了几个典型问题,记录在此供参考。
问题一:多 WSL 实例共享 Linux 内核导致 Docker 网络子网冲突
两个 WSL 实例(Ubuntu-24.04 和 langfuse)使用同一个 Linux 内核,Docker 默认都会尝试使用172.18.0.0/16,产生路由冲突,导致容器间 DNS 解析正确但 TCP 不通。
解法:给 Langfuse 的 Docker Compose 指定独立子网:
# docker-compose.override.yml(放在 langfuse 目录)networks:default:driver:bridgeipam:config:-subnet:172.25.0.0/16gateway:172.25.0.1问题二:iptables-legacy FORWARD 链默认 DROP
WSL 重启后,iptables-legacy的 FORWARD chain policy 变成 DROP,导致同一 Docker 网络内的容器之间无法通信(ping 通但 TCP 不通)。
表现:容器日志报Can't reach database server,但docker network inspect显示 IP 分配完全正确。
解法:持久化修复规则:
# /etc/rc.local#!/bin/bashiptables-legacy-PFORWARD ACCEPT问题三:WSL 内的 curl 走了系统代理返回 503
Langfuse 实际已正常启动(日志显示✓ Ready in 86.1s),但 curl 验证一直返回 503,因为环境变量中设置了 HTTP 代理,curl 把本地请求也转发给了代理服务器。
解法:验证时加--noproxy '*'参数。
核心配置:OTel Collector → Langfuse
这是本文的核心,只需修改一个文件:otelcol-config-extras.yml。
OTel Demo 的 Collector 支持通过 extras 文件叠加配置,不用动主配置文件。
第一步:生成 Langfuse API Key
登录 Langfuse 控制台,创建项目后在Settings → API Keys中获取:
- Public Key:
pk-lf-xxxxxxxx - Secret Key:
sk-lf-xxxxxxxx
将两者拼接后做 Base64 编码,作为 Basic Auth 凭证:
echo-n"pk-lf-xxxxxxxx:sk-lf-xxxxxxxx"|base64# 输出类似:cGstbGYteHh4eHh4eHg6c2stbGYteHh4eHh4eHg=第二步:编写 extras 配置
编辑src/otel-collector/otelcol-config-extras.yml:
# otelcol-config-extras.yml# 将 traces 额外转发到自托管 Langfuseexporters:otlp_http/langfuse:endpoint:http://<LANGFUSE_HOST_IP>:3000/api/public/otelheaders:Authorization:"Basic <上一步生成的Base64字符串>"tls:insecure:trueservice:pipelines:traces:# 在现有导出器基础上追加 langfuse,保留原有 jaeger 和 span_metricsexporters:[debug,otlp_grpc/jaeger,span_metrics,otlp_http/langfuse]注意:OTel Collector 合并多个配置文件时,
exporters是合并的,但pipeline.exporters数组是替换而非追加,所以必须把原有的导出器名称也写进去。
第三步:重启 OTel Collector
配置文件是以 volume mount 方式挂载的,修改后直接重启容器即可:
dockerrestart otel-collector查看日志确认 Langfuse 导出器已加载:
dockerlogs otel-collector2>&1|grep-ilangfuse正常输出(只有 deprecation 提示,无 error):
warn "otlphttp" alias is deprecated; use "otlp_http" instead {"otelcol.component.id": "otlphttp/langfuse", "otelcol.signal": "traces"}验证效果
Langfuse 收到 traces
通过 API 查询确认数据已入库:
curl--noproxy'*'\-u'pk-lf-xxxxxxxx:sk-lf-xxxxxxxx'\'http://<LANGFUSE_HOST_IP>:3000/api/public/traces?limit=5'成功返回 trace 列表,说明链路已打通:
{"data":[{"id":"c6d44c1d...","name":"chat gpt-4o-mini-2024-07-18","timestamp":"2026-05-29T02:43:05.639Z","projectId":"my-project",...}]}截图演示
Trace 全景:
Langfuse Traces 列表:
Trace 详情:
与 Jaeger 并行工作
Langfuse 接入后,原有的 Jaeger、Prometheus、Grafana 完全不受影响,traces 只是"多发了一份"。
两种平台的定位对比
| 维度 | Prometheus + Grafana | Langfuse |
|---|---|---|
| 擅长信号 | Metrics(时序) | Traces(LLM 调用链) |
| 适合场景 | 服务健康、资源监控 | LLM 输入输出、token 消耗、对话链路 |
| 接入方式 | OTel metrics pipeline | OTel traces pipeline(OTLP HTTP) |
| 数据存储 | TSDB | ClickHouse + PostgreSQL |
| 配置改动 | 已有 | 新增 extras 约 15 行 |
两者互补关系。
小结
整个接入过程的核心就是15 行 YAML:
- 在 extras 配置里声明一个新的
otlp_http/langfuseexporter - 在 traces pipeline 的 exporters 列表里追加它
- 重启 OTel Collector
Langfuse 对 OTLP 协议的原生支持是这一切能够顺滑实现的基础——不需要 SDK、不需要改业务代码、不需要额外的 agent,已有的 OTel instrumentation 产生的数据直接复用。
对于正在用 OTel 做可观测性、同时有 LLM 应用需要追踪的团队,这是成本最低的接入路径。
环境:Windows 11 + WSL2 + Docker Desktop / Docker Engine,OTel Demo v0.151.0,Langfuse v3.104.0