前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片NEW
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 业务场景题真实业务问题与追问
  • 查漏补缺常见问题解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
    • AI 定制路线NEW按你的简历现排
    • AI 知识地图NEW串起全站知识点
  • 动态
    • AI 热点NEWAI 每日动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
AI 助手NEW
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片NEW
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 业务场景题真实业务问题与追问
  • 查漏补缺常见问题解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
    • AI 定制路线NEW按你的简历现排
    • AI 知识地图NEW串起全站知识点
  • 动态
    • AI 热点NEWAI 每日动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
AI 助手NEW
旧版
返回 AI 情报前线
All News · 全部资讯8836
  • MCP Nexus 1.0:MCP工具路由与发现层,一个端点代理数百工具
  • MCP网关安全:AI Agent生产部署的鉴权与限流实战
  • GitHub安全实验室:AI驱动的模糊测试实战
  • Meta Muse 被曝可被诱导泄露全文件系统
  • Black Forest Labs 发布开源机器人控制模型
  • Google Cloud API Gateway原生支持MCP协议
  • 3 万次 AI Agent 调用审计:可验证收据机制实践
  • 用 AgentCore Gateway 和 MCP 构建跨账号 AI Agent 架构
  • AI 性能成本每年下降 13 倍,超越所有前代技术
  • Lovable年化收入超6000万美元,vibe coding进入主流
  • Agent失败的根本原因:从未定义"完成"的标准
  • Agent补丁审查策略:second process重放验证才能合并
  • 我用 Claude Code 九天给 47 个服务接入 OpenTelemetry 链路追踪
  • Google Gemini CLI新增文件修改确认机制
  • DeepSeek推理优化:内核补齐+通信重构,吞吐翻7倍
  • Hugging Face推出LFM2.5-VL-DSpark视觉语言模型加速方案
  • 管理 15 万 AI Agent 对数据库团队的挑战与变革
  • Cursor 收购 Firetiger 后一月推出代码变更生产追踪 Bot
  • OpenAI AI Agent 自主入侵澳大利亚政府网站
  • Claude Code 实际项目开发全流程复盘
  • Google Cloud Developer 插件让 AI 编码助手直连云端部署
  • OpenAI Agent 入侵澳大利亚 Medicare 统计门户
  • Google DeepMind负责人透露Gemini 4即将发布
  • Impeccable:AI 编程代理的确定性设计语言框架
  • AI Agent 生产环境失败启示录:构建攻击性测试
  • 金融合规监控系统的工程实践:数据管道、Agent 架构与审计日志
  • 2026 年十大 LLM 网关横评:语义缓存与多提供商故障转移
  • codebase-memory-mcp:毫秒级代码知识图谱MCP服务器,158语言支持
  • Strands Agents:开源AI Agent开发框架,支持Python/TS全生命周期管理
  • 2026年9大开源LLM网关生产级对比:Bifrost领先
  • 用 TigerGraph+MCP 构建自主欺诈调查 Agent
  • Jev 决策模型真实成本拆解:何时省钱何时烧钱
  • 开源CLM-8B:Agent动作评分比Jev快9倍
  • JEV:打破布尔二值困境的类型安全验证方案
  • Claude Opus 5.5降价40%逼近前沿性能
  • VS Code 1.139:Agent 会话首次加载提速约 12 倍
  • 定时 Agent 总重复干活?用完成分类账让它知道什么是「做完」
  • TypeSafe AI Jev 编码指南:类型化决策、置信度校准与推测式广播
  • Vercel Connect 新增 TanStack AI 集成
  • Anthropic与OpenAI 90分钟内相继降价
  • 选LLM API的六个价格陷阱
  • Agent记忆正常仍出错:问题在状态不在记忆
  • Mercury 2.5 推理速度达 770 tokens/秒
  • 已加载 43 / 8836
9.0
重磅
AI SCORE
编程提效2026-09-24 22:32

