關於golang中其他各種日誌插件對比,這裏不再贅述,直奔zap主題,理論小結+實戰。
目錄
前言
爲什麼選擇zap:
- zap是非常快的、結構化的,分日誌級別的Go日誌庫
- 它同時提供了結構化日誌記錄和printf風格的日誌記錄
以下是Zap發佈的基準測試信息
記錄一條消息和10個字段:
Package | Time | Time % to zap | Objects Allocated |
---|---|---|---|
⚡️ zap | 862 ns/op | +0% | 5 allocs/op |
⚡️ zap (sugared) | 1250 ns/op | +45% | 11 allocs/op |
zerolog | 4021 ns/op | +366% | 76 allocs/op |
go-kit | 4542 ns/op | +427% | 105 allocs/op |
apex/log | 26785 ns/op | +3007% | 115 allocs/op |
logrus | 29501 ns/op | +3322% | 125 allocs/op |
log15 | 29906 ns/op | +3369% | 122 allocs/op |
使用小結
哎,之前思考的少,都是拿來主義,就知道用別人寫好的,自己搞,才發現不少細節。分享個小結:
- 日誌按大小切割和備份個數、文件有效期,一般選zap+lumberjack
- 日誌按天或者小時切割,一般選zap+file-rotatelogs
zap中Logger和SugaredLogger的區別:
- Zap提供了兩種類型的日誌記錄器—SugaredLogger和Logger
- 在性能很好但不是很關鍵的上下文中,使用SugaredLogger。它比其他結構化日誌記錄包快4-10倍,並且支持結構化和printf風格的日誌記錄
- 在每一微秒和每一次內存分配都很重要的上下文中,使用Logger。它甚至比SugaredLogger更快,內存分配次數也更少,但它只支持強類型的結構化日誌記錄
- SugaredLogger是在Logger的基礎上,提供了額外參數的格式化方式,提供更大的便利性以格式化具體的msg,在可能帶來風險的同時,還會造成額外的內存分配
動手實戰
基於zap+file-rotatelogs實現按天分隔日誌
實現功能點:
- 按照不同級別日誌獨立文件記錄(可以參考info級別自己動手擴展下)
- 日誌按天分割
- 只保留近30天的日誌
編碼:
package main
import (
rotatelogs "github.com/lestrrat-go/file-rotatelogs"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"io"
"strings"
"time"
)
var errorLogger *zap.SugaredLogger
func init() {
encoder := zapcore.NewJSONEncoder(zapcore.EncoderConfig{
MessageKey: "msg",
LevelKey: "level",
EncodeLevel: zapcore.CapitalLevelEncoder,
TimeKey: "ts",
EncodeTime: func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
enc.AppendString(t.Format("2006-01-02 15:04:05"))
},
CallerKey: "call",
EncodeCaller: zapcore.ShortCallerEncoder,
EncodeDuration: func(d time.Duration, enc zapcore.PrimitiveArrayEncoder) {
enc.AppendInt64(int64(d) / 1000000)
},
})
infoLevel := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
return lvl >= zapcore.InfoLevel
})
infoWriter := getWriter("console_info.log")
core := zapcore.NewTee(
zapcore.NewCore(encoder, zapcore.AddSync(infoWriter), infoLevel),
)
//AddCallerSkip增加了調用者註釋跳過的調用者數量
//大白話:就是顯示調用打印日誌的是哪一行的code行數,你可以改爲0看看效果,哈哈
log := zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1))
errorLogger = log.Sugar()
}
func getWriter(filename string) io.Writer {
hook, err := rotatelogs.New(
strings.Replace(filename, ".log", "", -1) + "-%Y-%m-%d.log",
//文件最大壽命30天
rotatelogs.WithMaxAge(time.Hour*24*30),
//每隔1天進行文件分割
rotatelogs.WithRotationTime(time.Hour*24),
)
if err != nil {
panic(err)
}
return hook
}
func Debug(args ...interface{}) {
errorLogger.Debug(args...)
}
func Debugf(template string, args ...interface{}) {
errorLogger.Debugf(template, args...)
}
func Info(args ...interface{}) {
errorLogger.Info(args...)
}
func Infof(template string, args ...interface{}) {
errorLogger.Infof(template, args...)
}
func Warn(args ...interface{}) {
errorLogger.Warn(args...)
}
func Warnf(template string, args ...interface{}) {
errorLogger.Warnf(template, args...)
}
func Error(args ...interface{}) {
errorLogger.Error(args...)
}
func Errorf(template string, args ...interface{}) {
errorLogger.Errorf(template, args...)
}
func DPanic(args ...interface{}) {
errorLogger.DPanic(args...)
}
func DPanicf(template string, args ...interface{}) {
errorLogger.DPanicf(template, args...)
}
func Panic(args ...interface{}) {
errorLogger.Panic(args...)
}
func Panicf(template string, args ...interface{}) {
errorLogger.Panicf(template, args...)
}
func Fatal(args ...interface{}) {
errorLogger.Fatal(args...)
}
func Fatalf(template string, args ...interface{}) {
errorLogger.Fatalf(template, args...)
}
func main() {
Infof("Hello World")
}
效果如下:
基於zap+lumberjack實現按天分隔日誌
實現功能點:
- 按照500M文件大小切割文件
- 最多保留200個文件
- 最多保留30天
- 支持文件壓縮
編碼:
package main
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"gopkg.in/natefinch/lumberjack.v2"
)
// error logger
var errorLogger *zap.SugaredLogger
var levelMap = map[string]zapcore.Level{
"debug": zapcore.DebugLevel,
"info": zapcore.InfoLevel,
"warn": zapcore.WarnLevel,
"error": zapcore.ErrorLevel,
"dpanic": zapcore.DPanicLevel,
"panic": zapcore.PanicLevel,
"fatal": zapcore.FatalLevel,
}
func getLoggerLevel(lvl string) zapcore.Level {
if level, ok := levelMap[lvl]; ok {
return level
}
return zapcore.InfoLevel
}
//Filename: 日誌文件的位置
//MaxSize:在進行切割之前,日誌文件的最大大小(以MB爲單位)
//MaxBackups:保留舊文件的最大個數
//MaxAges:保留舊文件的最大天數
//Compress:是否壓縮/歸檔舊文件
func init() {
fileName := "console.log"
level := getLoggerLevel("debug")
syncWriter := zapcore.AddSync(&lumberjack.Logger{
Filename: fileName,
MaxSize: 500,
MaxBackups: 200,
MaxAge: 30,
LocalTime: true,
Compress: true,
})
encoder := zap.NewProductionEncoderConfig()
encoder.EncodeTime = zapcore.ISO8601TimeEncoder
core := zapcore.NewCore(zapcore.NewJSONEncoder(encoder), syncWriter, zap.NewAtomicLevelAt(level))
logger := zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1))
errorLogger = logger.Sugar()
}
func Debug(args ...interface{}) {
errorLogger.Debug(args...)
}
func Debugf(template string, args ...interface{}) {
errorLogger.Debugf(template, args...)
}
func Info(args ...interface{}) {
errorLogger.Info(args...)
}
func Infof(template string, args ...interface{}) {
errorLogger.Infof(template, args...)
}
func Warn(args ...interface{}) {
errorLogger.Warn(args...)
}
func Warnf(template string, args ...interface{}) {
errorLogger.Warnf(template, args...)
}
func Error(args ...interface{}) {
errorLogger.Error(args...)
}
func Errorf(template string, args ...interface{}) {
errorLogger.Errorf(template, args...)
}
func DPanic(args ...interface{}) {
errorLogger.DPanic(args...)
}
func DPanicf(template string, args ...interface{}) {
errorLogger.DPanicf(template, args...)
}
func Panic(args ...interface{}) {
errorLogger.Panic(args...)
}
func Panicf(template string, args ...interface{}) {
errorLogger.Panicf(template, args...)
}
func Fatal(args ...interface{}) {
errorLogger.Fatal(args...)
}
func Fatalf(template string, args ...interface{}) {
errorLogger.Fatalf(template, args...)
}
func main() {
Infof("Hello World")
}
效果如下:
寫在最後
上面的僅僅是zap的基本應用,想玩的更加順手、深入,自己入門後多動手,遇到問題看看源碼。
參考鏈接: