Go語言Zap日志庫使用教程
一、日志庫選型需要和比較
1.日志庫選型需求
- 日志性能
- 不同日志級(jí)別
- 可讀性(包括日志采集、監(jiān)控等)
- 文件切割(不同維度分割)
2.日志庫比較
記錄一條消息和 10 個(gè)字段:
Package | Time | Time % to zap | Objects Allocated |
---|---|---|---|
? zap | 2900 ns/op | +0% | 5 allocs/op |
? zap (sugared) | 3475 ns/op | +20% | 10 allocs/op |
zerolog | 10639 ns/op | +267% | 32 allocs/op |
go-kit | 14434 ns/op | +398% | 59 allocs/op |
logrus | 17104 ns/op | +490% | 81 allocs/op |
apex/log | 32424 ns/op | +1018% | 66 allocs/op |
log15 | 33579 ns/op | +1058% | 76 allocs/op |
使用已經(jīng)有 10 個(gè)上下文字段的記錄器記錄消息:
Package | Time | Time % to zap | Objects Allocated |
---|---|---|---|
? zap | 373 ns/op | +0% | 0 allocs/op |
? zap (sugared) | 452 ns/op | +21% | 1 allocs/op |
zerolog | 288 ns/op | -23% | 0 allocs/op |
go-kit | 11785 ns/op | +3060% | 58 allocs/op |
logrus | 19629 ns/op | +5162% | 70 allocs/op |
log15 | 21866 ns/op | +5762% | 72 allocs/op |
apex/log | 30890 ns/op | +8182% | 55 allocs/op |
記錄一個(gè)靜態(tài)字符串,沒有任何上下文或 printf 樣式的模板:
Package | Time | Time % to zap | Objects Allocated |
---|---|---|---|
? zap | 381 ns/op | +0% | 0 allocs/op |
? zap (sugared) | 410 ns/op | +8% | 1 allocs/op |
zerolog | 369 ns/op | -3% | 0 allocs/op |
standard library | 385 ns/op | +1% | 2 allocs/op |
go-kit | 606 ns/op | +59% | 11 allocs/op |
logrus | 1730 ns/op | +354% | 25 allocs/op |
apex/log | 1998 ns/op | +424% | 7 allocs/op |
log15 | 4546 ns/op | +1093% | 22 allocs/op |
通過上面benchmark測試可以Zap性能是非常出眾的。主要是大多數(shù)日志庫提供的方式是基于反射的序列化和字符串格式化,這種方式代價(jià)太高,而 Zap 采取不同的方法。
- 避免 interface{} 使用強(qiáng)類型設(shè)計(jì)
- 封裝強(qiáng)類型,無反射
- 使用零分配內(nèi)存的 JSON 編碼器,盡可能避免序列化開銷
二、Zap(Uber-go)
1.安裝
go get -u go.uber.org/zap
2.配置Zap Logger
Zap提供了兩種類型的日志記錄器:Sugared Logger、Logger。在每一微秒和每一次內(nèi)存分配都很重要的上下文中,使用Logger,內(nèi)存分配次數(shù)也更少,但它只支持強(qiáng)類型的結(jié)構(gòu)化日志記錄。對(duì)性能不是要求極致,建議使用SugaredLogger,支持結(jié)構(gòu)化和 printf 風(fēng)格的日志記錄。
2.1.Logger
- 通過調(diào)用zap.NewProduction() | zap.NewDevelopment() | zap.Example()創(chuàng)建一個(gè) Logger。
- 上面的每一個(gè)函數(shù)都將創(chuàng)建一個(gè) logger。唯一的區(qū)別在于它將記錄的信息不同。例如 production logger 默認(rèn)記錄調(diào)用函數(shù)信息、日期和時(shí)間等。
- 通過 Logger 調(diào)用 Info/Error 等。
- 默認(rèn)情況下日志都會(huì)打印到應(yīng)用程序的 console 界面。
package main import ( "errors" "go.uber.org/zap" ) var logger *zap.Logger func main() { InitLogger() logger.Debug("這是一條日志", zap.String("name", "zhangSang"), zap.Int("age", 18)) // zap.NewProduction() 默認(rèn)不輸出該級(jí)別日志 logger.Info("這是一條日志", zap.String("name", "zhangSang"), zap.Int("age", 18)) logger.Error("這是一條日志", zap.String("name", "zhangSang"), zap.Error(errors.New("錯(cuò)誤信息"))) } func InitLogger() { logger, _ = zap.NewProduction() defer logger.Sync() }
上面代碼執(zhí)行結(jié)果:
{"level":"info","ts":1655165315.1104648,"caller":"test/main.go:13","msg":"這是一條日志","name":"zhangSang","age":18}
{"level":"error","ts":1655165315.1105008,"caller":"test/main.go:14","msg":"這是 一條日志","name":"zhangSang","error":"錯(cuò)誤信息","stacktrace":"main.main\n\tD:/Go/Work/src/test/main.go:14\nruntime.main\n\tD:/Go/src/runtime/proc.go:255"
2.2.Sugared Logger
- 基本實(shí)現(xiàn)都一樣,使用SugaredLogger支持Printf格式記錄語句
- 調(diào)用logger的sugar()方法來獲取一個(gè)SugaredLogger
package main import ( "errors" "go.uber.org/zap" ) var logger *zap.Logger var sugar *zap.SugaredLogger func main() { InitLogger() sugar.Debug("這是一條日志", zap.String("name", "zhangSang"), zap.Int("age", 18)) // zap.NewProduction() 默認(rèn)不輸出該級(jí)別日志 sugar.Info("這是一條日志", zap.String("name", "zhangSang"), zap.Int("age", 18)) sugar.Error("這是一條日志", zap.String("name", "zhangSang"), zap.Error(errors.New("錯(cuò)誤信息"))) } func InitLogger() { logger, _ = zap.NewProduction() defer logger.Sync() sugar = logger.Sugar() }
上面代碼執(zhí)行結(jié)果:
{"level":"info","ts":1655165815.3873067,"caller":"test/main.go:14","msg":"這是一條日志{name 15 0 zhangSang <nil>} {age 11 18 <nil>}"}
{"level":"error","ts":1655165815.3880382,"caller":"test/main.go:15","msg":"這是一條日志{name 15 0 zhangSang <nil>} {error 26 0 錯(cuò)誤信息}","stacktrace":"main.main\n\tD:/GoWork/src/test/main.go:15\nruntime.main\n\tD:/Go/src/runtime/proc.go:255"}
2.3. 配置Logger
zap.New()
方法來手動(dòng)傳遞所有配置,而不是使用像zap.NewProduction()
這樣的預(yù)置方法來創(chuàng)建 logger;zapcore.Core需要三個(gè)配置——Encoder,WriteSyncer,LogLevel。
func New(core zapcore.Core, options ...Option) *Logger
1.Encoder: 編碼器 (配置日志格式)。此處使用NewJSONEncoder() (如果不喜歡JSON格式日志,NewConsoleEncoder()指定普通 Encoder),并使用預(yù)先設(shè)置的ProductionEncoderConfig(),ProductionEncoderConfig()返回一個(gè)自定義的EncoderConfig。
func getEncoder() zapcore.Encoder { encoderConfig := zap.NewProductionEncoderConfig() return zapcore.NewJSONEncoder(encoderConfig) }
2.WriterSyncer :指定日志寫到何處。使用zapcore.AddSync()函數(shù)并且將打開的文件句柄傳入。
func getLogWriter() zapcore.WriteSyncer { file, _ := os.OpenFile("./test.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666) // 打開文件(不存在創(chuàng)建) return zapcore.AddSync(file) }
3.Log Level-指定日志級(jí)別
func InitLogger() { writeSyncer := getLogWriter() encoder := getEncoder() core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel) // DebugLevel logger = zap.New(core) defer logger.Sync() sugarLogger = logger.Sugar() }
大家將上面的方法新增測試代碼中,運(yùn)行結(jié)果:
4.時(shí)間格式化處理
func getEncoder() zapcore.Encoder { encoderConfig := zap.NewProductionEncoderConfig() encoderConfig.EncodeTime = customTimeEncoder return zapcore.NewJSONEncoder(encoderConfig) } func customTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) { enc.AppendString(t.Format("2006-01-02 15:04:05.000")) }
運(yùn)行結(jié)果:
5.輸出文件名和行號(hào)
zap.New()中加上zap.AddCaller()。
func InitLogger() { writeSyncer := getLogWriter() encoder := getEncoder() core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel) // DebugLevel logger = zap.New(core, zap.AddCaller()) defer logger.Sync() sugarLogger = logger.Sugar() }
運(yùn)行結(jié)果:
三.使用 Lumberjack 進(jìn)行日志切割歸檔
1. 安裝
go get -u github.com/natefinch/lumberjack
2.Zap logger中使用Lumberjack
func getLogWriter() zapcore.WriteSyncer { lumberJackLogger := &lumberjack.Logger{ Filename: "./test.log", // 日志文件位置 MaxSize: 1, // 進(jìn)行切割之前,日志文件最大值(單位:MB),默認(rèn)100MB MaxBackups: 10, // 保留舊文件的最大個(gè)數(shù) MaxAge: 1, // 保留舊文件的最大天數(shù) Compress: false, // 是否壓縮/歸檔舊文件 } return zapcore.AddSync(lumberJackLogger) }
3.完整代碼
package main import ( "github.com/natefinch/lumberjack" "go.uber.org/zap" "go.uber.org/zap/zapcore" "time" ) var logger *zap.Logger var sugar *zap.SugaredLogger func main() { InitLogger() for i := 0; i < 99999; i++ { sugar.Debugf("查詢用戶信息開始 id:%d", 1) sugar.Infof("查詢用戶信息成功 name:%s age:%d", "zhangSan", 20) sugar.Errorf("查詢用戶信息失敗 error:%v", "未該查詢到該用戶信息") } } func InitLogger() { writeSyncer := getLogWriter() encoder := getEncoder() core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel) logger = zap.New(core, zap.AddCaller()) defer logger.Sync() sugar = logger.Sugar() } func getEncoder() zapcore.Encoder { encoderConfig := zap.NewProductionEncoderConfig() encoderConfig.EncodeTime = customTimeEncoder return zapcore.NewJSONEncoder(encoderConfig) } func getLogWriter() zapcore.WriteSyncer { lumberJackLogger := &lumberjack.Logger{ Filename: "./test.log", // 日志文件位置 MaxSize: 1, // 進(jìn)行切割之前,日志文件最大值(單位:MB),默認(rèn)100MB MaxBackups: 5, // 保留舊文件的最大個(gè)數(shù) MaxAge: 1, // 保留舊文件的最大天數(shù) Compress: false, // 是否壓縮/歸檔舊文件 } return zapcore.AddSync(lumberJackLogger) } func customTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) { enc.AppendString(t.Format("2006-01-02 15:04:05.000")) }
在main()函數(shù)中循環(huán)9999次輸出日志,運(yùn)行結(jié)果:
總結(jié)
zap日志庫很強(qiáng)大,性能優(yōu)勢以及絕塵,里面的設(shè)計(jì)和工程實(shí)踐很指的學(xué)習(xí),有興趣的盆友可以看看。
到此這篇關(guān)于Go語言Zap日志庫使用教程的文章就介紹到這了,更多相關(guān)Go Zap日志庫內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
go語言發(fā)送smtp郵件的實(shí)現(xiàn)示例
這篇文章主要介紹了go發(fā)送smtp郵件的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09GO語言實(shí)現(xiàn)的http抓包分析工具pproxy介紹
這篇文章主要介紹了GO語言實(shí)現(xiàn)的http抓包分析工具pproxy介紹,本文同時(shí)對(duì)比了Fiddler、Charles等抓包軟件,需要的朋友可以參考下2015-03-03