温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

zap接收gin框架默认的日志并配置日志归档的方法

发布时间:2022-04-16 13:34:42 来源:亿速云 阅读:588 作者:iii 栏目:开发技术

这篇“zap接收gin框架默认的日志并配置日志归档的方法”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“zap接收gin框架默认的日志并配置日志归档的方法”文章吧。

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响应的。

基于zap的中间件

我们可以模仿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())

在gin项目中使用zap

最后我们再加入我们项目中常用的日志切割,完整版的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:%v\n", 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框架默认的日志并配置日志归档的方法”这篇文章的内容,相信大家都有了一定的了解,希望小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注亿速云行业资讯频道。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI