<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
我們在基於gin框架開發專案時通常都會選擇使用專業的紀錄檔庫來記錄專案中的紀錄檔,go語言常用的紀錄檔庫有zap
、logrus
等.
但是我們該如何在紀錄檔中記錄gin框架本身輸出的那些紀錄檔呢?
首先我們來看一個最簡單的gin專案:
func main() { r := gin.Default() r.GET("/hello", func(c *gin.Context) { c.String("hello liwenzhou.com!") }) r.Run( }
接下來我們看一下gin.Default()
的原始碼:
func Default() *Engine { debugPrintWARNINGDefault() engine := New() engine.Use(Logger(), Recovery()) return engine }
也就是我們在使用gin.Default()
的同時是用到了gin框架內的兩個預設中介軟體Logger()
和Recovery()
。
其中Logger()
是把gin框架本身的紀錄檔輸出到標準輸出(我們本地開發偵錯時在終端輸出的那些紀錄檔就是它的功勞),而Recovery()
是在程式出現panic的時候恢復現場並寫入500響應的。
我們可以模仿Logger()
和Recovery()
的實現,使用我們的紀錄檔庫來接收gin框架預設輸出的紀錄檔。
這裡以zap為例,我們實現兩個中介軟體如下:
// GinLogger 接收gin框架預設的紀錄檔 func GinLogger(logger *zap.Logger) 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) logger.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), ) } } // GinRecovery recover掉專案可能出現的panic func GinRecovery(logger *zap.Logger, 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 { logger.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 { logger.Error("[Recovery from panic]", zap.Any("error", err), zap.String("request", string(httpRequest)), zap.String("stack", string(debug.Stack())), ) } else { logger.Error("[Recovery from panic]", zap.Any("error", err), zap.String("request", string(httpRequest)), ) } c.AbortWithStatus(http.StatusInternalServerError) } }() c.Next() } }
如果不想自己實現,可以使用github上有別人封裝好的https://github.com/gin-contrib/zap。
這樣我們就可以在gin框架中使用我們上面定義好的兩個中介軟體來代替gin框架預設的Logger()
和Recovery()
了。
r := gin.New() r.Use(GinLogger(), GinRecovery())
最後我們再加入我們專案中常用的紀錄檔切割,完整版的logger.go
程式碼如下:
package logger import ( "gin_zap_demo/config" "net" "net/http" "net/http/httputil" "os" "runtime/debug" "strings" "time" "github.com/gin-gonic/gin" "github.com/natefinch/lumberjack" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) var lg *zap.Logger // InitLogger 初始化Logger func InitLogger(cfg *config.LogConfig) (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 } core := zapcore.NewCore(encoder, writeSyncer, l) lg = zap.New(core, zap.AddCaller()) zap.ReplaceGlobals(lg) // 替換zap包中全域性的logger範例,後續在其他包中只需使用zap.L()呼叫即可 return } func getEncoder() zapcore.Encoder { encoderConfig := zap.NewProductionEncoderConfig() encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder encoderConfig.TimeKey = "time" encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder encoderConfig.EncodeDuration = zapcore.SecondsDurationEncoder encoderConfig.EncodeCaller = zapcore.ShortCallerEncoder return zapcore.NewJSONEncoder(encoderConfig) } func getLogWriter(filename string, maxSize, maxBackup, maxAge int) zapcore.WriteSyncer { lumberJackLogger := &lumberjack.Logger{ Filename: filename, MaxSize: maxSize, MaxBackups: maxBackup, MaxAge: maxAge, } return zapcore.AddSync(lumberJackLogger) } // GinLogger 接收gin框架預設的紀錄檔 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) lg.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), ) } } // GinRecovery recover掉專案可能出現的panic,並使用zap記錄相關紀錄檔 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 { lg.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 { lg.Error("[Recovery from panic]", zap.Any("error", err), zap.String("request", string(httpRequest)), zap.String("stack", string(debug.Stack())), ) } else { lg.Error("[Recovery from panic]", zap.Any("error", err), zap.String("request", string(httpRequest)), ) } c.AbortWithStatus(http.StatusInternalServerError) } }() c.Next() } }
然後定義紀錄檔相關設定:
type LogConfig struct { Level string `json:"level"` Filename string `json:"filename"` MaxSize int `json:"maxsize"` MaxAge int `json:"max_age"` MaxBackups int `json:"max_backups"` }
在專案中先從組態檔載入設定資訊,再呼叫logger.InitLogger(config.Conf.LogConfig)即可完成logger範例的初識化。其中,通過r.Use(logger.GinLogger(), logger.GinRecovery(true))註冊我們的中介軟體來使用zap接收gin框架自身的紀錄檔,在專案中需要的地方通過使用zap.L().Xxx()
方法來記錄自定義紀錄檔資訊。
package main import ( "fmt" "gin_zap_demo/config" "gin_zap_demo/logger" "net/http" "os" "go.uber.org/zap" "github.com/gin-gonic/gin" ) func main() { // load config from config.json if len(os.Args) < 1 { return } if err := config.Init(os.Args[1]); err != nil { panic(err) } // init logger if err := logger.InitLogger(config.Conf.LogConfig); err != nil { fmt.Printf("init logger failed, err:%vn", err) return } gin.SetMode(config.Conf.Mode) r := gin.Default() // 註冊zap相關中介軟體 r.Use(logger.GinLogger(), logger.GinRecovery(true)) r.GET("/hello", func(c *gin.Context) { // 假設你有一些資料需要記錄到紀錄檔中 var ( name = "q1mi" age = 18 ) // 記錄紀錄檔並使用zap.Xxx(key, val)記錄相關欄位 zap.L().Debug("this is hello func", zap.String("user", name), zap.Int("age", age)) c.String(http.StatusOK, "hello liwenzhou.com!") }) addr := fmt.Sprintf(":%v", config.Conf.Port) r.Run(addr) }
以上就是zap接收gin框架預設的紀錄檔並設定紀錄檔歸檔範例的詳細內容,更多關於zap紀錄檔gin框架預設設定歸檔的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45