Go語(yǔ)言中配置實(shí)現(xiàn)Logger日志的功能詳解
為什么需要Logger
一般在開(kāi)發(fā)項(xiàng)目的時(shí)候我們都是需要一個(gè)存儲(chǔ)日志的文件,因?yàn)樵诓渴痦?xiàng)目以后,我們只能通過(guò)去篩查日志進(jìn)行檢索問(wèn)題,這時(shí)候日志是否可以呈現(xiàn)清晰這個(gè)對(duì)于我們進(jìn)行排查工作是十分重要的,所以Logger能否展示出我們最想要的錯(cuò)誤展示方式是很有必要的!本章節(jié)的案例是基于gin框架和viper
進(jìn)行編寫(xiě)一個(gè)Logger的日志文件,使用zap
日志庫(kù)進(jìn)行記錄日志,日志會(huì)根據(jù)yaml
文件定義的 mode
進(jìn)行判斷是否是開(kāi)發(fā)環(huán)境還是線上環(huán)境進(jìn)行寫(xiě)的。
實(shí)現(xiàn)Logger.go
首先看一下yaml文件的配置
在yaml文件中定義了一個(gè)app,app下面有一個(gè)mode,這個(gè)mode就是用來(lái)識(shí)別我們是開(kāi)發(fā)環(huán)境還是線上環(huán)境的。
app: name: "web_app" mode: "dev" port: 8080 version: "v0.01" log: level: "debug" filename: "web_app.log" max_size: 200 max_age: 30 max_backups: 7
settings的定義
var Conf AppConfig type AppConfig struct { App App `mapstructure:"app" yaml:"app"` Log LogConfig `mapstructure:"log"` } type App struct { Name string `mapstructure:"name"` Mode string `mapstructure:"mode"` Version string `mapstructure:"version"` Port int `mapstructure:"port" yaml:"port"` } type LogConfig struct { Level string `mapstructure:"level"` Filename string `mapstructure:"filename"` MaxSize int `mapstructure:"max_size"` MaxAge int `mapstructure:"max_age"` MaxBackups int `mapstructure:"max_backups"` }
Logger.go
這里需要結(jié)合自己的實(shí)際進(jìn)行修改,如果復(fù)制了import中的依賴(lài),需要執(zhí)行一下:go mod tidy
package logger import( "github.com/gin-gonic/gin" "github.com/spf13/viper" "go.uber.org/zap" "go.uber.org/zap/zapcore" "gopkg.in/natefinch/lumberjack.v2" ) var logger *zap.Logger // Init 的兩個(gè)參數(shù): cfg 和 mode func Init(cfg *settings.LogConfig, mode string) (err error) { writeSyncer := getLogWriter(cfg.Filename, cfg.MaxSize, cfg.MaxBackups, cfg.MaxAge) encoder := getEncoder() var l = new(zapcore.Level) err = l.UnmarshalText([]byte(cfg.Level)) if err != nil { return err } var core zapcore.Core // 開(kāi)發(fā)模式,日志輸出終端 if mode == "dev" { consoleEncoder := zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig()) core = zapcore.NewTee( zapcore.NewCore(encoder, writeSyncer, l), // 保存在日志中 // 錯(cuò)誤信息展示在終端 zapcore.NewCore(consoleEncoder, zapcore.Lock(os.Stdout), zapcore.DebugLevel), ) } else { core = zapcore.NewCore(encoder, writeSyncer, l) } logger = zap.New(core, zap.AddCaller()) lg := zap.New(core, zap.AddCaller()) // 替換zap庫(kù)中的全局 zap.ReplaceGlobals(lg) return err } func getEncoder() zapcore.Encoder { //return zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()) encodeConfig := zapcore.EncoderConfig{ LevelKey: "level", TimeKey: "ts", NameKey: "logger", CallerKey: "caller", StacktraceKey: "stacktrace", LineEnding: zapcore.DefaultLineEnding, EncodeLevel: zapcore.LowercaseLevelEncoder, EncodeTime: zapcore.ISO8601TimeEncoder, EncodeDuration: zapcore.SecondsDurationEncoder, EncodeCaller: zapcore.ShortCallerEncoder, } //return zapcore.NewConsoleEncoder(zap.NewProductionEncoderConfig()) return zapcore.NewConsoleEncoder(encodeConfig) } // GinLogger 接收gin框架默認(rèn)的日志 func GinLogger() gin.HandlerFunc { return func(c *gin.Context) { start := time.Now() path := c.Request.URL.Path query := c.Request.URL.RawQuery c.Next() cost := time.Since(start) zap.L().Info(path, zap.Int("status", c.Writer.Status()), zap.String("method", c.Request.Method), zap.String("path", path), zap.String("query", query), zap.String("ip", c.ClientIP()), zap.String("user-agent", c.Request.UserAgent()), zap.String("errors", c.Errors.ByType(gin.ErrorTypePrivate).String()), zap.Duration("cost", cost), ) } } func getLogWriter(string, int, int, int) zapcore.WriteSyncer { lumberJackLogger := &lumberjack.Logger{ Filename: viper.GetString("log.filename"), MaxSize: viper.GetInt("log.max_size"), // 單位是M MaxBackups: viper.GetInt("log.max_backups"), // 備份數(shù)量 MaxAge: viper.GetInt("log.max_age"), // 最大備份天數(shù) Compress: false, // 是否壓縮 } return zapcore.AddSync(lumberJackLogger) } func GinRecovery(stack bool) gin.HandlerFunc { return func(c *gin.Context) { defer func() { if err := recover(); err != nil { // Check for a broken connection, as it is not really a // condition that warrants a panic stack trace. var brokenPipe bool if ne, ok := err.(*net.OpError); ok { if se, ok := ne.Err.(*os.SyscallError); ok { if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") { brokenPipe = true } } } httpRequest, _ := httputil.DumpRequest(c.Request, false) if brokenPipe { zap.L().Error(c.Request.URL.Path, zap.Any("error", err), zap.String("request", string(httpRequest)), ) // If the connection is dead, we can't write a status to it. c.Error(err.(error)) // nolint: errcheck c.Abort() return } if stack { zap.L().Error("[Recovery from panic]", zap.Any("error", err), zap.String("request", string(httpRequest)), zap.String("stack", string(debug.Stack())), ) } else { zap.L().Error("[Recovery from panic]", zap.Any("error", err), zap.String("request", string(httpRequest)), ) } c.AbortWithStatus(http.StatusInternalServerError) } }() c.Next() } }
最后在main.go中進(jìn)行注冊(cè)方法
func main() { // 1、加載配置文件 if err := settings.Init(); err != nil { fmt.Println("init setting failed", err) return } // 2、初始化日志 if err := logger.Init(&settings.Conf.Log, settings.Conf.App.Mode); err != nil { fmt.Printf("init logger failed, err:%v\n", err) } defer zap.L().Sync() zap.L().Debug("logger init success...") }
在開(kāi)發(fā)環(huán)境(dev)中的測(cè)試結(jié)果
與自己想要的結(jié)果是一致的:
到此這篇關(guān)于Go語(yǔ)言中配置實(shí)現(xiàn)Logger日志的功能詳解的文章就介紹到這了,更多相關(guān)Go配置Logger日志內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Golang操作DuckDB實(shí)戰(zhàn)案例分享
DuckDB是一個(gè)嵌入式SQL數(shù)據(jù)庫(kù)引擎,它與眾所周知的SQLite非常相似,但它是為olap風(fēng)格的工作負(fù)載設(shè)計(jì)的,DuckDB支持各種數(shù)據(jù)類(lèi)型和SQL特性,憑借其在以?xún)?nèi)存為中心的環(huán)境中處理高速分析的能力,它迅速受到數(shù)據(jù)科學(xué)家和分析師的歡迎,在這篇博文中,我們將探索在Go中使用DuckDB2025-01-01詳解go-zero如何實(shí)現(xiàn)計(jì)數(shù)器限流
這篇文章主要來(lái)和大家說(shuō)說(shuō)限流,主要包括計(jì)數(shù)器限流算法以及具體的代碼實(shí)現(xiàn),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-08-08GoLang使goroutine停止的五種方法實(shí)例
goroutine是Go并行設(shè)計(jì)的核心,下面這篇文章主要給大家介紹了關(guān)于GoLang使goroutine停止的五種方法,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-07-07Golang?內(nèi)存模型The?Go?Memory?Model
這篇文章主要為大家介紹了Golang?內(nèi)存模型The?Go?Memory?Model實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11Go語(yǔ)言利用ffmpeg轉(zhuǎn)hls實(shí)現(xiàn)簡(jiǎn)單視頻直播
這篇文章主要為大家介紹了Go語(yǔ)言利用ffmpeg轉(zhuǎn)hls實(shí)現(xiàn)簡(jiǎn)單視頻直播,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-04-04GO的基礎(chǔ)知識(shí)掃盲注意事項(xiàng)
這篇文章主要介紹了GO的基礎(chǔ)知識(shí)注意事項(xiàng),本文是GO語(yǔ)言小白的掃盲文,主要講解了go語(yǔ)言的基本知識(shí),GO程序目錄結(jié)構(gòu),GO程序包的導(dǎo)入與別名運(yùn)用,GO內(nèi)置關(guān)鍵字,GO注釋方法需要的朋友可以參考下2022-12-12Go語(yǔ)言實(shí)現(xiàn)類(lèi)似c++中的多態(tài)功能實(shí)例
Go本身不具有多態(tài)的特性,不能夠像Java、C++那樣編寫(xiě)多態(tài)類(lèi)、多態(tài)方法。但是,使用Go可以編寫(xiě)具有多態(tài)功能的類(lèi)綁定的方法。下面來(lái)一起看看吧2016-09-09Go 實(shí)現(xiàn)基于Token 的登錄流程深度分析
Token 認(rèn)證機(jī)制的核心思想是,服務(wù)端在用戶(hù)登錄時(shí)生成一個(gè) Token,客戶(hù)端在后續(xù)的請(qǐng)求中攜帶這個(gè) Token,服務(wù)端通過(guò)驗(yàn)證 Token 的有效性來(lái)確認(rèn)用戶(hù)的身份,本文將帶你深入探索基于 Token 的登錄流程,這是一種更為靈活且適用于現(xiàn)代應(yīng)用架構(gòu)的認(rèn)證方式2024-03-03