Go开发中配置一个Logger日志的功能实现(结合zap日志库)
为什么需要Logger
一般在开发项目的时候我们都是需要一个存储日志的文件,因为在部署项目以后,我们只能通过去筛查日志进行检索问题,这时候日志是否可以呈现清晰这个对于我们进行排查工作是十分重要的,所以Logger能否展示出我们最想要的错误展示方式是很有必要的!本章节的案例是基于gin框架和viper
进行编写一个Logger的日志文件,使用zap
日志库进行记录日志,日志会根据yaml
文件定义的 mode
进行判断是否是开发环境还是线上环境进行写的。
实现Logger.go
首先看一下yaml文件的配置
在yaml文件中定义了一个app,app下面有一个mode,这个mode就是用来识别我们是开发环境还是线上环境的。
app:name: "web_app"mode: "dev"port: 8080version: "v0.01"
log:level: "debug"filename: "web_app.log"max_size: 200max_age: 30max_backups: 7
settings的定义
var Conf AppConfigtype 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
这里需要结合自己的实际进行修改,如果复制了import中的依赖,需要执行一下:go mod tidy
package loggerimport("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 的两个参数: 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// 开发模式,日志输出终端if mode == "dev" {consoleEncoder := zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig())core = zapcore.NewTee(zapcore.NewCore(encoder, writeSyncer, l), // 保存在日志中// 错误信息展示在终端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库中的全局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框架默认的日志
func GinLogger() gin.HandlerFunc {return func(c *gin.Context) {start := time.Now()path := c.Request.URL.Pathquery := c.Request.URL.RawQueryc.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"), // 单位是MMaxBackups: viper.GetInt("log.max_backups"), // 备份数量MaxAge: viper.GetInt("log.max_age"), // 最大备份天数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 boolif 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: errcheckc.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中进行注册方法
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...")
}
在开发环境(dev)中的测试结果
与自己想要的结果是一致的:
Go开发中配置一个Logger日志的功能实现(结合zap日志库)相关推荐
- Android 系统(165)---在apns-conf文件中配置一个read_only字段,使APN不可被编辑
在apns-conf文件中配置一个read_only字段,使APN不可被编辑 如果有需要在apns-conf中配置一个新的read_only字段,并使其生效,比如使得APN不可被编辑.可以最如下的修改 ...
- 动态头像 Android 实现,Android开发中实现一个头像滑动变大变小功能
Android开发中实现一个头像滑动变大变小功能 发布时间:2020-11-21 16:36:20 来源:亿速云 阅读:74 作者:Leah 这篇文章给大家介绍Android开发中实现一个头像滑动变大 ...
- android图片浏览功能,怎么在Android应用中实现一个网页图片浏览功能
怎么在Android应用中实现一个网页图片浏览功能 发布时间:2020-12-05 17:28:31 来源:亿速云 阅读:80 作者:Leah 本篇文章给大家分享的是有关怎么在Android应用中实现 ...
- 【吉大刘大有数据结构绿皮书】向LinkedList类中增加一个函数Contrary,功能为将其所有结点按相反次序链接。
题目 向LinkedList类中增加一个函数Contrary,功能为将其所有结点按相反次序链接. 思路 考研要求用C语言,那我就用C语言(没有面向对象),本题就是个链表倒置算法,先将哨位结点和后面的结 ...
- 手机直播系统开发中关于iOS获取图形验证码功能
在手机直播系统开发中关于iOS获取图形验证码功能介绍,首先进入注册页面后请求图形验证码接口获取图形验证码的数字组合,然后加载到相应的页面上,在图形验证码页面我们定义了几个属性,字符串的数量.显示的线条 ...
- 由安卓开发中的一个坑引发的问题解决之道浅析
博客: 安卓之家 微博: 追风917 CSDN: 蒋朋的家 简书: 追风917 扯扯 最近发生了好多事情,两会,google AI alphaGo 大战李世石,俘获无数少男少女心的韩国电视剧<太 ...
- 记 QT 应用开发中的一个二进制兼容性问题
笔者在参与开发一个集成了 QT 的跨平台桌面应用程序,目标平台是 Windows 和 Mac.一段时间以来,运行 Windows 平台的应用程序时,不断地被类似于如下这样的崩溃问题所折磨. 这里提示说 ...
- Android开发中配置JDK环境的几种方式
文章目录 一.前言 二.IDE设置 三.环境变量 四.gradle中配置 五.参考链接 一.前言 在Android开发中,配置JDK环境有以下几种方式来是Android项目正常编译 - changin ...
- iOS开发中打电话发短信等功能的实现
在APP开发中,可能会涉及到打电话.发短信.发邮件等功能.比如说,通常一个产品的"关于"页面,会有开发者的联系方式,理想情况下,当用户点击该电话号码时,能够自动的帮用户拨出去,就涉 ...
最新文章
- 10分钟掌握RocketMQ的核心知识
- 数据分析之Pandas(一)
- 使用WTMPlus快速搭建发卡网
- 解决一个I2C读写问题
- stm32--FatFs调试过程(SPIFlash)
- android 快捷方式 未安装该应用程序,android,解决手动创建的桌面快捷方式无法跳转到制定的activity的问题,提示未安装应用程序...
- 直接调用内置数据源连接对话框(C#/VB.NET2005源码)
- 朴素贝叶斯算法,贝叶斯分类算法,贝叶斯定理原理
- 时空解析理论的实验检验方法
- delphi7 增加管理员权限_EHR系统的权限设计
- vba 定义类_类接口的实现及应用
- 74ls20设计半加器_用74ls138设计全加器
- 360 自动 html 极速模式,用Meta标签代码让360双核浏览器默认极速模式打开网站不是兼容模式(顺带解决很多兼容性问题)...
- html滚动字幕制作教程,抖音上的字幕怎么弄的?滚动字幕制作方法教程[多图]
- JavaScript 练手小技巧:过年了,用JS写一幅春联吧
- 微信订阅号如何快速涨粉
- 国外支付(Paypal,Cybersource)
- Fine BI、Smart BI、永洪BI、瓴羊Quick BI这些国产BI工具,都擅长哪些功能?
- c++,数组与指针的差别
- RPA操控Outlook邮件