GoLang基于zap日志庫(kù)的封裝過(guò)程詳解
zap日志封裝
Zap是一個(gè)高性能、結(jié)構(gòu)化日志庫(kù),專(zhuān)為Go語(yǔ)言設(shè)計(jì)。它由Uber開(kāi)源,并且在Go社區(qū)中非常受歡迎。它的設(shè)計(jì)目標(biāo)是提供一個(gè)簡(jiǎn)單易用、高效穩(wěn)定、靈活可擴(kuò)展的日志系統(tǒng)。
以下是Zap的一些主要特點(diǎn):
1.高性能:Zap的性能非常出色,可以在不影響應(yīng)用程序性能的情況下記錄大量的日志。它的性能比其他Go語(yǔ)言的日志庫(kù)高出數(shù)倍,這使得它成為高負(fù)載生產(chǎn)環(huán)境中的不錯(cuò)選擇。
2.結(jié)構(gòu)化日志:Zap支持結(jié)構(gòu)化日志,這意味著你可以在日志中包含結(jié)構(gòu)化數(shù)據(jù),而不是只是簡(jiǎn)單的文本。這個(gè)功能非常有用,因?yàn)樗梢宰屇愀菀椎貙?duì)日志進(jìn)行分析和搜索。
3.可擴(kuò)展:Zap提供了一個(gè)靈活的接口,可以讓你輕松地添加自定義的日志輸出器和格式化器。這使得它非常適合在大型項(xiàng)目中使用。
4.模塊化:Zap提供了一個(gè)模塊化的設(shè)計(jì),可以讓你選擇僅使用你需要的功能。這使得它非常適合在不同的項(xiàng)目中使用,因?yàn)槟憧梢灾皇褂媚阈枰墓δ?,而不必使用整個(gè)庫(kù)。
5.安全:Zap使用了一個(gè)嚴(yán)格的日志記錄器接口,這可以確保你的應(yīng)用程序的日志記錄不會(huì)被惡意軟件篡改或刪除。
實(shí)現(xiàn)方式
yaml配置文件
在根目錄下創(chuàng)建一個(gè)configs文件夾,然后再創(chuàng)建zap.debug.yaml
# zap logger configuration Zap: Level: 'info' Prefix: 'gin-vue-admin' Format: 'console' Director: 'logs' EncodeLevel: 'LowercaseColorLevelEncoder' StacktraceKey: 'stacktrace' MaxAge: 30 # 默認(rèn)日志留存默認(rèn)以天為單位 ShowLine: true LogInConsole: true
放置在全局Config中config.go
我的創(chuàng)建是使用protobuf快速創(chuàng)建出來(lái)的,如果你不是使用protobuf,你可以忽略這個(gè)tag。在根目錄下創(chuàng)建一個(gè)config文件,然后創(chuàng)建一個(gè)config.go 文件用來(lái)存放全局的config。
config.go 文件
type Config struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Zap *Zap `protobuf:"bytes,2,opt,name=Zap,proto3" json:"Zap,omitempty"`
}
// Zap zap logger config
type Zap struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// Level 級(jí)別
Level string `protobuf:"bytes,1,opt,name=Level,proto3" json:"Level,omitempty"`
// Prefix 日志前綴
Prefix string `protobuf:"bytes,2,opt,name=Prefix,proto3" json:"Prefix,omitempty"`
// Format 輸出
Format string `protobuf:"bytes,3,opt,name=Format,proto3" json:"Format,omitempty"`
// Director 日志文件夾
Director string `protobuf:"bytes,4,opt,name=Director,proto3" json:"Director,omitempty"`
// EncodeLevel 編碼級(jí)
EncodeLevel string `protobuf:"bytes,5,opt,name=EncodeLevel,proto3" json:"EncodeLevel,omitempty"`
// StacktraceKey 棧名
StacktraceKey string `protobuf:"bytes,6,opt,name=StacktraceKey,proto3" json:"StacktraceKey,omitempty"`
// MaxAge 日志留存時(shí)間
MaxAge int64 `protobuf:"varint,7,opt,name=MaxAge,proto3" json:"MaxAge,omitempty"`
// ShowLine 顯示行
ShowLine bool `protobuf:"varint,8,opt,name=ShowLine,proto3" json:"ShowLine,omitempty"`
// LogInConsole 輸出控制臺(tái)
LogInConsole bool `protobuf:"varint,9,opt,name=LogInConsole,proto3" json:"LogInConsole,omitempty"`
}創(chuàng)建zap.go
然后再config文件夾再創(chuàng)建zap.go文件,該文件主要是用來(lái)將我們配置文件的內(nèi)容轉(zhuǎn)換成為zap所認(rèn)識(shí)的內(nèi)容。
type LevelEncoder int
// ZapEncodeLevel 根據(jù) EncodeLevel 返回 zapcore.LevelEncoder
func (x *Zap) ZapEncodeLevel() zapcore.LevelEncoder {
switch {
case x.EncodeLevel == "LowercaseLevelEncoder": // 小寫(xiě)編碼器(默認(rèn))
return zapcore.LowercaseLevelEncoder
case x.EncodeLevel == "LowercaseColorLevelEncoder": // 小寫(xiě)編碼器帶顏色
return zapcore.LowercaseColorLevelEncoder
case x.EncodeLevel == "CapitalLevelEncoder": // 大寫(xiě)編碼器
return zapcore.CapitalLevelEncoder
case x.EncodeLevel == "CapitalColorLevelEncoder": // 大寫(xiě)編碼器帶顏色
return zapcore.CapitalColorLevelEncoder
default:
return zapcore.LowercaseLevelEncoder
}
}
// TransportLevel 根據(jù)字符串轉(zhuǎn)化為 zapcore.Level
func (x *Zap) TransportLevel() zapcore.Level {
x.Level = strings.ToLower(x.Level)
switch x.Level {
case "debug":
return zapcore.DebugLevel
case "info":
return zapcore.InfoLevel
case "warn":
return zapcore.WarnLevel
case "error":
return zapcore.WarnLevel
case "dpanic":
return zapcore.DPanicLevel
case "panic":
return zapcore.PanicLevel
case "fatal":
return zapcore.FatalLevel
default:
return zapcore.DebugLevel
}
}創(chuàng)建核心文件core
這里我主要放置一些Initialization 初始化的方法,比如gorm、viper、zap等一些核心的內(nèi)容。
創(chuàng)建zap.go
在core文件中創(chuàng)建zap.go 文件,該文件主要是初始化自己配置的zap日志,一般會(huì)把日志分割、日志存放地、注冊(cè)到全局等放置在這里,當(dāng)然為了讓代碼更加整潔和可閱讀性下,我們會(huì)對(duì)這里封裝成為方法。注: _zap 命名方式是因?yàn)楹蛕ap包重名了,可以根據(jù)自己喜好命名,但是這樣的命明也就是僅在該文件下生效,你可以認(rèn)為這樣變成了所謂的私有性
core/zap.go
var Zap = new(_zap)
type _zap struct{}
// Initialization 初始化
func (c *_zap) Initialization() {
ok, _ := utils.Directory.PathExists(global.Config.Zap.Director)
if !ok { // 判斷是否有 global.Config.Zap.Director 文件夾
fmt.Printf("create %v directory\n", global.Config.Zap.Director)
_ = os.Mkdir(global.Config.Zap.Director, os.ModePerm)
}
cores := internal.Zap.GetZapCores() // 獲取 zap 核心切片
logger := zap.New(zapcore.NewTee(cores...)) // 初始化 zap.Logger
if global.Config.Zap.ShowLine { // 判斷是否顯示行
logger = logger.WithOptions(zap.AddCaller())
}
zap.ReplaceGlobals(logger) // logger 注冊(cè)到全局, 通過(guò) zap.L() 調(diào)用日志組件
}創(chuàng)建私有訪(fǎng)問(wèn)方法
在core文件夾下創(chuàng)建interal文件中,這個(gè)internal的方法僅能在這個(gè)core下的文件才可以進(jìn)行訪(fǎng)問(wèn),其他文件夾比如service、api、util等文件夾無(wú)法訪(fǎng)問(wèn),這樣使得這些方法不會(huì)泄漏導(dǎo)致程序結(jié)構(gòu)的污染性,我個(gè)人也比較喜歡這樣去命名以及去寫(xiě)代碼。注:下列寫(xiě)法:core/interal/zap.go ,但是我們調(diào)取interal文件夾的方法不需要通過(guò)core去調(diào)取,直接使用interal進(jìn)行訪(fǎng)問(wèn)。
core/interal/zap.go
var Zap = new(_zap)
type _zap struct{}
// GetEncoder 獲取 zapcore.Encoder
func (z *_zap) GetEncoder() zapcore.Encoder {
// 日志的內(nèi)容格式有 控制臺(tái) 和 json
if global.Config.Zap.Format == "json" {
return zapcore.NewJSONEncoder(z.GetEncoderConfig())
}
return zapcore.NewConsoleEncoder(z.GetEncoderConfig())
}
// GetEncoderConfig 獲取zapcore.EncoderConfig
func (z *_zap) GetEncoderConfig() zapcore.EncoderConfig {
return zapcore.EncoderConfig{
MessageKey: "message",
LevelKey: "level",
TimeKey: "time",
NameKey: "logger",
CallerKey: "caller",
StacktraceKey: global.Config.Zap.StacktraceKey,
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: global.Config.Zap.ZapEncodeLevel(),
EncodeTime: z.CustomTimeEncoder,
EncodeDuration: zapcore.SecondsDurationEncoder,
EncodeCaller: zapcore.FullCallerEncoder,
}
}
// GetEncoderCore 獲取Encoder的 zapcore.Core
func (z *_zap) GetEncoderCore(l zapcore.Level, level zap.LevelEnablerFunc) zapcore.Core {
syncer, err := FileRotatelogs.GetWriteSyncer(l.String()) // 使用file-rotatelogs進(jìn)行日志分割
if err != nil {
fmt.Printf("Get Write Syncer Failed err:%v", err.Error())
return nil
}
return zapcore.NewCore(z.GetEncoder(), syncer, level)
}
// CustomTimeEncoder 自定義日志輸出時(shí)間格式
func (z *_zap) CustomTimeEncoder(t time.Time, encoder zapcore.PrimitiveArrayEncoder) {
encoder.AppendString(global.Config.Zap.Prefix + " " + t.Format("2006-01-02 15:04:05.000"))
}
// GetZapCores 根據(jù)配置文件的Level獲取 []zapcore.Core
func (z *_zap) GetZapCores() []zapcore.Core {
cores := make([]zapcore.Core, 0, 7)
for level := global.Config.Zap.TransportLevel(); level <= zapcore.FatalLevel; level++ {
cores = append(cores, z.GetEncoderCore(level, z.GetLevelPriority(level)))
}
return cores
}
// GetLevelPriority 根據(jù) zapcore.Level 獲取 zap.LevelEnablerFunc
func (z *_zap) GetLevelPriority(level zapcore.Level) zap.LevelEnablerFunc {
switch level {
case zapcore.DebugLevel:
return func(level zapcore.Level) bool { // 調(diào)試級(jí)別
return level == zap.DebugLevel
}
case zapcore.InfoLevel:
return func(level zapcore.Level) bool { // 日志級(jí)別
return level == zap.InfoLevel
}
case zapcore.WarnLevel:
return func(level zapcore.Level) bool { // 警告級(jí)別
return level == zap.WarnLevel
}
case zapcore.ErrorLevel:
return func(level zapcore.Level) bool { // 錯(cuò)誤級(jí)別
return level == zap.ErrorLevel
}
case zapcore.DPanicLevel:
return func(level zapcore.Level) bool { // dpanic級(jí)別
return level == zap.DPanicLevel
}
case zapcore.PanicLevel:
return func(level zapcore.Level) bool { // panic級(jí)別
return level == zap.PanicLevel
}
case zapcore.FatalLevel:
return func(level zapcore.Level) bool { // 終止級(jí)別
return level == zap.FatalLevel
}
default:
return func(level zapcore.Level) bool { // 調(diào)試級(jí)別
return level == zap.DebugLevel
}
}
}在main中注冊(cè)
在根目錄下創(chuàng)建一個(gè)main.go文件(這個(gè)就不多啰嗦了…)
main.go
func main() {
core.Zap.Initialization()
}到此這篇關(guān)于GoLang基于zap日志庫(kù)的封裝過(guò)程詳解的文章就介紹到這了,更多相關(guān)Go zap日志封裝內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
go語(yǔ)言中sort包的實(shí)現(xiàn)方法與應(yīng)用詳解
golang中也實(shí)現(xiàn)了排序算法的包sort包,所以下面這篇文章主要給大家介紹了關(guān)于go語(yǔ)言中sort包的實(shí)現(xiàn)方法與應(yīng)用的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友們可以參考借鑒,下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-11-11
Go語(yǔ)言中內(nèi)存泄漏的常見(jiàn)案例與解決方法
Go雖然是自動(dòng)GC類(lèi)型的語(yǔ)言,但在編碼過(guò)程中如果不注意,很容易造成內(nèi)存泄漏的問(wèn)題,本文為大家整理了一些內(nèi)存泄漏的常見(jiàn)Case與解決方法,希望對(duì)大家有所幫助2024-03-03
golang實(shí)現(xiàn)unicode轉(zhuǎn)換為字符串string的方法
這篇文章主要介紹了golang實(shí)現(xiàn)unicode轉(zhuǎn)換為字符串string的方法,實(shí)例分析了Go語(yǔ)言編碼轉(zhuǎn)換的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2016-07-07
Golang實(shí)現(xiàn)http文件上傳小功能的案例
這篇文章主要介紹了Golang實(shí)現(xiàn)http文件上傳小功能的案例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-05-05
go rate 原生標(biāo)準(zhǔn)限速庫(kù)的使用
本文主要介紹了Go標(biāo)準(zhǔn)庫(kù)golang.org/x/time/rate實(shí)現(xiàn)限流,采用令牌桶算法控制請(qǐng)求速率,提供Allow/Reserve/Wait方法,具有一定的參考價(jià)值,感興趣的可以了解一下2025-05-05
Golang安裝和使用protocol-buffer流程介紹
這篇文章主要介紹了Golang安裝和使用protocol-buffer過(guò)程,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-09-09
Golang實(shí)現(xiàn)web文件共享服務(wù)的示例代碼
這篇文章主要介紹了Golang實(shí)現(xiàn)web文件共享服務(wù)的示例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-10-10
Go語(yǔ)言實(shí)現(xiàn)IP段范圍校驗(yàn)示例
這篇文章主要介紹了Go語(yǔ)言實(shí)現(xiàn)IP段范圍校驗(yàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09

