可观测性与错误追踪:日志、Trace 与告警闭环

系统讲解现代应用可观测性体系:日志、指标与链路追踪(三支柱)、结构化日志与上下文注入、OpenTelemetry 集成、边缘函数与 Serverless 的可观测性挑战(日志聚合/冷启动/分布式追踪)、错误追踪与 Sourcemap 还原、告警规则与 SLO、以及可观测性闭环与成本控制。

一、引言

「系统出问题了,但不知道出在哪」是运维最痛的事。可观测性(Observability)解决的就是这个:通过日志、指标、链路追踪三支柱,让你能回答「发生了什么、现在状态如何、某个请求经历了什么」。而在 Serverless/边缘架构下,函数无状态、实例瞬息万变,可观测性更是唯一能「看见」分布式行为的窗口。

本文系统讲可观测性:先建立三支柱框架,再深入结构化日志与上下文注入、OpenTelemetry 集成、Serverless 的可观测性挑战(日志聚合/冷启动/跨边缘追踪)、错误追踪与 Sourcemap 还原、告警与 SLO,最后给出可观测性闭环设计与成本控制。

关联:https://plumephp.com/tools-frontend-monitoring-rum/(前端监控 RUM)、https://plumephp.com/tools-edge-cache-cdn-strategy/(边缘缓存)、https://plumephp.com/tools-serverless-cold-start/(Serverless 形态)、https://plumephp.com/vercel-analytics-speed-insights/(Vercel 监控)。


二、三支柱:日志、指标、链路追踪

2.1 三个支柱的分工

支柱回答的问题典型工具
日志(Logs)发生了什么(事件明细)Datadog/Loki/CloudWatch
指标(Metrics)现在状态如何(数值聚合)Prometheus/StatsD
追踪(Traces)一个请求经历了什么(调用链)Jaeger/Tempo/OTel
三者互补:
  指标 → 发现「服务慢了」(报警)
  日志 → 查「哪个请求慢」(明细)
  追踪 → 追「慢在哪一环」(调用链)

2.2 从「监控」到「可观测性」

监控:你知道要问什么(预设指标)→ 看仪表盘
可观测性:你不知道要问什么 → 随时下钻(日志+追踪+上下文)
关键:上下文(context)——每个日志带 requestId、user、service 维度

一句话总结:日志管事件、指标管状态、追踪管调用链——三支柱合起来让你能「从报警下钻到某一行代码」。


三、结构化日志与上下文注入

3.1 结构化日志(不要 print 字符串)

// 反例:无法检索
console.log('order created: ' + orderId)

// 正例:结构化 + 上下文
console.log(JSON.stringify({
  level: 'info',
  event: 'order.created',
  orderId,
  userId,
  requestId,      // 贯穿全链路
  service: 'orders-api',
  latencyMs: 123
}))

3.2 上下文注入(Context Propagation)

// 中间件生成并传播 requestId
export async function middleware(request: Request) {
  const requestId = request.headers.get('x-request-id') || crypto.randomUUID()
  request.headers.set('x-request-id', requestId)
  return NextResponse.next({ request })
}
// 每个函数日志都带 requestId → 全链路可串

3.3 日志分级与采样

debug:开发期用,生产不开(量大)
info :关键事件(创建/更新/登出)
warn :可恢复异常(重试/降级)
error:真实故障(带 stack + 上下文)
采样:高频 info 按比例采样,error 全量

一句话总结:结构化日志 + requestId 上下文 = 可检索、可串链路;分级控制量与采样降成本。


四、OpenTelemetry 集成

4.1 OTel 的核心概念

统一采集:Logs/Metrics/Traces 统一 API 与协议
自动埋点:SDK 自动捕获 HTTP/DB/消息
导出:OTLP 协议导出到任意后端(Jaeger/Tempo/Datadog/自建)
// 初始化 OTel(Node.js)
import { NodeSDK } from '@opentelemetry/sdk-node'
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'

const sdk = new NodeSDK({
  traceExporter: new OTLPTraceExporter({ url: process.env.OTEL_EXPORTER }),
  instrumentations: [getNodeAutoInstrumentations()]
})
sdk.start()

4.2 手动埋点(关键业务)

import { trace } from '@opentelemetry/api'

const tracer = trace.getTracer('payment')
export async function processOrder(orderId: string) {
  const span = tracer.startSpan('order.process')
  span.setAttribute('order.id', orderId)
  try {
    // ... 业务
    span.setStatus({ code: SpanStatusCode.OK })
  } catch (e) {
    span.setStatus({ code: SpanStatusCode.ERROR })
    span.recordException(e)
    throw e
  } finally {
    span.end()
  }
}

4.3 供应商中立

OTel 的价值是「一次埋点、任意后端」——避免锁定供应商
迁移后端只改 exporter 配置,埋点代码不动

一句话总结:OTel 统一三支柱采集、自动埋点、OTLP 导出——供应商中立,迁移后端只换 exporter。


五、Serverless 可观测性的挑战

5.1 无状态与日志聚合

挑战:函数实例瞬灭,日志散落各地
解法:平台日志(CloudWatch/Workers Logs)+ 结构化 → 聚合到集中存储
      用 requestId/version 维度检索

5.2 冷启动的观测

// 记录冷启动(模块级一次性)
const started = Date.now()
let coldStart = false
if (!globalThis.__warm) {
  globalThis.__warm = true
  coldStart = true
}
// 日志带 coldStart 标记 → 聚合看冷启动占比

5.3 跨边缘/跨服务追踪

边缘函数 → API → DB/第三方,每跳都是独立实例
需要 trace context(W3C traceparent)传播:
  HTTP 头带 traceparent → 下游续接 span
边缘平台(Vercel/Cloudflare)逐步内置 trace 支持

一句话总结:Serverless 可观测的难点是「无状态 + 跨边缘」——结构化日志聚合、显式标记冷启动、trace context 跨跳传播。


六、错误追踪与 Sourcemap 还原

6.1 前端错误与堆栈还原

前端打包后堆栈全是压缩代码 → 用 Sourcemap 还原源码
步骤:
  1. 构建时生成 .map(不上传到 CDN,仅上传监控平台)
  2. 错误上报带堆栈 → 平台用 map 还原
  3. 关联用户/版本/路径 → 定位
// 前端错误上报(示意)
window.addEventListener('error', (e) => {
  fetch('/api/track', {
    method: 'POST',
    body: JSON.stringify({
      message: e.message,
      stack: e.error?.stack,   // 压缩堆栈 → 平台还原
      path: location.pathname,
      version: __BUILD_ID__
    })
  })
})

6.2 服务端错误聚合

后端 error 日志 → 聚合平台 → 按「签名」分组(同错误合并计数)
签名 = 堆栈首几行哈希 → 快速看「哪个错误影响多少人」

6.3 错误分级的响应

P0(核心功能全挂)→ 立即告警 + 自动回滚
P1(局部功能异常)→ 告警 + 排查
P2(低影响)      → 日报合并处理
P3(噪音)        → 采样/静默

一句话总结:错误追踪 = 前端 Sourcemap 还原源码 + 后端按签名聚合 + 分级响应;堆栈还原与分组计数是「快速定位影响面」的关键。


七、告警与 SLO

7.1 SLO 定义

SLO(服务目标):如「P95 延迟 < 500ms,99% 请求成功」
SLI(度量):错误率、延迟、可用性
SLO 是「可量化的质量契约」→ 触发时即告警

7.2 告警规则设计

好的告警:指向问题、可行动、不噪音
规则示例:
  错误率 5xx > 1%(5 分钟)→ 告警
  P95 延迟 > 基线 2 倍(10 分钟)→ 告警
  SLO 烧钱率 > 阈值 → 告警

反模式:告警疲劳(阈值过敏感)→ 会被忽略

7.3 告警闭环

告警 → 定位(日志+追踪下钻)→ 修复 → 验证 → 复盘
闭环要求:每条告警有「runbook」(如何排查)

一句话总结:SLO 定质量目标、告警按目标触发、闭环到复盘——告警的关键是「可行动、不噪音、有 runbook」。


八、可观测性闭环设计

观测层:日志 + 指标 + 追踪(OTel 统一采集)
分析层:聚合、分组、下钻(requestId 串链路)
决策层:告警 + SLO + 看板
行动层:回滚 / 降级 / 扩容 / 修复
反馈层:复盘 → 补指标 / 补告警 → 持续改进

核心链路:用户请求 → 边缘(trace)→ 函数(log+metric)→ 数据库(span)
→ 平台聚合 → 报警 → 定位 → 修复

一套最小闭环:

1. 全站埋点(OTel + RUM)
2. 集中日志(结构化 + requestId)
3. 关键指标看板(错误率/P95/可用性)
4. SLO + 分级告警
5. Sourcemap 还原 + 签名聚合
6. 告警 runbook + 自动回滚兜底

一句话总结:可观测性闭环 = 采集(OTel+RUM)→ 分析(聚合+下钻)→ 决策(SLO+告警)→ 行动(回滚/修复)→ 反馈(复盘补测)。


九、成本与治理

9.1 可观测性成本怎么爆

日志量指数增长 → 存储贵
Trace 全量采样 → 昂贵
告警噪音 → 人效下降

9.2 成本控制策略

手段做法
采样trace 按比例采样(头部采样),error 全量
日志分级debug 不存储、info 限 TTL
压缩/裁剪去敏感字段、限制字段数
告警收敛分组、去抖、分级
存储分层热 7 天、温 30 天、冷归档

9.3 隐私与合规

日志可能含 PII(用户信息)→ 脱敏后再存储
保留策略符合合规(GDPR 等)→ 限 TTL
敏感字段不进日志(密码/token)

一句话总结:可观测性成本 = 采样控 trace、分级控日志、收敛控告警;PII 脱敏与保留策略是合规底线。


十、速查表

需求方案
事件明细结构化日志 + requestId
状态数值指标(Prometheus 等)
调用链OTel Trace + traceparent
统一采集OpenTelemetry
前端还原Sourcemap 上传监控平台
错误聚合签名分组 + 计数
质量目标SLO/SLI
告警错误率/延迟阈值 + runbook
冷启动观测显式标记 + 日志维度
成本控制采样 + 分级 + TTL

一句话记忆:可观测性 = 日志(事件)+ 指标(状态)+ 追踪(调用链)三支柱,OTel 统一采集、供应商中立;结构化日志带 requestId 才能串链路,Sourcemap 还原前端堆栈、签名聚合后端错误;SLO 定目标、分级告警可行动、闭环到复盘;Serverless 的难点是跨边缘 trace 与冷启动观测;成本靠采样、分级、TTL 控制——观测不是堆工具,是能「从报警下钻到代码」的能力。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「tools」更多文章

  1. 部署与回滚策略:蓝绿、金丝雀与不可变部署实战
  2. 边缘认证与会话管理:JWT、Cookie 与 Serverless 登录实战
  3. 全球部署与合规:多区域、数据主权与 GDPR 落地