欢迎关注「全栈工程师修炼指南」公众号
点击 👇 下方卡片 即可关注我哟!
设为「星标⭐」每天带你 基础入门 到 进阶实践 再到 放弃学习!
专注 企业运维实践、网络安全、系统运维、应用开发、物联网实战、全栈文章 等知识分享
“ 花开堪折直须折,莫待无花空折枝。 ”
作者主页:[ https://www.weiyigeek.top ]
博客:[ https://blog.weiyigeek.top ]
作者<开发安全运维>学习交流群,回复【学习交流群】即可加入
文章目录:
0x02 如何自定义 Gin 日志格式?
1.自定义定义路由日志的格式
2.自定义原生路由访问日志格式
3.使用 log 自定义日志并按天分隔保存到文件
0x02 如何自定义 Gin 日志格式?
1.自定义定义路由日志的格式
描述: 此处介绍如何定义路由日志的格式,而非使用默认的路由访问日志格式。
例如:默认的路由日志格式 GIN-debug] POST /foo --> main.main.func1 (3 handlers)
, 如果你想要以指定的格式(例如 JSON,key values 或其他格式)记录信息,则可以使用 gin.DebugPrintRouteFunc 指定格式。
package mainimport ("github.com/gin-gonic/gin""log""net/http"
)func main() {// gin 运行模式gin.SetMode(gin.DebugMode)r := gin.Default()// 关键点gin.DebugPrintRouteFunc = func(httpMethod, absolutePath, handlerName string, nuHandlers int) {log.Printf("endpoint %v %v %v %v\n", httpMethod, absolutePath, handlerName, nuHandlers)}r.POST("/foo", func(c *gin.Context) {c.JSON(http.StatusOK, "foo")})r.GET("/bar", func(c *gin.Context) {c.JSON(http.StatusOK, "bar")})r.GET("/status", func(c *gin.Context) {c.JSON(http.StatusOK, "ok")})// 监听并在 0.0.0.0:8080 上启动服务r.Run()
}
执行效果:
偷偷的告诉你哟?【极客全栈修炼】微信小程序已经上线了,
可直接在微信里面直接浏览博主博客了哟,后续将上线更多有趣的小工具。
2.自定义原生路由访问日志格式
描述: 此处是使用 gin.LoggerWithFormatter & gin.LogFormatterParams
实现自定义路由访问日志。
代码示例:
package mainimport ("fmt""io""os""time""github.com/gin-gonic/gin"
)func main() {// 强制日志颜色化gin.ForceConsoleColor()// 禁用控制台颜色// gin.DisableConsoleColor()// 默认为 debug 模式,设置为发布模式gin.SetMode(gin.ReleaseMode)// 将日志同时写入文件和控制台,请使用以下代码 f 表示文件,os,os.Stdout 表示终端f, _ := os.Create("gin.log")// 如果需要同时将日志写入文件和控制台,请使用以下代码。gin.DefaultWriter = io.MultiWriter(f, os.Stdout)// 生成gin实例,即 WSGI 应用程序r := gin.New()// 自定义日志格式// LoggerWithFormatter 中间件会将日志写入 gin.DefaultWriter// By default => gin.DefaultWriter = os.Stdoutr.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {// 自定义格式return fmt.Sprintf("%s - [%s] \"%s %s %s %d %s \"%s\" %s\"\n",param.ClientIP,param.TimeStamp.Format(time.RFC1123),param.Method,param.Path,param.Request.Proto,param.StatusCode,param.Latency,param.Request.UserAgent(),param.ErrorMessage,)}))r.Use(gin.Recovery())// 声明了一个路由及对应的处理函数 (匿名函数)r.GET("/log", func(c *gin.Context) {c.JSON(200, gin.H{"code": "200", "msg": "Test gin logs", "data": "",})})r.Run()
}
执行效果:
3.使用 log 自定义日志并按天分隔保存到文件
描述: 此处使用原生的 log 模块实现自定义路由日志,并载入到Gin的日志中间件中,实现终端与文件同时输出,输入的日志的文件按照天进行分隔,好了,废话不多说直接上代码:
日志中间件: middleware\Logger.go
package middlewareimport ("fmt""io""log""os""time""github.com/gin-gonic/gin"
)// Logger 是一个自定义的日志中间件
func Logger() gin.HandlerFunc {// 日志文件路径logFilePath := "./logs/"// 日志文件名前缀logFileName := "weiyigeek"// 日志文件后缀logFileExt := "log"// 日志文件最大大小,单位为 MBlogFileMaxSize := 1000// 日志文件切割的时间间隔,单位为天logFileSplitDays := 1// 检查日志目录是否存在,不存在则创建err := os.MkdirAll(logFilePath, os.ModePerm)if err != nil {log.Fatalf("Failed to create log directory: %v", err)}// 获取当前时间的年月日now := time.Now()year, month, day := now.Date()// 构造日志文件名logFileName = fmt.Sprintf("%s-%d-%02d-%02d", logFileName, year, month, day)// 打开日志文件logFile, err := os.OpenFile(fmt.Sprintf("%s/%s.%s", logFilePath, logFileName, logFileExt),os.O_WRONLY|os.O_APPEND|os.O_CREATE,0666,)if err != nil {log.Fatalf("Failed to open log file: %v", err)}// 设置日志输出writers := []io.Writer{logFile,os.Stdout}log.SetOutput(io.MultiWriter(writers...))log.SetFlags(log.Ldate | log.Ltime | log.LUTC)return func(c *gin.Context) {// 处理请求前记录日志// 开始时间startTime := time.Now()// 调用该请求的剩余处理程序c.Next()// 结束时间endTime := time.Now()// 执行时间latencyTime := endTime.Sub(startTime)// 请求IPclientIP := c.ClientIP()// remoteIP := c.RemoteIP()// 请求方式reqMethod := c.Request.Method// 请求路由reqUri := c.Request.RequestURI// 请求协议reqProto := c.Request.Proto// 请求来源repReferer := c.Request.Referer()// 请求UAreqUA := c.Request.UserAgent()// 请求响应内容长度resLength := c.Writer.Size()if resLength < 0 {resLength = 0}// 响应状态码statusCode := c.Writer.Status()log.Printf("%s | %3d | %s %10s | \033[44;37m%-6s\033[0m %s %s | %10v | \"%s\" \"%s\"",colorForStatus(statusCode),statusCode,colorForStatus(0),clientIP,// remoteIP,reqMethod,reqUri,reqProto,latencyTime,reqUA,repReferer,)// 判断日志文件是否需要切割fileInfo, err := logFile.Stat()if err != nil {log.Fatalf("Failed to get log file info: %v", err)}_, _, lastDay := endTime.AddDate(0, 0, -1*logFileSplitDays).Date()if fileInfo.Size() > int64(logFileMaxSize*1024*1024) {// 关闭当前日志文件logFile.Close()// 构造新的日志文件名logFileName = fmt.Sprintf("%s-%s", logFileName, time.Now().Format("2006-01-02-15"))// 创建新的日志文件logFile, err = os.OpenFile(fmt.Sprintf("%s/%s.%s", logFilePath, logFileName, logFileExt),os.O_WRONLY|os.O_APPEND|os.O_CREATE,0666,)if err != nil {log.Fatalf("Failed to create log file: %v", err)}// 设置日志输出log.SetOutput(logFile)log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)} else if fileInfo.ModTime().Day() == lastDay {// 关闭当前日志文件logFile.Close()// 构造新的日志文件名logFileName = fmt.Sprintf("%s-%s", logFileName, endTime.Format("2006-01-02"))// 创建新的日志文件logFile, err = os.OpenFile(fmt.Sprintf("%s/%s.%s", logFilePath, logFileName, logFileExt),os.O_WRONLY|os.O_APPEND|os.O_CREATE,0666,)if err != nil {log.Fatalf("Failed to create log file: %v", err)}// 设置日志输出log.SetOutput(logFile)log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)}}
}// colorForStatus 根据 HTTP 状态码返回 ANSI 颜色代码
func colorForStatus(code int) string {switch {case code >= 200 && code < 300:return "\033[42;1;37m" // greencase code >= 300 && code < 400:return "\033[34m" // bluecase code >= 400 && code < 500:return "\033[33m" // yellowcase code == 0:return "\033[0m" // canceldefault:return "\033[31m" // red}
}
亲,文章就要看完了,不关注一下【全栈工程师修炼指南】吗?
入口文件: main.go
package mainimport ("devopsapi/middleware"router "devopsapi/routers""fmt""log""net/http""time""github.com/gin-gonic/gin""golang.org/x/sync/errgroup"
)// 处理属于同一总体任务的子任务的goroutine的集合
var (g errgroup.Group
)func main() {// 指定 gin 运行模式gin.SetMode(global.App.Mode)// 返回一个新的空白Engine实例r := gin.New()// 设置日志中间件r.Use(middleware.Logger())// 加载自定义路由router.Load(r)// Linux、Mac 环境下使用 fvbock/endless 艰辛平滑重启// err := endless.ListenAndServe(fmt.Sprintf("%s:%d", global.App.Host, global.App.Port), r)// if err != nil || err != http.ErrServerClosed {// log.Println("err:", err)// }// W通用:开放监听运行Gin服务server := &http.Server{// Gin运行的监听端口Addr: ":8080",// 要调用的处理程序,http.DefaultServeMux如果为nilHandler: r,// ReadTimeout是读取整个请求(包括正文)的最长持续时间。ReadTimeout: 5 * time.Second,// WriteTimeout是超时写入响应之前的最长持续时间WriteTimeout: 10 * time.Second,// MaxHeaderBytes控制服务器解析请求标头的键和值(包括请求行)时读取的最大字节数 (通常情况下不进行设置)MaxHeaderBytes: 1 << 20,}// 创建 goroutine 中调用给定的函数g.Go(func() error {return server.ListenAndServe()})// goroutine 所有函数调用都返回,然后从中返回第一个非零错误(如果有的话)。if err := g.Wait(); err != nil {log.Fatal(err)}
}
执行结果:
日志实现格式: 2023/06/09 09:59:04 [42;1;37m | 200 | [0m 10.20.172.103 | [44;37mGET [0m /app/version HTTP/1.1 | 144.4µs | "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Edg/113.0.1774.57" ""
本文至此完毕,更多技术文章,尽情等待下篇好文!
原文地址: https://blog.weiyigeek.top/2023/6-2-745.html
如果此篇文章对你有帮助,请你将它分享给更多的人!
学习书籍推荐 往期发布文章
公众号回复【0008】获取【Ubuntu22.04安装与加固脚本】
公众号回复【10001】获取【WinServer安全加固脚本】
公众号回复【10002】获取【KylinOS银河麒麟安全加固脚本】
公众号回复【0011】获取【k8S二进制安装部署教程】
公众号回复【0014】获取【Nginx学习之路汇总】
公众号回复【0015】获取【Jenkins学习之路汇总】
公众号回复【10005】获取【adb工具刷抖音赚米】
热文推荐
Golang | Web开发之Gin框架快速入门基础实践
Go开发学习 | 如何快速读取json/yaml/ini等格式的配置文件使用示例
Go开发学习 | 如何使用Gomail.v2模块包发送邮箱验证码消息及附件学习记录
Go开发学习 | 如何使用日志记录模块包针对日志按天数、按大小分隔文件示例
开发基础 | Golang语言的RESTfulAPI接口设计规范快速入门
欢迎长按(扫描)二维码 ,获取更多渠道哟!
欢迎关注 【全栈工程师修炼指南】(^U^)ノ~YO
添加作者微信【weiyigeeker 】 一起学习交流吧!
关注回复【学习交流群】即可加入【安全运维沟通交流小群】
温馨提示: 由于作者水平有限,本章错漏缺点在所难免,希望读者批评指正,若有问题或建议请在文章末尾留下您宝贵的经验知识,或联系邮箱地址
master@weiyigeek.top 或 关注公众号 [全栈工程师修炼指南] 留言。
点个【赞 + 在看】吧!
点击【"阅读原文"】获取更多有趣的知识!