如何在Golang中实现日志级别错误记录_区分Info、Warn和Error

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

Go 标准库的 log 包本身不支持日志级别(如 Info/Warn/Error)的原生区分,但通过简单封装或使用成熟日志库(如 log/slog(Go 1.21+ 内置)或 zapzerolog),可以清晰、安全地实现多级别日志记录。关键在于:**用不同输出目标、前缀或结构化字段标识级别,并确保 Error 日志包含堆栈或上下文以便排查。**

使用 Go 1.21+ 内置 slog(推荐,轻量且标准)

slog 是官方维护的结构化日志包,天然支持日志级别、属性绑定和自定义处理器。

  • 默认终端输出已按级别着色(Error 红、Warn 黄、Info 白/灰),无需额外配置
  • slog.Infoslog.Warnslog.Error 直接区分,语义明确
  • 可轻松添加上下文:如 slog.String("user_id", "u123")slog.Int("status", 500)
  • 错误日志建议附带 err 属性(slog.Any("err", err)),slog 会自动展开错误链和底层原因

示例:

```go
import "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 包 + 前缀 + 不同输出器模拟多级别。

  • 为每个级别创建独立的 *log.Logger,设置不同前缀(如 "INFO ""WARN ""ERROR "
  • 将 Error 日志写入 os.Stderr,Info/Warn 写入 os.Stdout,便于 shell 重定向分离
  • 在 Error 日志中手动调用 debug.PrintStack() 或用 errors.WithStack(需第三方包)补充堆栈

示例:

```go
import (
  "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))

关键实践提醒

无论用哪个库,以下几点直接影响排障效率:

  • 不要只记 “发生错误”,要记 “哪里出错、为什么错、影响什么” —— 在 Error 日志中包含关键参数、用户 ID、请求 ID、HTTP 状态码等上下文
  • Warn 不是 “轻量 Error”,而是表示“可能有问题但未中断流程”,如降级策略触发、缓存命中率骤降
  • Info 日志要有业务意义,避免刷屏式 debug(如循环内打 Info)。考虑用 Debug 级别代替,上线时关闭
  • 敏感信息(密码、token、身份证)禁止写入日志,必要时脱敏(如 slog.String("phone", "***"+last4)