关于 structured-logging
structured-logging 是用于设计可查询 JSON 日志、排查分布式服务故障的开发技能,涵盖字段、上下文传递、级别与性能建议,而非日志库。
主要功能
- 01JSON 键值日志格式
- 02必需字段与关联 ID
- 03日志级别选择建议
- 04跨服务上下文传递
- 05避免敏感数据与噪声
- 06性能与采样建议
适用场景
- 01统一各服务的事件字段
- 02追踪请求在分布式系统中的流转
- 03为故障和预期事件选择级别
- 04在事故调查中检查日志
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ majesticlabs-dev/ majestic-marketplace --skill structured-logging安装后在 Claude Code 中输入/structured-logging确认已加载。
详细介绍
结构化日志
核心理念
- 日志的优化目标是查询,而非写入——设计时要考虑调试需求
- 在分布式系统中,没有关联 ID 的日志毫无用处
- 如果无法在 5 分钟内回答“影响了谁?出了什么问题?何时发生?为什么发生?”,就需要改进日志
结构化格式
始终使用键值对(JSON),切勿使用字符串插值。
{
"event": "payment_failed",
"user_id": "123",
"reason": "insufficient_funds",
"amount": 99.99,
"timestamp": "2025-01-24T20:00:00Z",
"level": "error",
"service": "billing",
"request_id": "req_abc123"
}
必需字段
每条日志事件都必须包含:
| 字段 | 格式 | 示例 |
|---|---|---|
timestamp | 带时区的 ISO 8601 | 2025-01-24T20:00:00Z |
level | debug、info、warn、error | info |
event | snake_case,过去时 | user_login_succeeded |
request_id 或 trace_id | UUID 或带前缀的 ID | req_abc123 |
service | 服务/应用名称 | api-gateway |
environment | prod、staging、dev | prod |
高基数值字段
如有可用,请包含以下字段——它们有助于在事件期间查询日志:
| 类别 | 字段 |
|---|---|
| 身份 | user_id、org_id、account_id |
| 追踪 | request_id、trace_id、span_id |
| 领域 | order_id、transaction_id、job_id |
**规则:**寻找有助于将问题定位到特定实体的领域专用标识符。
日志级别
| 级别 | 使用时机 | 示例 |
|---|---|---|
debug | 详细的本地开发信息,在 prod 中禁用 | 变量值、循环迭代 |
info | 值得记录的常规操作 | 用户操作、作业完成、部署 |
warn | 意外但已处理的情况 | 触发重试、启用回退机制 |
error | 操作失败,需要关注 | 异常、请求失败、超时 |
**反模式:**不要为预期情况记录错误(密码错误 = info,而非 error)。
上下文传播
对于分布式系统:
- 继承 ID——下游服务必须接收来自上游的关联 ID
- 跨边界传递——HTTP 标头、消息队列、异步作业
- 通过中间件注入——通过中间件/拦截器自动将上下文注入每条日志
[Client] --request_id--> [API Gateway] --request_id--> [Service A] --request_id--> [Service B]
| | |
(logs) (logs) (logs)
↓ ↓ ↓
All queryable by single request_id
**异步作业:**处理后台工作时,存储并恢复原始请求上下文。
记录哪些内容
| 记录这些内容 | 跳过这些内容 |
|---|---|
| 记录请求进入/退出及其持续时间 | 敏感数据(密码、令牌、PII、银行卡信息) |
| 状态转换(created → paid → shipped) | 紧密循环内部 |
| 记录外部服务调用的延迟和状态 | 没有调试价值的成功情况 |
| 认证/授权事件 | 冗余的基础设施日志(负载均衡器已记录) |
| 作业启动、完成、失败 | |
| 重试次数、熔断器状态变化 |
命名约定
| 模式 | 示例 |
|---|---|
字段名称:snake_case | user_id,而非 userId 或 user-id |
| 事件:过去时动词 | payment_completed,而非 complete_payment |
| 适用时使用领域前缀 | auth.login_failed、billing.invoice_created |
**团队约定:**统一定义字段名称,并在所有服务中保持一致。
性能
| 问题 | 解决方案 |
|---|---|
| 高吞吐量的 debug 日志 | 在生产环境中采样 |
| 热路径日志记录 | 避免记录,或使用异步追加器 |
| I/O 开销 | 缓冲并批量写入 |
| 动态详细程度 | 支持在运行时配置日志级别 |
特定语言的实现
| 语言 | 库 | 备注 |
|---|---|---|
| Python | structlog | 参见 majestic-data/etl-core-patterns |
| Ruby/Rails | Rails.event(8.1+)、semantic_logger | 参见 majestic-rails/dhh-coder/structured-events |
| Node.js | 使用 JSON 格式化器的 pino、winston | |
| Go | slog(标准库)、zerolog | |
| Java | 使用 JSON 编码器的 logback |
决策表:是否记录日志?
| 场景 | 决策 | 原因 |
|---|---|---|
| 用户输入错误的密码 | info | 预期行为,并非错误 |
| 支付网关超时 | error + 重试 | 需要关注,会影响用户 |
| 缓存未命中 | debug | 仅对性能分析有用 |
| 用户创建了账户 | info | 值得记录的业务事件 |
| 10000 次循环中的第 5000 次迭代 | 不记录 | 会产生噪声,没有调试价值 |
| 外部 API 返回 500 | warn 或 error | 取决于重试或回退行为 |
| 后台任务已启动 | info | 有助于调试任务 |
| 后台任务重试后仍失败 | error | 需要调查 |
事件调试清单
设计日志时,请确认你能回答以下问题:
- 谁 — 能否筛选出特定用户/组织/账户?
- 什么 — 能否确定失败的具体操作?
- 何时 — 能否缩小到特定时间范围?
- 为什么 — 是否记录了错误上下文(原因、上游原因)?
- 哪里 — 能否通过关联 ID 跟踪跨服务调用?
事件结束后: 补上你当时希望已有的日志。
评论
支持 Markdown