# Logging Best Practices

> 日志最佳实践，强调宽事件（canonical log lines），便于调试与分析。

- Skill: `majiayu000/logging-best-practices` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds add majiayu000/logging-best-practices`
- Raw SKILL.md: https://api.skillmd.com/api/skills/majiayu000/logging-best-practices/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- License: MIT
- Author: majiayu000 (https://skillmd.com/u/majiayu000)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/majiayu000/logging-best-practices

---


# Logging Best Practices Skill

## 目的

本技能提供应用内日志的实践指南，聚焦 **宽事件**（canonical log lines）：每个请求、每个服务只发出**一条**富含上下文的日志事件，便于调试与分析。

## 适用场景

在以下情况参考本指南：
- 编写或审查日志代码
- 使用 `console.log`、`logger.info` 等
- 为新服务设计日志策略
- 搭建日志基础设施

## 核心原则

### 1. 宽事件（关键）

每个请求、每个服务**只发一条**富含上下文的日志事件。不要在 handler 里到处打 log，而是在请求结束时汇总为一条结构化事件再输出。

```typescript
const wideEvent: Record = {
 method: 'POST',
 path: '/checkout',
 requestId: c.get('requestId'),
 timestamp: new Date().toISOString(),
};

try {
 const user = await getUser(c.get('userId'));
 wideEvent.user = { id: user.id, subscription: user.subscription };

 const cart = await getCart(user.id);
 wideEvent.cart = { total_cents: cart.total, item_count: cart.items.length };

 wideEvent.status_code = 200;
 wideEvent.outcome = 'success';
 return c.json({ success: true });
} catch (error) {
 wideEvent.status_code = 500;
 wideEvent.outcome = 'error';
 wideEvent.error = { message: error.message, type: error.name };
 throw error;
} finally {
 wideEvent.duration_ms = Date.now() - startTime;
 logger.info(wideEvent);
}
```

### 2. 高基数与高维度（关键）

包含高基数字段（如 user ID、request ID，百万级唯一值）和高维度（每条事件多字段）。便于按用户查询、回答未事先想到的问题。

### 3. 业务上下文（关键）

始终包含业务上下文：用户订阅档位、购物车金额、功能开关、账号龄等。目标是能知道「一名高级用户未能完成 2499 美元购买」，而不只是「结账失败」。

### 4. 环境特征（关键）

每条事件包含环境与部署信息：commit hash、服务版本、region、实例 ID。便于把问题与部署关联、定位区域性问题。

### 5. 单一 Logger（高）

启动时配置一个 logger 实例，全局复用。保证格式一致、自动带上环境上下文。

### 6. 中间件模式（高）

用中间件处理宽事件基础设施（计时、状态、环境、写出）。Handler 只负责补充业务上下文。

### 7. 结构与一致性（高）

- 统一使用 JSON 格式
- 跨服务保持字段命名一致
- 简化为两种级别：`info`、`error`
- 不记录非结构化字符串

## 反模式

1. **分散日志**：一个请求内多次 `console.log`
2. **多个 logger**：不同文件用不同实例
3. **缺环境上下文**：无 commit hash、部署信息
4. **缺业务上下文**：只有技术细节、无用户/业务数据
5. **非结构化字符串**：`console.log('something happened')` 而非结构化数据
6. **schema 不一致**：跨服务字段名不统一

## 指南摘要

### 宽事件

- 每个服务跳转发一条宽事件
- 包含所有相关上下文
- 用 request ID 串联事件
- 在 `finally` 中、请求结束时发出

### 上下文

- 支持高基数（user_id、request_id）
- 高维度（多字段）
- 始终含业务上下文
- 始终含环境特征（commit_hash、version、region）

### 结构

- 全代码库使用单一 logger
- 用中间件保证宽事件一致
- 使用 JSON 格式
- 保持 schema 一致
- 仅 info / error 两级
- 不记录非结构化字符串

### 常见坑

- 避免一个请求多行 log
- 为「未知的未知」设计
- 跨服务传递 request ID

## 参考

- [Logging Sucks](https://loggingsucks.com)
- [Observability Wide Events 101](https://boristane.com/blog/observability-wide-events-101/)
- [Stripe - Canonical Log Lines](https://stripe.com/blog/canonical-log-lines)

