go-logging
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseGo Logging
Go 日志处理
Core Principle
核心原则
Logs are for operators, not developers. Every log line should help someone
diagnose a production issue. If it doesn't serve that purpose, it's noise.
日志是给运维人员看的,而非开发人员。每一条日志都应能帮助排查生产环境问题。如果无法达到这个目的,那这条日志就是冗余信息。
Choosing a Logger
选择日志库
Normative: Usefor new Go code.log/slog
slogWhich logger?
├─ New production code → log/slog
├─ Trivial CLI / one-off → log (standard)
└─ Measured perf bottleneck → zerolog or zap (benchmark first)Do not introduce a third-party logging library unless profiling shows
is a bottleneck in your hot path. When you do, keep the same structured
key-value style.
slogRead references/LOGGING-PATTERNS.md when setting up slog handlers, configuring JSON/text output, or migrating from log.Printf to slog.
规范要求:新的Go代码请使用。log/slog
slog如何选择日志库?
├─ 新的生产代码 → log/slog
├─ 简单CLI工具/一次性脚本 → log(标准库)
└─ 经测试的性能瓶颈场景 → zerolog或zap(先做基准测试)除非性能分析显示是热路径中的瓶颈,否则不要引入第三方日志库。如果必须引入,请保持相同的结构化键值对风格。
slog当设置slog处理器、配置JSON/文本输出,或从log.Printf迁移到slog时,请阅读references/LOGGING-PATTERNS.md。
Structured Logging
结构化日志
Normative: Always use key-value pairs. Never interpolate values into the message string.
The message is a static description of what happened. Dynamic data goes in
key-value attributes:
go
// Good: static message, structured fields
slog.Info("order placed", "order_id", orderID, "total", total)
// Bad: dynamic data baked into the message string
slog.Info(fmt.Sprintf("order %d placed for $%.2f", orderID, total))规范要求:始终使用键值对。切勿将动态值插入到消息字符串中。
消息部分是对事件的静态描述。动态数据应放在键值属性中:
go
// 推荐:静态消息+结构化字段
slog.Info("order placed", "order_id", orderID, "total", total)
// 不推荐:动态数据嵌入消息字符串
slog.Info(fmt.Sprintf("order %d placed for $%.2f", orderID, total))Key Naming
键命名规范
Advisory: Usefor log attribute keys.snake_case
Keys should be lowercase, underscore-separated, and consistent across the
codebase: , , .
user_idrequest_idelapsed_ms建议:日志属性键使用格式。snake_case
键名应为小写、下划线分隔,且在整个代码库中保持一致:、、。
user_idrequest_idelapsed_msTyped Attributes
类型化属性
For performance-critical paths, use typed constructors to avoid allocations:
go
slog.LogAttrs(ctx, slog.LevelInfo, "request handled",
slog.String("method", r.Method),
slog.Int("status", code),
slog.Duration("elapsed", elapsed),
)Read references/LEVELS-AND-CONTEXT.md when optimizing log performance or pre-checking with Enabled().
在性能敏感路径中,使用类型化构造函数避免内存分配:
go
slog.LogAttrs(ctx, slog.LevelInfo, "request handled",
slog.String("method", r.Method),
slog.Int("status", code),
slog.Duration("elapsed", elapsed),
)当优化日志性能或使用Enabled()预检查时,请阅读references/LEVELS-AND-CONTEXT.md。
Log Levels
日志级别
Advisory: Follow these level semantics consistently.
| Level | When to use | Production default |
|---|---|---|
| Debug | Developer-only diagnostics, tracing internal state | Disabled |
| Info | Notable lifecycle events: startup, shutdown, config loaded | Enabled |
| Warn | Unexpected but recoverable: deprecated feature used, retry succeeded | Enabled |
| Error | Operation failed, requires operator attention | Enabled |
Rules of thumb:
- If nobody should act on it, it's not Error — use Warn or Info
- If it's only useful with a debugger attached, it's Debug
- should always include an
slog.Errorattribute"err"
go
slog.Error("payment failed", "err", err, "order_id", id)
slog.Warn("retry succeeded", "attempt", n, "endpoint", url)
slog.Info("server started", "addr", addr)
slog.Debug("cache lookup", "key", key, "hit", hit)Read references/LEVELS-AND-CONTEXT.md when choosing between Warn and Error or defining custom verbosity levels.
建议:始终遵循以下级别语义。
| 级别 | 使用场景 | 生产环境默认设置 |
|---|---|---|
| Debug | 仅用于开发人员诊断、追踪内部状态 | 禁用 |
| Info | 重要的生命周期事件:启动、关闭、配置加载完成 | 启用 |
| Warn | 意外但可恢复的情况:使用了已废弃特性、重试成功 | 启用 |
| Error | 操作失败,需要运维人员关注 | 启用 |
经验法则:
- 如果不需要任何人采取行动,那它就不是Error级别——使用Warn或Info级别
- 只有在连接调试器时才有用的内容,使用Debug级别
- 必须始终包含
slog.Error属性"err"
go
slog.Error("payment failed", "err", err, "order_id", id)
slog.Warn("retry succeeded", "attempt", n, "endpoint", url)
slog.Info("server started", "addr", addr)
slog.Debug("cache lookup", "key", key, "hit", hit)当在Warn和Error级别之间做选择,或定义自定义详细级别时,请阅读references/LEVELS-AND-CONTEXT.md。
Request-Scoped Logging
请求范围日志
Advisory: Derive loggers from context to carry request-scoped fields.
Use middleware to enrich a logger with request ID, user ID, or trace ID, then
pass the enriched logger downstream via context or as an explicit parameter:
go
func middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
logger := slog.With("request_id", requestID(r))
ctx := context.WithValue(r.Context(), loggerKey, logger)
next.ServeHTTP(w, r.WithContext(ctx))
})
}All subsequent log calls in that request carry automatically.
request_idRead references/LOGGING-PATTERNS.md when implementing logging middleware or passing loggers through context.
建议:从上下文派生日志实例,以携带请求范围的字段。
使用中间件为日志实例添加请求ID、用户ID或追踪ID,然后通过上下文或显式参数将增强后的日志实例传递到下游:
go
func middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
logger := slog.With("request_id", requestID(r))
ctx := context.WithValue(r.Context(), loggerKey, logger)
next.ServeHTTP(w, r.WithContext(ctx))
})
}该请求后续的所有日志调用都会自动携带字段。
request_id当实现日志中间件或通过上下文传递日志实例时,请阅读references/LOGGING-PATTERNS.md。
Log or Return, Not Both
日志或返回错误,不要同时做
Normative: Handle each error exactly once — either log it or return it.
Logging an error and then returning it causes duplicate noise as callers up the
stack also handle the error.
go
// Bad: logged here AND by every caller up the stack
if err != nil {
slog.Error("query failed", "err", err)
return fmt.Errorf("query: %w", err)
}
// Good: wrap and return — let the caller decide
if err != nil {
return fmt.Errorf("query: %w", err)
}Exception: HTTP handlers and other top-of-stack boundaries may log detailed
errors server-side while returning a sanitized message to the client:
go
if err != nil {
slog.Error("checkout failed", "err", err, "user_id", uid)
http.Error(w, "internal error", http.StatusInternalServerError)
return
}See go-error-handling for the full
handle-once pattern and error wrapping guidance.
规范要求:每个错误只处理一次——要么记录日志,要么返回错误。
记录错误后又返回它会导致冗余日志,因为调用栈上游的代码也会处理该错误。
go
// 不推荐:此处记录日志,且调用栈上游的所有调用者也会记录
if err != nil {
slog.Error("query failed", "err", err)
return fmt.Errorf("query: %w", err)
}
// 推荐:包装后返回——让调用者决定如何处理
if err != nil {
return fmt.Errorf("query: %w", err)
}例外情况:HTTP处理器和其他栈顶边界组件可以在服务器端记录详细错误,同时向客户端返回经过脱敏的消息:
go
if err != nil {
slog.Error("checkout failed", "err", err, "user_id", uid)
http.Error(w, "internal error", http.StatusInternalServerError)
return
}完整的一次性处理模式和错误包装指导,请参考go-error-handling。
What NOT to Log
禁止记录的内容
Normative: Never log secrets, credentials, PII, or high-cardinality unbounded data.
- Passwords, API keys, tokens, session IDs
- Full credit card numbers, SSNs
- Request/response bodies that may contain user data
- Entire slices or maps of unbounded size
Read references/LEVELS-AND-CONTEXT.md when deciding what data is safe to include in log attributes.
规范要求:切勿记录密钥、凭证、个人身份信息(PII)或高基数无界数据。
- 密码、API密钥、令牌、会话ID
- 完整信用卡号、社保号码
- 可能包含用户数据的请求/响应体
- 无界大小的完整切片或映射
当判断哪些数据可以安全地包含在日志属性中时,请阅读references/LEVELS-AND-CONTEXT.md。
Quick Reference
快速参考
| Do | Don't |
|---|---|
| |
| Static message + structured fields | |
| camelCase or inconsistent keys |
| Log OR return errors | Log AND return the same error |
| Derive logger from context | Create a new logger per call |
Use | |
Pre-check | Always allocate log args |
| 推荐做法 | 不推荐做法 |
|---|---|
| |
| 静态消息 + 结构化字段 | 在消息中使用 |
| camelCase或不一致的键名 |
| 日志记录 或 返回错误 | 同时记录日志并返回同一个错误 |
| 从上下文派生日志实例 | 每次调用都创建新的日志实例 |
| 使用 |
在热路径中预检查 | 始终分配日志参数 |
Related Skills
相关技能
- Error handling: See go-error-handling when deciding whether to log or return an error, or for the handle-once pattern
- Context propagation: See go-context when passing request-scoped values (including loggers) through context
- Performance: See go-performance when optimizing hot-path logging or reducing allocations in log calls
- Code review: See go-code-review when reviewing logging practices in Go PRs
- 错误处理:当决定是记录日志还是返回错误,或了解一次性处理模式时,请参考go-error-handling
- 上下文传递:当通过上下文传递请求范围的值(包括日志实例)时,请参考go-context
- 性能优化:当优化热路径日志或减少日志调用中的内存分配时,请参考go-performance
- 代码评审:当评审Go语言PR中的日志实践时,请参考go-code-review
Reference Files
参考文件
- references/LOGGING-PATTERNS.md — slog setup, handler configuration, HTTP middleware, migration from log
- references/LEVELS-AND-CONTEXT.md — Level semantics, context-based logging, performance, what not to log
- references/LOGGING-PATTERNS.md — slog设置、处理器配置、HTTP中间件、从log迁移的相关内容
- references/LEVELS-AND-CONTEXT.md — 级别语义、基于上下文的日志、性能优化、禁止记录内容的相关内容