我用 Claude Code 九天给 47 个服务接入 OpenTelemetry 链路追踪

dev.to · AI#Claude Code#OpenTelemetry#AI 编程
Editor brief · 编辑速览

作者通过手写参考实现 + 可执行校验器的方式,让 Claude Code 按规范为 47 个 Node.js/Python 服务统一注入 OpenTelemetry,将跨服务故障定位时间从 40 分钟压缩到分钟级,并总结了五点改进反思。

文章思维导图
Knowledge map
拖拽缩放
Full translation

完整中文译文

我们有 47 个后端服务,都输出结构化 JSON 日志,但没有分布式追踪。每当一个请求跨越四个服务边界、某处出现延迟时,调试过程就变成了这样:

在边缘服务的日志里找请求 ID。

希望下一个服务也记录了同样的请求 ID。

发现它用的 header 是 x-request-id 而不是 x-correlation-id。

放弃,往上加个 console.log。

我们的中等事故有 40 多分钟都花在"到底是哪个服务慢"上。我手动修复做了个时间盒估算,诚实地算了一下:47 个服务 × 每个约半天(setup、span 规范、配置、冒烟测试)≈ 六周专注工作。没人会批六周的基建活儿。

让这件事变得有意思的约束条件是:追踪埋点是重复的,但不是完全相同的。自动埋点能覆盖 70%,然后每个服务都有自己定制的那 30%——自定义的 HTTP 客户端、队列消费者、根本不是请求入口的 cron 任务。这种组合恰恰是 AI coding agent 的强项,也恰恰是它会悄悄自创规范的地方——如果你放任它的话。

步骤 1:手写参考实现

我的第一次尝试是一篇 2000 词的 CONVENTIONS.md,描述 span 应该如何命名、哪些属性是必填的、SDK 怎么接入。Agent 大概遵守了 70%,剩下的全靠即兴发挥——一半服务的属性名不一样,有的用 startSpan 有的用 startActiveSpan。

于是我把那篇文档扔掉,手动仔细地给一个服务做了埋点,让这个服务成为规范:

// tracing.js — 每个其他服务被要求对标的参考实现。
// 在进程启动时尽早 import 以产生副作用。
import { NodeSDK } from '@opentelemetry/sdk-node'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'
import { resourceFromAttributes } from '@opentelemetry/resources'
import {
  ATTR_SERVICE_NAME,
  ATTR_SERVICE_VERSION,
} from '@opentelemetry/semantic-conventions'

const sdk = new NodeSDK({
  resource: resourceFromAttributes({
    [ATTR_SERVICE_NAME]: process.env.SERVICE_NAME,
    [ATTR_SERVICE_VERSION]: process.env.GIT_SHA ?? 'dev',
    'deployment.environment.name': process.env.APP_ENV ?? 'local',
  }),
  traceExporter: new OTLPTraceExporter({
    url: `${process.env.OTEL_COLLECTOR_URL}/v1/traces`,
  }),
  instrumentations: [
    getNodeAutoInstrumentations({
      // 噪音大、零信号,还会让 span 数量翻倍。
      '@opentelemetry/instrumentation-fs': { enabled: false },
    }),
  ],
})

sdk.start()
process.on('SIGTERM', () => sdk.shutdown().finally(() => process.exit(0)))

然后是自动埋点做不到的那部分手动埋点——包装一个领域操作:

import { trace, SpanStatusCode } from '@opentelemetry/api'

const tracer = trace.getTracer('billing')

export async function chargeInvoice(invoiceId, amountCents) {
  // Span 名称 = "<domain>.<operation>",低基数,,永远不包含 ID。
  return tracer.startActiveSpan('billing.charge_invoice', async (span) => {
    try {
      span.setAttribute('billing.invoice_id', invoiceId)
      span.setAttribute('billing.amount_cents', amountCents)
      const result = await gateway.charge(invoiceId, amountCents)
      span.setAttribute('billing.gateway_status', result.status)
      return result
    } catch (err) {
      span.recordException(err)
      span.setStatus({ code: SpanStatusCode.ERROR, message: err.message })
      throw err
    } finally {
      span.end()
    }
  })
}

大约 90 行真实可运行、有明确主张的代码。把这个文件交给 agent、告诉它"照这个做,给这个服务加上",效果远比任何文字描述好得多。Agent 会做模式匹配,给它们一个模式。

步骤 2:把规范变成一个会失败的脚本

文字规范是无法执行的,"我觉得看起来对"在 47 个 PR 面前无法扩展。所以我花了半天写了一个验证器,告诉 agent:验证器不通过就不算完成:

# check_tracing.py — 在 CI 中逐服务运行。退出 1 会阻塞 PR。
import re, sys, pathlib

FORBIDDEN_ATTR = re.compile(
    r"setAttribute\(\s*['\"](?:.*\b(?:email|user_id|token|url|path|query)\b.*)['\"]"
)
SPAN_NAME = re.compile(r"startActiveSpan\(\s*['\"]([a-z0-9_]+\.[a-z0-9_]+)['\"]")
TEMPLATED_SPAN_NAME = re.compile(r"startActiveSpan\(\s*[`'\"].*\$\{")

def check(service: pathlib.Path) -> list[str]:
    errors = []
    if not (service / "tracing.js").exists():
        errors.append("missing tracing.js bootstrap")
    for f in service.rglob("*.js"):
        src = f.read_text()
        if TEMPLATED_SPAN_NAME.search(src):
            errors.append(f"{f}: interpolated span name (cardinality bomb)")
        for m in FORBIDDEN_ATTR.finditer(src):
            errors.append(f"{f}: high-cardinality or PII attribute: {m.group(0)}")
        for m in SPAN_NAME.finditer(src):
            if m.group(1).split(".")[0] != service.name.replace("-", "_"):
                errors.append(f"{f}: span namespace != service name: {m.group(1)}")
    return errors

if __name__ == "__main__":
    problems = check(pathlib.Path(sys.argv[1]))
    print("\n".join(problems) or "tracing conventions OK")
    sys.exit(1 if problems else 0)

这一个文件改变了整个项目的经济模型。在这之前,我审查的是品味;在这之后,我只审查判断决策——验证器在我打开 diff 之前就抓到了所有机械性违规。

步骤 3:证明 spans 真的能到达

一个服务可以通过所有静态检查但仍然什么都不导出,因为 SDK 在 HTTP 框架 import 之后才启动,或者 collector URL 写错了。所以每个服务还有一个冒烟测试:对着本地 collector 启动进程,并断言真实导出的 spans:

// tracing.smoke.test.js
test('http request produces a parented db span', async () => {
  await request(app).get('/invoices/42').expect(200)
  await flushSpans()

  const spans = collector.finished()
  const http = spans.find((s) => s.name === 'GET /invoices/:id')
  const db = spans.find((s) => s.name.startsWith('pg.query'))

  expect(http).toBeDefined()
  expect(db?.parentSpanContext?.spanId).toBe(http.spanContext().spanId)
  // 路由是模板化的,所以没有 invoice ID 泄露到 span 名称里。
  expect(http.name).not.toMatch(/42/)
})

那个父子断言抓住了最常见的真实故障:上下文传播静默断裂,产生 47 条互不相连的单-span"追踪",在列表视图里看起来没问题,但在瀑布图里毫无价值。

flowchart LR
    A[Pick next service<br/>grouped by framework] --> B[Agent instruments<br/>against reference impl]
    B --> C{check_tracing.py}
    C -- fail --> B
    C -- pass --> D{smoke test<br/>vs local collector}
    D -- fail --> B
    D -- pass --> E[Human review:<br/>judgment calls only]
    E --> F[PR merged]
    F --> A

两个验证器都在 agent 自己的循环里运行,所以大部分迭代都不需要我参与。我的工作是最后一个环节:这个服务自定义的队列消费者是否真的需要一个 span,这个属性是否值得它的存储成本。

