新網站百度收錄北京疫情又嚴重了
關于日志的一些問題:
單個文件過大會影響寫入效率,所以會做拆分,但是到多大拆分? 最多保留幾個日志文件?最多保留多少天,要不要做壓縮處理?
一般都使用 lumberjack[1]這個庫完成上述這些操作
lumberjack
?//info文件writeSyncer
?infoFileWriteSyncer?:=?zapcore.AddSync(&lumberjack.Logger{
??Filename:???"./log/info.log",?//日志文件存放目錄,如果文件夾不存在會自動創(chuàng)建
??MaxSize:????2,????????????????//文件大小限制,單位MB
??MaxBackups:?100,??????????????//最大保留日志文件數量
??MaxAge:?????30,???????????????//日志文件保留天數
??Compress:???false,????????????//是否壓縮處理
?})
?infoFileCore?:=?zapcore.NewCore(encoder,?zapcore.NewMultiWriteSyncer(infoFileWriteSyncer,?zapcore.AddSync(os.Stdout)),?lowPriority)?//第三個及之后的參數為寫入文件的日志級別,ErrorLevel模式只記錄error級別的日志
?//error文件writeSyncer
?errorFileWriteSyncer?:=?zapcore.AddSync(&lumberjack.Logger{
??Filename:???"./log/error.log",?//日志文件存放目錄
??MaxSize:????1,?????????????????//文件大小限制,單位MB
??MaxBackups:?5,?????????????????//最大保留日志文件數量
??MaxAge:?????30,????????????????//日志文件保留天數
??Compress:???false,?????????????//是否壓縮處理
?})
測試日志到達指定大小后自動會切分

例如,當info級別的日志文件到達2M時,會根據當時的時間戳,切分出一個info-2023-04-13T05-27-18.296.log
。 后續(xù)新寫入的info級別的日志將寫入到info.log,直到又到達2M,繼續(xù)會切分。
測試日志到達指定最大保留日志文件數量后,將作何操作
清掉log文件夾,修改error日志配置:
?errorFileWriteSyncer?:=?zapcore.AddSync(&lumberjack.Logger{
??Filename:???"./log/error.log",?//日志文件存放目錄
??MaxSize:????1,?????????????????//文件大小限制,單位MB
??MaxBackups:?3,?????????????????//最大保留日志文件數量
??MaxAge:?????30,????????????????//日志文件保留天數
??Compress:???false,?????????????//是否壓縮處理
?})
代碼中只打印error日志,執(zhí)行代碼 進行觀察

繼續(xù)執(zhí)行

繼續(xù)執(zhí)行


可見最早拆分出的那個error-2023-04-13T05-40-48.715.log
文件不見了~
繼續(xù)執(zhí)行,切分出來的文件數量,也會始終保持3個
完整變化圖:

測試壓縮處理的效果
清掉log文件夾,修改error日志配置:
?errorFileWriteSyncer?:=?zapcore.AddSync(&lumberjack.Logger{
??Filename:???"./log/error.log",?//日志文件存放目錄
??MaxSize:????5,?????????????????//文件大小限制,單位MB
??MaxBackups:?10,?????????????????//最大保留日志文件數量
??MaxAge:?????30,????????????????//日志文件保留天數
??Compress:???false,?????????????//是否壓縮處理
?})
代碼中只打印error日志,執(zhí)行代碼,循環(huán)10000000次, 進行觀察

不壓縮共占用814M存儲空間
清掉log文件夾,修改Compress字段為true,執(zhí)行代碼:

