Go 标准库 log 不支持原生日志级别,推荐使用 Go 1.21+ 内置 slog(轻量、结构化、着色输出),或高性能库如 zap/zerolog;兼容旧版可封装 log.Writer 并按级别分离输出目标与前缀。

Go 标准库的 log 包本身不支持日志级别(如 Info/Warn/Error)的原生区分,但通过简单封装或使用成熟日志库(如 log/slog(Go 1.21+ 内置)或 zap、zerolog),可以清晰、安全地实现多级别日志记录。关键在于:**用不同输出目标、前缀或结构化字段标识级别,并确保 Error 日志包含堆栈或上下文以便排查。**
使用 Go 1.21+ 内置 slog(推荐,轻量且标准)
slog 是官方维护的结构化日志包,天然支持日志级别、属性绑定和自定义处理器。
- 默认终端输出已按级别着色(Error 红、Warn 黄、Info 白/灰),无需额外配置
- 用
slog.Info、slog.Warn、slog.Error直接区分,语义明确 - 可轻松添加上下文:如
slog.String("user_id", "u123")、slog.Int("status", 500) - 错误日志建议附带
err属性(slog.Any("err", err)),slog会自动展开错误链和底层原因
示例:
```goimport "log/slog"
func main() {
slog.Info("服务启动", "addr", ":8080")
slog.Warn("磁盘空间不足", "free_mb", 124)
if err := someOperation(); err != nil {
slog.Error("请求处理失败", "path", "/api/data", "err", err)
}
}
```
自定义 log.Writer 封装(兼容老版本 Go)
若暂不能升级到 Go 1.21,可用标准 log 包 + 前缀 + 不同输出器模拟多级别。
立即学习“go语言免费学习笔记(深入)”;
- 为每个级别创建独立的
*log.Logger,设置不同前缀(如"INFO "、"WARN "、"ERROR ") - 将 Error 日志写入
os.Stderr,Info/Warn 写入os.Stdout,便于 shell 重定向分离 - 在 Error 日志中手动调用
debug.PrintStack()或用errors.WithStack(需第三方包)补充堆栈
示例:
```goimport (
"log"
"os"
)
var (
infoLog = log.New(os.Stdout, "INFO ", log.Ldate|log.Ltime|log.Lshortfile)
warnLog = log.New(os.Stdout, "WARN ", log.Ldate|log.Ltime|log.Lshortfile)
errorLog = log.New(os.Stderr, "ERROR ", log.Ldate|log.Ltime|log.Lshortfile)
)
func main() {
infoLog.Println("配置加载完成")
warnLog.Printf("忽略无效配置项: %s", "timeout_ms")
errorLog.Printf("数据库连接失败: %v", err)
}
```
生产环境建议:用 zap 或 zerolog 替代手写
高并发、低延迟场景下,标准库和 slog 默认处理器可能有性能瓶颈。zap(Uber)和 zerolog(rs/zerolog)专为高性能设计:
- 零内存分配(zero-allocation)日志写入,避免 GC 压力
- 支持异步日志(
zap.NewAsync)、日志轮转、JSON 输出,天然适配 ELK/Prometheus 生态 - Error 级别自动捕获 caller、stacktrace(可开关),无需手动调用
- 结构化字段统一,比如
logger.Error("db timeout", zap.String("query", q), zap.Duration("elapsed", d))
关键实践提醒
无论用哪个库,以下几点直接影响排障效率:










