
本文介绍如何使用 zap 库为不同日志级别配置终端颜色输出,并将各等级日志分别写入独立文件(如 info.log、error.log),涵盖开发与生产环境的编码器配置、多写入器(writesyncer)组合及核心注意事项。
本文介绍如何使用 zap 库为不同日志级别配置终端颜色输出,并将各等级日志分别写入独立文件(如 info.log、error.log),涵盖开发与生产环境的编码器配置、多写入器(writesyncer)组合及核心注意事项。
Zap 默认不内置“按级别自动着色 + 按级别自动分文件”一体化方案,但可通过组合其灵活的 EncoderConfig、LevelEnablerFunc 和 MultiWriteSyncer 实现。以下是一个完整、可运行的初始化示例,支持:
- ✅ 控制台输出:
DEBUG/INFO绿色、WARN黄色、ERROR/FATAL/PANIC红色(使用CapitalColorLevelEncoder) - ✅ 文件输出:
INFO及以上写入info.log,ERROR及以上写入error.log(无重复、无遗漏) - ✅ 开发友好:启用堆栈追踪、行号、调用函数名等调试信息
package main
import (
"os"
"syscall"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"golang.org/x/term"
)
func newLogger() (*zap.Logger, error) {
// 1. 判断是否为 TTY 终端(仅在交互式终端启用颜色)
consoleIsTTY := term.IsTerminal(int(os.Stdout.Fd()))
// 2. 构建 Console Encoder(带颜色)
consoleEncoderConfig := zap.NewDevelopmentEncoderConfig()
consoleEncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder // 关键:启用颜色级别
if !consoleIsTTY {
// 非 TTY 环境(如管道、CI)禁用颜色,避免 ANSI 转义符污染
consoleEncoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
}
consoleEncoder := zapcore.NewConsoleEncoder(consoleEncoderConfig)
// 3. 构建 File Encoders(无颜色,适合日志文件)
fileEncoderConfig := zap.NewProductionEncoderConfig()
fileEncoderConfig.TimeKey = "time"
fileEncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
infoEncoder := zapcore.NewJSONEncoder(fileEncoderConfig)
errorEncoder := zapcore.NewJSONEncoder(fileEncoderConfig)
// 4. 创建写入器(WriteSyncer)
consoleWriter := zapcore.Lock(os.Stdout)
// 按级别分流文件写入器:info.log(INFO+)、error.log(ERROR+)
infoFile, _ := os.OpenFile("info.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
errorFile, _ := os.OpenFile("error.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
infoWriter := zapcore.Lock(infoFile)
errorWriter := zapcore.Lock(errorFile)
// 5. 定义 LevelEnablerFunc 实现分流逻辑
infoLevel := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
return lvl >= zapcore.InfoLevel
})
errorLevel := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
return lvl >= zapcore.ErrorLevel
})
// 6. 组合 Core:控制台(全级别 + 颜色) + infoFile(INFO+) + errorFile(ERROR+)
core := zapcore.NewTee(
zapcore.NewCore(consoleEncoder, consoleWriter, zapcore.DebugLevel), // 控制台显示所有级别(含 DEBUG)
zapcore.NewCore(infoEncoder, infoWriter, infoLevel),
zapcore.NewCore(errorEncoder, errorWriter, errorLevel),
)
// 7. 构建 Logger(禁用采样以确保每条日志都写入)
return zap.New(core, zap.AddCaller(), zap.WithCallerSkip(1)), nil
}
func main() {
logger, _ := newLogger()
defer logger.Sync() // 必须调用,确保日志刷入磁盘
logger.Debug("debug message") // 控制台绿色 DEBUG,不写入任何文件
logger.Info("info message") // 控制台绿色 INFO,写入 info.log
logger.Warn("warn message") // 控制台黄色 WARN,写入 info.log
logger.Error("error message") // 控制台红色 ERROR,写入 info.log & error.log
logger.Fatal("fatal message") // 控制台红色 FATAL,写入 info.log & error.log(并 panic)
}
⚠️ 关键注意事项:
-
颜色仅限终端:
CapitalColorLevelEncoder依赖 ANSI 转义序列,务必通过term.IsTerminal()检测环境,避免在docker logs、CI 输出中出现乱码;生产部署建议关闭颜色。 -
文件写入需显式
Sync():defer logger.Sync()是必须的,否则进程退出时未刷盘的日志会丢失。 -
避免日志重复:
NewTee会将每条日志广播给所有匹配的 Core。因此error.log中也会包含INFO级别日志(若其 Core 的 LevelEnabler 允许)。本例中通过errorLevel严格限定仅ERROR+写入error.log,确保语义清晰。 -
性能考量:开发模式(
NewDevelopmentEncoderConfig)含丰富调试字段(如行号、函数名),生产环境请切换为NewProductionEncoderConfig并禁用AddCaller。 -
文件权限与轮转:上述示例未集成日志轮转(如
lumberjack)。如需按大小/时间切割,请用lumberjack.Logger替换os.File,并确保WriteSyncer实现Sync()方法。
综上,Zap 的模块化设计允许你精准控制日志行为:颜色由 EncoderConfig.EncodeLevel 决定,分流由 LevelEnablerFunc + NewTee 协同完成。合理组合这些原语,即可构建既直观(彩色终端)又规范(分级归档)的企业级日志系统。










