go使用zap + lumberjack重构项目的日志系统
【工作隨筆】重構(gòu)項(xiàng)目的日志系統(tǒng)
原先公司的go項(xiàng)目是采用之前一個(gè)大哥自己開發(fā)的一個(gè)log包,來實(shí)現(xiàn)各種日志的打印監(jiān)控,對(duì)于和很多的場景支持和日志分割都做的不太好,所以在近期有空的時(shí)候?qū)系娜罩鞠到y(tǒng)進(jìn)行了重寫。
1 明確優(yōu)化需求
- 性能強(qiáng)勁(至少要比老的好)
- 兼容老的日志代碼,減少改動(dòng)(不然就得改成百上千行代碼)
- 支持日志按天分割(別的C++的服務(wù)都是這么分割,要方便運(yùn)維開發(fā)監(jiān)控腳本)
2 前期準(zhǔn)備
在網(wǎng)絡(luò)上看了很多的大牛寫的介紹決定采用【zap + lumberjack】兩個(gè)包來實(shí)現(xiàn)我們的新日志系統(tǒng),這也是現(xiàn)在go語言常見的一種日志解決方案
- Zap go.uber.org/zap
現(xiàn)在最強(qiáng)大的go logger包之一,官方文檔附在超連接里,性能強(qiáng)大調(diào)用精簡。并且zap提供了兩種類型的 logger
- lumberjack github.com/natefinch/lumberjack
是一個(gè)支持日志分割,日志過期刪除的小包,非常適配Zap
3 擼代碼
首先是創(chuàng)建一個(gè)可以打印日志的SugaredLogger
package loggerimport ("github.com/natefinch/lumberjack""go.uber.org/zap""go.uber.org/zap/zapcore" )var Svrlogger Logger// 為了兼容老代碼,選擇自己封裝一層 type Logger struct {logger *zap.SugaredLogger }// 創(chuàng)建一個(gè)lumberjack的Write func getLogWriter() zapcore.WriteSyncer {lumberJackLogger := &lumberjack.Logger{Filename: "./logPath",MaxSize: 10, // 日志大小(塞滿了才創(chuàng)建新的)// 備份相關(guān)的參數(shù)MaxBackups: 10,MaxAge: 10,Compress: false,}return zapcore.AddSync(lumberJackLogger) }func InitLogger() {// 設(shè)置一些基本日志格式encoder := zapcore.NewConsoleEncoder(zapcore.EncoderConfig{// 略~})// 創(chuàng)建一個(gè)corecore := zapcore.NewCore(encoder, getLogWriter(), zapcore.DebugLevel)// 給logger賦值logger := zap.New(core, zap.AddCaller())sugarLogger = logger.Sugar() }Demo寫完了,測試了一下沒有問題,接下里我們思考下一個(gè)問題:
我們期望的是日志隨日期的變化來打印,即換天之后創(chuàng)建新的日志,然后往新的日志中打印,而lumberjack的日志打印模式是先給日志文件設(shè)置最大的大小,如果日志內(nèi)容超過這個(gè)大小才會(huì)創(chuàng)建新的日志文件,這顯然是不符合我們要求的,所以沒辦法手撕lumberjack,還好就百來行代碼重寫一下就行了。
解決了lumberjack的問題之后,我又發(fā)現(xiàn)了新的問題:
首先我們期望debug,info 這類信息的日志信息更適合用于保活信息的打印,這類信息應(yīng)該交由運(yùn)維同學(xué)監(jiān)控,而我們開發(fā)成員更應(yīng)該關(guān)注Warn,Error,Fatal,Painc;所以更好的實(shí)現(xiàn)方式應(yīng)該是一個(gè)go服務(wù)同時(shí)打印兩份文件,所以我們一個(gè)logger實(shí)例就需要?jiǎng)?chuàng)建多個(gè)zapcore!
打印兩個(gè)日志的問題解決之后,我又了個(gè)新的鬼點(diǎn)子,我希望打印的錯(cuò)誤日志也可以在程序終端進(jìn)行輸出!所以還得要改改!使用io包的io.MultiWriter方法,讓zap的日志往終端和文件都能打印
func InitLogger() {/*不重要的代碼...*/core := zapcore.NewTee(// 保活日志zapcore.NewCore(encoder, zapcore.AddSync(getLogWriter("./save/log")), savelog ),// 錯(cuò)誤日志zapcore.NewCore(encoder, zapcore.AddSync(io.MultiWriter(os.Stdout,getLogWriter("./error/log"))), errorlog ),)/*不重要的代碼...*/ }最后新的日志系統(tǒng)提交測試之后,發(fā)現(xiàn)我開發(fā)的時(shí)候忘記了自己是在zap的外面包了一層,所以打印日志堆棧信息的時(shí)候,打印的調(diào)用函數(shù)都是logger.go,我就回到go.uber.org/zap的文檔里一陣翻閱找到了這個(gè)方法:
// AddCallerSkip increases the number of callers skipped by caller annotation // (as enabled by the AddCaller option). When building wrappers around the // Logger and SugaredLogger, supplying this Option prevents zap from always // reporting the wrapper code as the caller. func AddCallerSkip(skip int) Option {return optionFunc(func(log *Logger) {log.callerSkip += skip}) }他允許我們打印調(diào)用堆棧信息的時(shí)候向上skip,所以我們最后的代碼變成了這樣子
log := zap.New(core, zap.AddCaller(),zap.AddCallerSkip(1))Svrlogger.logger := log.Sugar()以上,代碼都是精簡省流版本,更加環(huán)保,更加便于閱讀,希望對(duì)大家有所幫助
總結(jié)
以上是生活随笔為你收集整理的go使用zap + lumberjack重构项目的日志系统的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: webdav 文件服务器,WebDAV
- 下一篇: 2021江苏地区高考成绩排名查询,江苏高