九天,47 个服务,每天大约花我 90 分钟注意力。前六个服务用了四天,剩下的 41 个用了五天。

经验 1:一段可工作的参考实现胜过任何风格指南

我那篇 2000 词的规范文档产生了大约 70% 的遵守率。一段 90 行的参考文件立刻带来了近乎完美的结构遵守率。如果你发现自己正在写一大段描述代码应该长什么样的文字,停下来去写那段代码——然后指着它说"就这样"。

经验 2:规范必须可执行,否则等于不存在

验证器是这个项目杠杆最高的半天投入。任何你在 prompt 里写"请务必……"的东西,都应该是一个退出非零的脚本。Prompt 是建议;退出码是物理定律。这也意味着规范能脱离你而存在:下一个添加服务的人无需阅读任何文档就能获得同样的约束。

经验 3:基数是 agent 会伤到你的地方

放任不管的话,agent 写出的代码看起来确实合理,比如 startActiveSpan(charge invoice ${invoiceId}) 和 span.setAttribute('user.email', email)。两者都可以用"有描述性"来辩护。两者都是灾难性的——无界的 span 名称会摧毁你后端的索引和成本,而属性里的 PII 是驻留在遥测存储中、伴随整个保留期的合规事件。

Agent 不是不小心,它在优化可读性,因为没有任何东西告诉它去优化基数。这个权衡在 diff 里看不见,在生产环境里却很贵——这恰恰是适合放在 linter 里、而不是 review 评论里的规则类型。

经验 4:加埋点是机械的;删旧日志不是

我的原始计划包括"删除现在冗余的日志"。第二天我就把这部分从 agent 的 scope 里删掉了。判断一条日志是否和 span 冗余,需要知道凌晨三点谁会去 grep 它——那是代码库里没有的部落知识,而 agent 自信地删掉 on-call 运行手册里承重的日志行,是个糟糕的取舍。

按信息可用性分割工作,而不是按难度。机械的 90% 交给 agent。需要活在某人脑子里的上下文的那 10%,留给人类。

经验 5:按框架批量,而不是按字母顺序

我按仓库顺序开始,每个服务都要重新建立上下文,token 烧得厉害。按技术栈批量——所有 Fastify,然后所有 Express,然后所有 FastAPI——意味着每个批次复用同一个心智模型、同一堆坑和同一个刚解决的问题。同样工作量,明显更少的 token 和错路。

相邻的任务比打乱顺序的任务便宜。按相似度而不是按便利性排序你的待办列表。

后续三件事

采样策略。我们目前 staging 是 100% 的头部采样,已经很吵了。尾部采样保留每条错误追踪和 5% 的无聊追踪,是显而易见的下一步。

追踪驱动的性能工作。现在有了瀑布图,我想把真实的慢追踪反馈给 agent,作为优化的起点,而不是模糊的"这个端点感觉慢"的 prompt。

自动生成的服务依赖图。追踪里已经编码了真实的调用图,包括没人记得存在的三条调用。自动渲染出来比手工维护架构图强。

这个模式可以泛化到追踪以外任何大规模、重复性、强规范迁移——feature flag SDK、错误上报、认证中间件——都符合同样的结构:一份手写的参考、一个可执行的验证器、一个证明它活着的冒烟测试,然后让 agent 去磨。

如果你正因为"要六周无聊的活儿"而搁置一个"某天再说"的可观测性迁移,它大概已经不是六周了。但杠杆不在 prompt 里——在你开始之前写的参考实现和验证器里。

如果你尝试这个,我真的很想知道你的验证器最终抓到了什么。我抓到了 31 个我本来会合并进去的基数违规。

Original source

本文由 AI 翻译整理自 dev.to · AI,原文版权归原作者所有。

阅读英文原文
上一篇
Agent补丁审查策略:second process重放验证才能合并
下一篇
Google Gemini CLI新增文件修改确认机制