啟用壓縮后,僅占用了30M磁盤空間!
不太好的地方就是不方便直接查看了,需要解壓后查看。但大大省了所占用的空間
golang zap日志庫使用[2]
lumberjack這個庫目前只支持按文件大小切割(按時間切割效率低且不能保證日志數據不被破壞,詳情見https://github.com/natefinch/lumberjack/issues/54)
想按日期切割可以使用github.com/lestrrat-go/file-rotatelogs[3]這個庫(目前不維護了)
file-rotatelogs實現按時間的切割
注意:
github.com/lestrrat-go/file-rotatelogs[4](2021年后不更新了) 和 github.com/lestrrat/go-file-rotatelogs[5](2018年以后就不更新了) 兩個不一樣。。前面那個是更新的,作者是一個人...
(有一個linux系統上的日志工具,也叫l(wèi)ogrotate)
logrotate 是一個用于日志文件輪換的 Go 語言庫,支持按時間輪換、按文件大小輪換和按行數輪換。還支持在輪換時壓縮文件、刪除舊文件、給文件添加時間戳等功能
用zap和go-file-rotatelogs實現日志的記錄和日志按時間分割[6]
WithRotationCount和WithMaxAge兩個選項不能共存,只能設置一個(都設置編譯時不會出錯,但運行時會報錯。也是為了防止影響切分的處理邏輯):
panic: options MaxAge and RotationCount cannot be both set
package?main
import?(
?"fmt"
?"io"
?"net/http"
?"time"
?rotatelogs?"github.com/lestrrat-go/file-rotatelogs"
?"go.uber.org/zap"
?"go.uber.org/zap/zapcore"
)
//?使用file-rotatelogs做切分
var?sugarLogger?*zap.SugaredLogger
func?main()?{
?fmt.Println("shuang提示:begin?main")
?InitLogger()
?defer?sugarLogger.Sync()
?for?i?:=?0;?i?<?100000;?i++?{
??simpleHttpGet("www.cnblogs.com")
??simpleHttpGet("https://www.baidu.com")
?}
}
//?例子,http訪問url,返回狀態(tài)
func?simpleHttpGet(url?string)?{
?fmt.Println("begin?simpleHttpGet:"?+?url)
?sugarLogger.Debugf("Trying?to?hit?GET?request?for?%s",?url)
?resp,?err?:=?http.Get(url)
?if?err?!=?nil?{
??sugarLogger.Errorf("Error?fetching?URL?%s?:?Error?=?%s",?url,?err)
?}?else?{
??sugarLogger.Infof("Success!?statusCode?=?%s?for?URL?%s",?resp.Status,?url)
??resp.Body.Close()
?}
}
func?InitLogger()?{
?encoder?:=?getEncoder()
?//兩個interface,判斷日志等級
?//warnlevel以下歸到info日志
?infoLevel?:=?zap.LevelEnablerFunc(func(lvl?zapcore.Level)?bool?{
??return?lvl?<?zapcore.WarnLevel
?})
?//warnlevel及以上歸到warn日志
?warnLevel?:=?zap.LevelEnablerFunc(func(lvl?zapcore.Level)?bool?{
??return?lvl?>=?zapcore.WarnLevel
?})
?infoWriter?:=?getLogWriter("/Users/fliter/zap-demo/demo2-log/info")
?warnWriter?:=?getLogWriter("/Users/fliter/zap-demo/demo2-log/warn")
?//創(chuàng)建zap.Core,for?logger
?core?:=?zapcore.NewTee(
??zapcore.NewCore(encoder,?infoWriter,?infoLevel),
??zapcore.NewCore(encoder,?warnWriter,?warnLevel),
?)
?//生成Logger
?logger?:=?zap.New(core,?zap.AddCaller())
?sugarLogger?=?logger.Sugar()
}
//?getEncoder
func?getEncoder()?zapcore.Encoder?{
?encoderConfig?:=?zap.NewProductionEncoderConfig()
?encoderConfig.EncodeTime?=?zapcore.ISO8601TimeEncoder
?encoderConfig.EncodeLevel?=?zapcore.CapitalLevelEncoder
?return?zapcore.NewConsoleEncoder(encoderConfig)
}
//?得到LogWriter
func?getLogWriter(filePath?string)?zapcore.WriteSyncer?{
?warnIoWriter?:=?getWriter(filePath)
?return?zapcore.AddSync(warnIoWriter)
}
//?日志文件切割
func?getWriter(filename?string)?io.Writer?{
?//保存日志30天,每1分鐘分割一次日志
?hook,?err?:=?rotatelogs.New(
??filename+"_%Y-%m-%d?%H:%M:%S.log",
??//?為最新的日志建立軟連接,指向最新日志文件
??rotatelogs.WithLinkName(filename),
??//?清理條件:?將已切割的日志文件按條件(數量or時間)直接刪除
??//---?MaxAge?and?RotationCount?cannot?be?both?set??兩者不能同時設置
??//---?RotationCount用來設置最多切割的文件數(超過的會被?從舊到新?清理)
??//---?MaxAge?是設置文件清理前的最長保存時間?最小分鐘為單位
??//---?if?both?are?0,?give?maxAge?a?default?7?*?24?*?time.Hour
??//?WithRotationCount和WithMaxAge兩個選項不能共存,只能設置一個(都設置編譯時不會出錯,但運行時會報錯。也是為了防止影響切分的處理邏輯)
??//rotatelogs.WithRotationCount(10),???????//?超過這個數的文件會被清掉
??rotatelogs.WithMaxAge(time.Hour*24*30),?//?保存多久(設置文件清理前的最長保存時間?最小分鐘為單位)
??//?切分條件(將日志文件做切割;WithRotationTime?and?WithRotationSize?~~兩者任意一個條件達到都會切割~~)
??//?經過親測后發(fā)現,如果日志沒有持續(xù)增加,WithRotationTime設置較小(如10s),并不會按WithRotationTime頻次切分文件。當日志不停增加時,會按照WithRotationTime設置來切分(即便WithRotationTime設置的很小)
??rotatelogs.WithRotationTime(time.Second*10),?????//?10秒分割一次(設置日志切割時間間隔,默認?24?*?time.Hour)
??rotatelogs.WithRotationSize(int64(1*1024*1024*1024)),?//?文件達到多大則進行切割,單位為?bytes;
?)
?if?err?!=?nil?{
??panic(err)
?}
?return?hook
}
驗證其切分功能:
將觸發(fā)切分的文件大小設置得很大(110241024*1024 Byte即1 GB),切分時間設置得較小(10秒分割一次),執(zhí)行代碼,可以觀察到日志文件的變化:


再將觸發(fā)切分的文件大小設置得很小(1102450 Byte即50 KB),切分時間設置得較大(24h分割一次),執(zhí)行代碼,清掉之前的日志,再觀察到日志文件的變化:


將觸發(fā)切分的文件大小設置得很小(1102435 Byte即35 KB),同時切分時間也設置得很小(10s分割一次),執(zhí)行代碼,清掉之前的日志,再觀察到日志文件的變化:

當前日志容量大于配置的容量時,會生成新的日志文件,如果時間一樣,在時間后綴后面會自動加上一個數字后綴,以此區(qū)分同一時間的不同日志文件,如果時間不一樣,則生成新的時間后綴文件 (golang實現分割日志[7])
日志文件中是會出現有的命中時間規(guī)則,有的命中文件大小規(guī)則的情況,兩者命名格式不同,參考上圖
切分之后執(zhí)行壓縮命令

默認是沒有的,不像lumberjack那樣提供Compress選項
前面所提的還支持在輪換時壓縮文件、刪除舊文件、給文件添加時間戳等功能需要自己實現。 提供了一個WithHandler回調函數,發(fā)生切分后會觸發(fā)該函數,可以在其中進項壓縮等操作

改一下代碼(不再請求網站因為速度太慢,直接在for里面寫日志)
不啟用壓縮:

啟用壓縮,效果顯著:

相關代碼:
package?main
import?(
?"archive/zip"
?"fmt"
?"io"
?"net/http"
?"os"
?"path/filepath"
?"reflect"
?"time"
?"github.com/davecgh/go-spew/spew"
?rotatelogs?"github.com/lestrrat-go/file-rotatelogs"
?"go.uber.org/zap"
?"go.uber.org/zap/zapcore"
)
//?使用file-rotatelogs做切分
var?sugarLogger?*zap.SugaredLogger
func?main()?{
?fmt.Println("shuang提示:begin?main")
?InitLogger()
?defer?sugarLogger.Sync()
?for?i?:=?0;?i?<?10000000;?i++?{
??sugarLogger.Infof("測試壓縮后少占用的空間,這是填充文本這是填充文本這是填充文本這是填充文本這是填充文本這是填充文本這是填充文本這是填充文本這是填充文本這是填充文本這是填充文本這是填充文本這是填充文本這是填充文本這是填充文本這是填充文本這是填充文本這是填充文本這是填充文本,i?is?%d",?i)
??//simpleHttpGet("www.cnblogs.com",?i)
??//simpleHttpGet("https://www.baidu.com",?i)
?}
?time.Sleep(10000e9)
}
//?例子,http訪問url,返回狀態(tài)
func?simpleHttpGet(url?string,?i?int)?{
?//fmt.Println("begin?simpleHttpGet:"?+?url)
?sugarLogger.Debugf("Trying?to?hit?GET?request?for?%s,?i?is?%d",?url,?i)
?resp,?err?:=?http.Get(url)
?if?err?!=?nil?{
??sugarLogger.Errorf("Error?fetching?URL?%s?:?Error?=?%s,?i?is?%d",?url,?err,?i)
?}?else?{
??sugarLogger.Infof("Success!?statusCode?=?%s?for?URL?%s,i?is?%d",?resp.Status,?url,?i)
??resp.Body.Close()
?}
}
func?InitLogger()?{
?encoder?:=?getEncoder()
?//兩個interface,判斷日志等級
?//warnlevel以下歸到info日志
?infoLevel?:=?zap.LevelEnablerFunc(func(lvl?zapcore.Level)?bool?{
??return?lvl?<?zapcore.WarnLevel
?})
?//warnlevel及以上歸到warn日志
?warnLevel?:=?zap.LevelEnablerFunc(func(lvl?zapcore.Level)?bool?{
??return?lvl?>=?zapcore.WarnLevel
?})
?infoWriter?:=?getLogWriter("/Users/fliter/zap-demo/demo2-log/info")
?warnWriter?:=?getLogWriter("/Users/fliter/zap-demo/demo2-log/warn")
?//創(chuàng)建zap.Core,for?logger
?core?:=?zapcore.NewTee(
??zapcore.NewCore(encoder,?infoWriter,?infoLevel),
??zapcore.NewCore(encoder,?warnWriter,?warnLevel),
?)
?//生成Logger
?logger?:=?zap.New(core,?zap.AddCaller())
?sugarLogger?=?logger.Sugar()
}
//?getEncoder
func?getEncoder()?zapcore.Encoder?{
?encoderConfig?:=?zap.NewProductionEncoderConfig()
?encoderConfig.EncodeTime?=?zapcore.ISO8601TimeEncoder
?encoderConfig.EncodeLevel?=?zapcore.CapitalLevelEncoder
?return?zapcore.NewConsoleEncoder(encoderConfig)
}
//?得到LogWriter
func?getLogWriter(filePath?string)?zapcore.WriteSyncer?{
?warnIoWriter?:=?getWriter(filePath)
?return?zapcore.AddSync(warnIoWriter)
}
//?日志文件切割
func?getWriter(filename?string)?io.Writer?{
?//保存日志30天,每1分鐘分割一次日志
?hook,?err?:=?rotatelogs.New(
??filename+"_%Y-%m-%d?%H:%M:%S.log",
??//?為最新的日志建立軟連接,指向最新日志文件
??rotatelogs.WithLinkName(filename),
??//?清理條件:?將已切割的日志文件按條件(數量or時間)直接刪除
??//---?MaxAge?and?RotationCount?cannot?be?both?set??兩者不能同時設置
??//---?RotationCount用來設置最多切割的文件數(超過的會被?從舊到新?清理)
??//---?MaxAge?是設置文件清理前的最長保存時間?最小分鐘為單位
??//---?if?both?are?0,?give?maxAge?a?default?7?*?24?*?time.Hour
??//?WithRotationCount和WithMaxAge兩個選項不能共存,只能設置一個(都設置編譯時不會出錯,但運行時會報錯。也是為了防止影響切分的處理邏輯)
??//rotatelogs.WithRotationCount(10),???????//?超過這個數的文件會被清掉
??rotatelogs.WithMaxAge(time.Hour*24*30),?//?保存多久(設置文件清理前的最長保存時間?最小分鐘為單位)
??//?切分條件(將日志文件做切割;WithRotationTime?and?WithRotationSize?~~兩者任意一個條件達到都會切割~~)
??//?經過親測后發(fā)現,如果日志沒有持續(xù)增加,WithRotationTime設置較小(如10s),并不會按WithRotationTime頻次切分文件。當日志不停增加時,會按照WithRotationTime設置來切分(即便WithRotationTime設置的很小)
??rotatelogs.WithRotationTime(time.Second*10),???????????//?10秒分割一次(設置日志切割時間間隔,默認?24?*?time.Hour)
??rotatelogs.WithRotationSize(int64(1*1024*35000*1024)),?//?文件達到多大則進行切割,單位為?bytes;
??//?其他可選配置
??//default:?rotatelogs.Local?,you?can?set?rotatelogs.UTC
??//rotatelogs.WithClock(rotatelogs.UTC),
??//rotatelogs.WithLocation(time.Local),
??//---?當rotatelogs.New()創(chuàng)建的文件存在時,強制創(chuàng)建新的文件?命名為原文件的名稱+序號,如a.log存在,則創(chuàng)建創(chuàng)建?a.log.1
??//rotatelogs.ForceNewFile(),
??rotatelogs.WithHandler(rotatelogs.Handler(rotatelogs.HandlerFunc(func(e?rotatelogs.Event)?{
???if?e.Type()?!=?rotatelogs.FileRotatedEventType?{
????return
???}
???fmt.Println("切割完成,進行打包壓縮操作")
???spew.Dump("e?is:",?e)
???prevFile?:=?e.(*rotatelogs.FileRotatedEvent).PreviousFile()
???if?prevFile?!=?""?{
????//?進行壓縮
????paths,?fileName?:=?filepath.Split(prevFile)
????//_?=?paths
????//err?:=?Zip("archive.zip",?paths,?prevFile)
????err?:=?ZipFiles(paths+fileName+".zip",?[]string{prevFile})
????fmt.Println("err?is",?err)
????if?err?==?nil?{
?????os.RemoveAll(prevFile)
????}
???}
???fmt.Println("e的類型為:",?reflect.TypeOf(e))
???fmt.Println("------------------")
???fmt.Println()
???fmt.Println()
???fmt.Println()
???//ctx?:=?CleanContext{
???//?Dir:?????????LogsConfig.LogOutputDir,
???//?DirMaxSizeG:?LogsConfig.LogDirMaxSizeG,
???//?DirMaxCount:?LogsConfig.LogDirMaxFileCount,
???//}
???//strategyOne?:=?CleanStrategyOne{}
???//result,?err?:=?NewCleanStrategy(&ctx,?&strategyOne).
???//?Clean().
???//?Result()
???//Warn("文件切割,清理文件策略one已經執(zhí)行完畢;?結果:%v;?錯誤:%v",?result,?err)
??}))),
?)
?if?err?!=?nil?{
??panic(err)
?}
?return?hook
}
//?ZipFiles?compresses?one?or?many?files?into?a?single?zip?archive?file.
//?Param?1:?filename?is?the?output?zip?file's?name.
//?Param?2:?files?is?a?list?of?files?to?add?to?the?zip.
func?ZipFiles(filename?string,?files?[]string)?error?{
?newZipFile,?err?:=?os.Create(filename)
?if?err?!=?nil?{
??return?err
?}
?defer?newZipFile.Close()
?zipWriter?:=?zip.NewWriter(newZipFile)
?defer?zipWriter.Close()
?//?Add?files?to?zip
?for?_,?file?:=?range?files?{
??if?err?=?AddFileToZip(zipWriter,?file);?err?!=?nil?{
???return?err
??}
?}
?return?nil
}
func?AddFileToZip(zipWriter?*zip.Writer,?filename?string)?error?{
?fileToZip,?err?:=?os.Open(filename)
?if?err?!=?nil?{
??return?err
?}
?defer?fileToZip.Close()
?//?Get?the?file?information
?info,?err?:=?fileToZip.Stat()
?if?err?!=?nil?{
??return?err
?}
?header,?err?:=?zip.FileInfoHeader(info)
?if?err?!=?nil?{
??return?err
?}
?//?Using?FileInfoHeader()?above?only?uses?the?basename?of?the?file.?If?we?want
?//?to?preserve?the?folder?structure?we?can?overwrite?this?with?the?full?path.
?header.Name?=?filename
?//?Change?to?deflate?to?gain?better?compression
?//?see?http://golang.org/pkg/archive/zip/#pkg-constants
?header.Method?=?zip.Deflate
?writer,?err?:=?zipWriter.CreateHeader(header)
?if?err?!=?nil?{
??return?err
?}
?_,?err?=?io.Copy(writer,?fileToZip)
?return?err
}
//
?Zip?compresses?the?specified?files?or?dirs?to?zip?archive.
?If?a?path?is?a?dir?don't?need?to?specify?the?trailing?path?separator.
?For?example?calling?Zip("archive.zip",?"dir",?"csv/baz.csv")?will?get?archive.zip?and?the?content?of?which?is
?baz.csv
?dir
?├──?bar.txt
?└──?foo.txt
?Note?that?if?a?file?is?a?symbolic?link?it?will?be?skipped.
//
?https://blog.csdn.net/K346K346/article/details/122441250
//func?Zip(zipPath?string,?paths?...string)?error?{
//?//?Create?zip?file?and?it's?parent?dir.
//?if?err?:=?os.MkdirAll(filepath.Dir(zipPath),?os.ModePerm);?err?!=?nil?{
//??return?err
//?}
//?archive,?err?:=?os.Create(zipPath)
//?if?err?!=?nil?{
//??return?err
//?}
//?defer?archive.Close()
//
//?//?New?zip?writer.
//?zipWriter?:=?zip.NewWriter(archive)
//?defer?zipWriter.Close()
//
//?//?Traverse?the?file?or?directory.
//?for?_,?rootPath?:=?range?paths?{
//??//?Remove?the?trailing?path?separator?if?path?is?a?directory.
//??rootPath?=?strings.TrimSuffix(rootPath,?string(os.PathSeparator))
//
//??//?Visit?all?the?files?or?directories?in?the?tree.
//??err?=?filepath.Walk(rootPath,?walkFunc(rootPath,?zipWriter))
//??if?err?!=?nil?{
//???return?err
//??}
//?}
//?return?nil
//}
//
//func?walkFunc(rootPath?string,?zipWriter?*zip.Writer)?filepath.WalkFunc?{
//?return?func(path?string,?info?fs.FileInfo,?err?error)?error?{
//??if?err?!=?nil?{
//???return?err
//??}
//
//??//?If?a?file?is?a?symbolic?link?it?will?be?skipped.
//??if?info.Mode()&os.ModeSymlink?!=?0?{
//???return?nil
//??}
//
//??//?Create?a?local?file?header.
//??header,?err?:=?zip.FileInfoHeader(info)
//??if?err?!=?nil?{
//???return?err
//??}
//
//??//?Set?compression?method.
//??header.Method?=?zip.Deflate
//
//??//?Set?relative?path?of?a?file?as?the?header?name.
//??header.Name,?err?=?filepath.Rel(filepath.Dir(rootPath),?path)
//??if?err?!=?nil?{
//???return?err
//??}
//??if?info.IsDir()?{
//???header.Name?+=?string(os.PathSeparator)
//??}
//
//??//?Create?writer?for?the?file?header?and?save?content?of?the?file.
//??headerWriter,?err?:=?zipWriter.CreateHeader(header)
//??if?err?!=?nil?{
//???return?err
//??}
//??if?info.IsDir()?{
//???return?nil
//??}
//??f,?err?:=?os.Open(path)
//??if?err?!=?nil?{
//???return?err
//??}
//??defer?f.Close()
//??_,?err?=?io.Copy(headerWriter,?f)
//??return?err
//?}
//}
完整demo項目代碼 以zap為例,展示如何切割日志文件。 使用Go生態(tài)兩個使用最高的切分庫[8]
關于壓縮:
壓縮解壓文件[9]
Golang 學習筆記(五)- archive/zip 實現壓縮及解壓[10]
Golang zip 壓縮與解壓[11]
更多參考:
zap日志切割,同時支持按日期拆分,也支持按日志固定大小拆分,支持定時清理[12]
go-logrus 日志框架封裝使用[13]
Go zap日志[14]
設計自用的golang日志模塊[15]
golang log rotate file[16]
golang高性能日志庫zap的使用[17]
參考資料
lumberjack: https://github.com/natefinch/lumberjack
[2]golang zap日志庫使用: https://segmentfault.com/a/1190000040443996
[3]github.com/lestrrat-go/file-rotatelogs: https://github.com/lestrrat/go-file-rotatelogs
[4]github.com/lestrrat-go/file-rotatelogs: https://github.com/lestrrat-go/file-rotatelogs
[5]github.com/lestrrat/go-file-rotatelogs: https://github.com/lestrrat/go-file-rotatelogs
[6]用zap和go-file-rotatelogs實現日志的記錄和日志按時間分割: https://blog.csdn.net/weixin_43881017/article/details/110200176
[7]golang實現分割日志: https://blog.csdn.net/qq_42119514/article/details/121372416
[8]以zap為例,展示如何切割日志文件。 使用Go生態(tài)兩個使用最高的切分庫: https://github.com/cuishuang/zap-demo/tree/main
[9]壓縮解壓文件: https://www.topgoer.com/%E5%85%B6%E4%BB%96/%E5%8E%8B%E7%BC%A9%E8%A7%A3%E5%8E%8B%E6%96%87%E4%BB%B6.html
[10]Golang 學習筆記(五)- archive/zip 實現壓縮及解壓: https://learnku.com/articles/23434/golang-learning-notes-five-archivezip-to-achieve-compression-and-decompression
[11]Golang zip 壓縮與解壓: https://blog.csdn.net/K346K346/article/details/122441250
[12]zap日志切割,同時支持按日期拆分,也支持按日志固定大小拆分,支持定時清理: https://blog.csdn.net/qq_22186119/article/details/122003691
[13]go-logrus 日志框架封裝使用: https://www.jianshu.com/p/722250f0b609
[14]Go zap日志: https://blog.csdn.net/qq_41004932/article/details/119760061
[15]設計自用的golang日志模塊: https://studygolang.com/articles/12537
[16]golang log rotate file: https://juejin.cn/s/golang%20log%20rotate%20file
[17]golang高性能日志庫zap的使用: https://www.jianshu.com/p/910b626f67d9
本文由 mdnice 多平臺發(fā)布