日志与链路追踪:如何快速定位线上问题|架构思考

日志不是堆在一起,而是要能串起来。

场景与痛点

这篇文章面向正在扩张的团队,从可维护性视角深入拆解日志与链路追踪。当前定位为「深挖」阶段,核心目标是规模化演进与成本优化。我们会从实际场景出发,结合具体代码示例,把关键知识点拆解为可落地的行动步骤。衡量标准:代码复杂度/变更频率。

线上出了问题,第一反应是”看日志”。但当你面对几十个服务、每秒产生上万条日志时,“看日志”变成了大海捞针。更糟糕的是,一个用户请求可能经过 API 网关、用户服务、订单服务、支付服务、通知服务——每个服务都有自己的日志,怎么把它们串起来?这就是链路追踪要解决的问题。结构化日志 + Trace ID 是现代后端系统可观测性的基石。

当团队规模是正在扩张的团队时,最大的挑战不是”不会做”,而是”做了但不可复用、不可追溯”。在需要多端联动的复杂需求的背景下,我们需要一套既轻量又可靠的方案。

核心原理

传统的 console.log 式日志有三个致命问题:格式不统一(有的是纯文本,有的是 JSON),无法检索(只能 grep 关键词),无法关联(跨服务的日志没有关联关系)。当系统规模增长后,这种日志几乎没有排障价值。结构化日志解决了前两个问题,链路追踪解决了第三个问题。两者结合,你可以在几秒内定位到”用户 A 在 14:32:05 的下单请求,在支付服务的第三方回调环节超时了”。

分步实施指南

结构化日志的核心是:每条日志都是一个 JSON 对象,包含固定的字段集合。必要字段包括:timestamp(时间戳)、level(日志级别)、service(服务名)、traceId(链路 ID)、msg(消息)。可选字段包括:userId、requestId、duration、error 等。

链路追踪的实现原理:在请求入口(通常是 API 网关)生成一个全局唯一的 Trace ID,通过 HTTP Header(如 X-Trace-Id)在服务间传递。每个服务在处理请求时,将 Trace ID 写入所有日志。这样,通过 Trace ID 就能检索到一个请求在所有服务中的完整日志链路。

OpenTelemetry 是目前最主流的可观测性标准,它统一了 Traces(链路)、Metrics(指标)、Logs(日志)三种信号的采集和传输协议。建议新项目直接使用 OpenTelemetry SDK,避免被特定厂商锁定。

实战代码

以下代码片段经过简化,可以直接用于项目中:

// 结构化日志工具封装
const logger = {
  info(msg, meta = {}) {
    console.log(JSON.stringify({
      level: 'info',
      timestamp: new Date().toISOString(),
      service: process.env.SERVICE_NAME,
      traceId: meta.traceId || 'unknown',
      msg,
      ...meta
    }));
  },
  error(msg, error, meta = {}) {
    console.error(JSON.stringify({
      level: 'error',
      timestamp: new Date().toISOString(),
      service: process.env.SERVICE_NAME,
      traceId: meta.traceId || 'unknown',
      msg,
      error: { message: error.message, stack: error.stack },
      ...meta
    }));
  }
};

// Express 中间件:注入 Trace ID
app.use((req, res, next) => {
  req.traceId = req.headers['x-trace-id'] || crypto.randomUUID();
  res.setHeader('X-Trace-Id', req.traceId);
  logger.info('request start', {
    traceId: req.traceId,
    method: req.method,
    path: req.path
  });
  next();
});

进阶实践

日志级别使用规范:ERROR 用于需要立即处理的异常,WARN 用于可能的问题但不影响主流程,INFO 用于关键业务节点(请求开始/结束、状态变更),DEBUG 用于开发调试(生产环境关闭)。告警策略:ERROR 日志触发即时告警,WARN 日志按频率告警(5分钟内超过 100 条),INFO 日志不告警但可检索。

踩坑记录

常见错误:日志中包含敏感信息(密码、Token、身份证号);日志量过大导致存储成本失控(应该做采样和分级存储);告警太多导致”狼来了”效应(应该分级和抑制);以及 Trace ID 没有贯穿全链路(某个服务忘记传递了,链路就断了)。

下一步行动

如果你正处于深挖阶段,建议先把核心链路的代码复杂度/变更频率监控建立起来,然后按照上面的步骤逐项推进。记住,降低理解和修改代码的成本不是一蹴而就的,而是持续迭代的过程。每次改进后都要回看数据,确认效果符合预期。