在公司内部见过各种各样的日志库,其中很多是基于zap开发的,但是看源码觉得有些过于复杂,其实要自定义zap的日志格式还是挺容易的,只要自己实现Encoder接口,如果合理利用zap库已有的实现,可以做到只写EncodeEntry方法。下面就以在console格式的日志上加上日志颜色为例进行简单说明。


01

参考默认console的实现


type consoleEncoder struct {
*jsonEncoder
}


func NewConsoleEncoder(cfg EncoderConfig) Encoder {
if cfg.ConsoleSeparator == "" {
// Use a default delimiter of '\t' for backwards compatibility
cfg.ConsoleSeparator = "\t"
}
return consoleEncoder{newJSONEncoder(cfg, true)}
}
如上,默认的console格式利用了json格式实现的一些方法,我们可以以此为参考,重写下EncodeEntry,在前后加上颜色即可。

02

如何给终端添加颜色


在linux终端中添加颜色的方式非本文重点,感兴趣的可以自行搜索,这里简单说明下配置方式及代码

// 颜色设置规则:`\033[字体颜色;背景色;字体效果m`,因为代码都不一样,故其实3种效果的顺序并不重要,只要用`;`分割即可,如果没变化就不填
// 1. 字体颜色代码(30~37):30-黑色,31-红色,32-绿色,33-黄色,34-蓝色,35-紫红色,36-青蓝色,37-白色
// 2. 背景颜色代码(40~47):40-黑色,41-红色,42-绿色,43-黄色,44-蓝色,45-紫红色,46-青蓝色,47-白色
// 3. 字体效果代码(1~8):1-加粗,4-下划线,5-闪烁,7-反色,8-不可见
// 4. 0用于重置效果
// 示例:辣眼睛的绿底红字加粗:\033[32;42;1m
const (
green = "\033[32m"
yellow = "\033[33m"
red = "\033[31m"
reset = "\033[0m"
)


var colorMap = map[zapcore.Level]string{
zapcore.InfoLevel: green,
zapcore.WarnLevel: yellow,
zapcore.ErrorLevel: red,
}


03

重写EncodeEntry方法


下面我们重写EncodeEntry
type colorConsoleEncoder struct {
*zapcore.EncoderConfig
zapcore.Encoder
}


// NewColorConsole 新建ColorConsole
func NewColorConsole(cfg zapcore.EncoderConfig) (enc zapcore.Encoder) {
return colorConsoleEncoder{
EncoderConfig: &cfg,
// 使用默认的的 ConsoleEncoder,可以避免重写一遍 ObjectEncoder 等接口
// PS:ConsoleEncoder 其实也是利用了自带的 JSONEncoder
Encoder: zapcore.NewConsoleEncoder(cfg),
}
}


// EncodeEntry 重写 ConsoleEncoder 的 EncodeEntry
func (c colorConsoleEncoder) EncodeEntry(ent zapcore.Entry, fields []zapcore.Field) (buff *buffer.Buffer, err error) {
buff2, err := c.Encoder.EncodeEntry(ent, fields) // 利用zap已有的实现
if err != nil {
return nil, err
}


buff = _pool.Get()
buff.AppendString(colorMap[ent.Level]) // 设置颜色
buff.AppendString(buff2.String())
buff.AppendString(reset) // 重置
return buff, err
}


04

注册Encoder


注册Encoder,这样我们就能像配置json、console一样修改了

const (
// ColorConsole 带颜色的console
ColorConsole = "color_console"
)


func init() {
_ = zap.RegisterEncoder(ColorConsole, func(config zapcore.EncoderConfig) (zapcore.Encoder, error) {
return NewColorConsole(config), nil
}) // 注册Encoder
}


05

测试效果


写个测试用例看下效果

func TestNewColorConsole(t *testing.T) {
cfg := zap.NewDevelopmentConfig()
cfg.Encoding = ColorConsole
cfg.DisableStacktrace = true // 不打堆栈,不然Warn及以上等级会打印堆栈
logger, _ := cfg.Build()


logger.Debug("测试Debug", zap.Int64("i", 1))
logger.Info("测试Info", zap.Int64("i", 2))
logger.Warn("测试Warn", zap.Int64("i", 3))
logger.Error("测试Error", zap.Int64("i", 4))
}