Golang实践录:gin框架使用自定义日志模块
本文介绍在 Golang 的 gin 框架中使用自定义日志模块的一些方法。
背景
很早之前就实现并使用了自己封装的日志模块,但一直没有将gin框架内部的日志和日志模块结合。gin的日志都是在终端上打印的,排查问题不方便。趁五一假期,集中研究把此事了了。
实践
gin支持中间件,对于一些常用接口,也提供了自定义函数。本文主要是从这2方面着手。
据官方demo,在初始化时基本都使用如下语句初始化内部日志(Logger())和panic恢复功能(Recovery())。
router.Use(gin.Logger())
router.Use(gin.Recovery())
默认输出日志:
[GIN] 2024/05/08 - 09:09:13 | 200 | 1.082244ms | ::1 | GET "/info"
自定义Writer
gin对外提供了DefaultWriter,类型为io.Writer,默认输出终端,定义如下:
var DefaultWriter io.Writer = os.Stdout
因此可以修改该参数达到自定义输出日志的目的。代码如下:
f, _ := os.OpenFile(filepath.Join("./", "gin.log"), os.O_RDWR|os.O_APPEND|os.O_CREATE, 0666)
gin.DefaultWriter = io.MultiWriter(f)
上述代码在当前目录创建gin.log文件,将gin的日志输出到该文件,效果如下:
[GIN] 2024/05/08 - 09:10:54 | 200 | 844.357µs | ::1 | GET "/info"
评:自由度不够。
使用中间件
调用语句router.Use(gin.Logger())中的gin.Logger()实际可理解为中间件函数。函数定义:
func XXX() HandlerFunc {
...
}type HandlerFunc func(*Context)
自定义的中间件函数模板:
func XXXs() gin.HandlerFunc {return func(c *gin.Context) {
...// 下一处理c.Next()
...}
}
日志中间件实现如下:
func filterLogs() gin.HandlerFunc {return func(c *gin.Context) {// Start timerstart := time.Now()path := c.Request.URL.Path// Process requestc.Next()// Stop timerlatency := time.Now().Sub(start)clientIP := c.ClientIP()method := c.Request.MethodstatusCode := c.Writer.Status()klog.Printf("middleware | %3d | %13v | %15s | %-7s %#v\n", statusCode, latency,clientIP, method, path)}
}调用:
router.Use(filterLogs())
注:klog.Printf为自封装的日志模块输出函数。源码可参考klog项目。
效果如下:
[2024-05-08 09:44:34 515] [INFO] middleware | 404 | 738ns | ::1 | GET "/info"
评:参数gin框架的日志中间件实现简洁版本,自由度较大。可在此函数中再做其它处理。如统计某path的次数,等。
使用日志格式化回调函数
gin中自带了日志格式化的函数LoggerWithFormatter,该函数参数为函数,原型为type LogFormatter func(params LogFormatterParams) string。其本意是可以自定义输出的日志的字段内容,因此返回值为字符串,默认输出还是使用gin.DefaultWriter。
因此,可以在该函数中添加日志的打印,但不返回字符串,这样gin框架就不会输出日志了。代码如下:
router.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {klog.Printf("| %3d | %13v | %15s | %-7s %#v\n%s", param.StatusCode, param.Latency,param.ClientIP, param.Method, param.Path, param.ErrorMessage)return ""
}))
效果如下:
[2024-05-08 09:44:34 516] [INFO] | 200 | 1.580895ms | ::1 | GET "/info"
评:利用现有的中间件,自实现部分代码较简洁。可在此函数中再做其它处理。如统计某path的次数,等。
Recovery中间件实现
gin框架默认的Recovery处理函数gin.Recovery()。与上类似,输出信息也是使用内部日志模块的。为保存可能出现的panic信息,因此需要重新实现。由于原代码已经很完备,只是日志与需求不符,因此并无大改,具体代码如下:
// panic日志记录 替换gin的Recovery函数
func filterRecovery() 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}}}stack := com.GetStack(3)httpRequest, _ := httputil.DumpRequest(c.Request, false)headers := strings.Split(string(httpRequest), "\r\n")for idx, header := range headers {current := strings.Split(header, ":")if current[0] == "Authorization" {headers[idx] = current[0] + ": *"}}headersToStr := strings.Join(headers, "\r\n")if brokenPipe {klog.Printf("%s\n%s", err, headersToStr)} else {klog.Printf("[Recovery] panic recovered:\n%s\n%s", err, stack)}if brokenPipe {// If the connection is dead, we can't write a status to it.c.Error(err.(error)) // nolint: errcheckc.Abort()}}}()c.Next()}
}使用:
router.Use(filterRecovery())
小结
本文介绍的几种方法,基本上都可达到使用自定义日志的目的。笔者实际工程中使用中间件形式,可定制性较高。
附:gin框架日志源码跟踪
外部使用:router.Use(gin.Logger())
--> LoggerWithConfig--> 如未指定formatter,则用defaultLogFormatter--> 如未指定日志输出Write,则用DefaultWriter--> 记录无需要输出的path,存放于skip--> 有请求,遍历skip,如找不到,则组装LogFormatterParams,使用fmt.Fprint输出日志。
从上述流程可以看出,可以通过指定formatter在框架里内嵌自定义的函数,由于会调用fmt.Fprint,因此formatter不返回字符串。当然,可实现自己的中间件替换gin.Logger()。
相关文章:
Golang实践录:gin框架使用自定义日志模块
本文介绍在 Golang 的 gin 框架中使用自定义日志模块的一些方法。 背景 很早之前就实现并使用了自己封装的日志模块,但一直没有将gin框架内部的日志和日志模块结合。gin的日志都是在终端上打印的,排查问题不方便。趁五一假期,集中研究把此事…...
Django之配置数据库
一,创建项目 二,将项目的setting.py中的 DATABASES {default: {ENGINE: django.db.backends.sqlite3,NAME: BASE_DIR / db.sqlite3,} }替换成如下(以mysql为例) DATABASES {default: {ENGINE: django.db.backends.mysql,NAME: …...
Ajax 笔记02
01 jq中的ajax方法中的dataType属性 dataType属性的属性值有以下几种: xml 返回数据按照xml解析 json 返回的数据按照json代码解析 script 返回的数据按照js代码解析 text 把返回的数据按照普通文本解析 jsonp 跨域 json: javascript object notation(js对象简谱) json整体…...
【隧道篇 / WAN优化】(7.4) ❀ 03. WAN优化的原理 ❀ FortiGate 防火墙
【简介】相信对WAN优化感兴趣的人都会有疑问,WAN优化真的有作用吗?如果真的有作用,那是根据什么原理呢?让我们来更深入的了解一下。 客户端和服务器端 其实很多人在一开始看到WAN优化这个词,就自然的以为上网速度太慢&…...
网络爬虫概述与原理
网络爬虫概述与原理 网络爬虫简介狭义上理解功能上理解常见用途总结 网络爬虫分类通用网络爬虫聚焦网络爬虫增量网络爬虫深度网络爬虫 网络爬虫流程网络爬虫采集策略深度有限搜索策略广度优先搜索策略 网络爬虫简介 通过有效地获取网络资源的方式,便是网络爬虫。网…...
可视化实验三 Matplotlib库绘图及时变数据可视化
1.1 任务一 1.1.1 恢复默认配置 #绘图风格,恢复默认配置 plt.rcParams.update(plt.rcParamsDefault)#恢复默认配置 或者 plt.rcdefaults() 1.1.2 汉字和负号的设置 import matplotlib.pyplot as plt plt.rcParams["font.sans-serif"]"SimH…...
开启多线程下变量共享与私有问题
开启多线程下变量共享与私有问题 🌵ThreadLocal和Atomic是Java中用于多线程编程的两个重要工具。 ThreadLocal是一个线程局部变量,它为每个线程提供了独立的变量副本,确保每个线程都可以访问自己的变量副本而不会影响其他线程的变量。在多线…...
Qt模型视图代理之QTableView应用的简单介绍
往期回顾 Qt绘图与图形视图之绘制带三角形箭头的窗口的简单介绍-CSDN博客 Qt绘图与图形视图之Graphics View坐标系的简单介绍-CSDN博客 Qt模型视图代理之MVD(模型-视图-代理)概念的简单介绍-CSDN博客 Qt模型视图代理之QTableView应用的简单介绍 一、最终效果 二、设计思路 这里…...
第七届精武杯部分wp
第一部分:计算机和手机取证 1.请综合分析计算机和手机检材,计算机最近一次登录的账户名是 答案:admin 创建虚拟机时直接给出了用户名 2. 请综合分析计算机和手机检材,计算机最近一次插入的USB存储设备串号是 答案:…...
3.2Java全栈开发前端+后端(全栈工程师进阶之路)-前端框架VUE3框架-企业级应用- Vuex
Vuex简介 Vuex概述 Vuex是一个专门为Vue.js应用程序开发的状态管理模式, 它采用集中式存储管理所有组件的公共状态, 并以相应的规 则保证状态以一种可预测的方式发生变化. 试想这样的场景, 比如一个Vue的根实例下面有一个根组件名为App.vue, 它下面有两个子组件A.vue和B.vu…...
整合 Java, Python 和 Scrapy 爬虫以传递和使用参数
这篇博客将详细说明如何从 Java 应用程序调用一个 Python 脚本,并在此过程中传递参数给一个 Scrapy 爬虫。最终目标是让 Java 控制爬虫的行为,如爬取数量和特定的运行参数。 一、Scrapy 爬虫的修改 首先,我们需要确保 Scrapy 爬虫能接收从命…...
Android 蓝牙实战——蓝牙音乐播放进度(二十)
对于蓝牙音乐的开发来说,播放进度是一个比较重要的数据参数,这里我们就来分析一下蓝牙音乐播放进度的相关回调。 一、回调流程 1、AvrcpControllerService 源码位置:/packages/apps/Bluetooth/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java /…...
SQL注入实例(sqli-labs/less-1)
初始网页 从网页可知传递的参数名为 id,并且为数字类型 1、得知数据表有多少列 1.1 使用联合查询查找列数(效率低) http://localhost/sqli-labs-master/Less-1/?id1 union select 1,2 -- 1.2 使用order by查找列数(效率高&…...
Python中tkinter编程入门3
在使用tkinter创建了窗口之后,可以将一些控件“放置”到窗口中。这些控件包括标签、按键以及输入框等。 1 在窗口中“放置”标签 在窗口中“放置”标签主要有两个步骤,一是创建标签控件,二是将创建好的标签“放置”到窗口上。 1.1 创建标签…...
XMind 2023 v23.05.2660软件安装教程(附软件下载地址)
软件简介: 软件【下载地址】获取方式见文末。注:推荐使用,更贴合此安装方法! XMind 2023 v23.05.2660被视为顶尖思维导图软件,其界面简洁清爽,功能布局直观简单,摒弃繁复不实。尽管体积小巧&a…...
docker compose kafka集群部署
kafka集群部署 目录 部署zookeeper准备工作2、部署kafka准备工作3、编辑docker-compose.yml文件4、启动服务5、测试kafka6、web监控管理 部署zookeeper准备工作 mkdir data/zookeeper-{1,2,3}/{data,datalog,logs,conf} -p cat >data/zookeeper-1/conf/zoo.cfg<<EOF…...
最新版在线客服系统源码
源码介绍 首发最新在线客服系统源码,优化更好并且重构源码布局UI 性能不吃cpu并发快,普通1H2G都能带动最新版只要是服务器都能带动 搭建即可使用,操作简单,易懂 修复了老版本bug 内附有搭建教程 gofly.v1kf.com 运行环境 Nginx 1.20 MySQL 5.7 演示截图...
【比邻智选】MR880A模组
🚀高性价比,5G/4G双模,稳定可靠 🌐功能丰富,5G特性一应俱全 🧩多封装兼容,适配性强,灵活升级智能设备...
超大文件去除重复数据
背景 一个超大200万行文件 第一列是文件名 第二列是文本 第一列有重复的文件名 如何删除重复的文件名和对应的文本 awk ‘!seen[$1]’ 使用一些命令行工具来处理大文件,如awk、sed、grep等。 使用awk命令来去除重复行: bash awk !seen[$1] your_file.…...
ICode国际青少年编程竞赛- Python-4级训练场-列表综合练习
ICode国际青少年编程竞赛- Python-4级训练场-列表综合练习 1、 Flyer[3].step(1) Flyer[7].step(2) Flyer[11].step(1) for i in range(4):Flyer[i * 2].step(1) Flyer[8].step(3)for i in range(3):Dev.turnRight()Dev.step(-5)2、 for i in range(5):Flyer[i5].step(Flyer[…...
不用下载IDE!浏览器直接练Python二级考题的宝藏网站测评
浏览器直通Python二级考场:零配置备考实战指南 距离全国计算机二级Python考试还有30天,小张的笔记本电脑却突然罢工。维修店报价让他望而却步,而图书馆公共电脑禁止安装软件的规定更让他雪上加霜。这种困境并非个例——据教育技术协会2024年…...
Agent 语音交互如何更稳、更快?一次高并发消息链路优化实践
作者:雀贤、文婷、复礼、稚柳 随着大语言模型(LLM)、语音识别(ASR)、语音合成(TTS)等能力逐步成熟,AI Agent 开始从文本交互走向语音交互,典型场景包括 AI 教师、AI 情感…...
能耗效率比拼:百川2-13B量化版在OpenClaw长时间任务中的表现
能耗效率比拼:百川2-13B量化版在OpenClaw长时间任务中的表现 1. 测试背景与目标 最近在探索如何用OpenClaw实现个人工作流的自动化时,遇到一个现实问题:当需要长时间运行自动化任务时,本地设备的能耗和稳定性会成为瓶颈。我决定…...
Leptin30;YQQVLTSLPSQNVLQIANDLENLRDLLHLL (mouse)
一、基本信息名称: Leptin30(小鼠源瘦素功能片段肽)单字母序列: YQQVLTSLPSQNVLQIANDLENLRDLLHLL三字母序列: Tyr-Gln-Gln-Val-Leu-Thr-Ser-Leu-Pro-Ser-Gln-Asn-Val-Leu-Gln-Ile-Ala-Asn-Asp-Leu-Glu-Asn-Leu-Arg-Asp…...
SpringBoot 接口全维度性能优化指南
文章目录: 前言 一、背景 1.1 为什么必须做 SpringBoot 接口优化? 1.2 接口优化的核心目标 1.3 本文适用范围 二、核心原理 2.1 接口请求全流程(瓶颈定位核心) 2.2 核心优化原理总览 2.3 优化优先级(生产环境…...
南北阁 4.1-3B 开源镜像实战:Streamlit轻量化UI+CoT折叠展示一文详解
南北阁 4.1-3B 开源镜像实战:Streamlit轻量化UICoT折叠展示一文详解 想快速体验一个能在本地流畅运行、还能“看见”模型思考过程的智能对话工具吗?今天要介绍的,就是基于南北阁(Nanbeige)4.1-3B模型打造的轻量化流式…...
2026大模型应用爆发:504个案例揭示行业变革新机遇!
2025年,大模型技术如同一颗璀璨的新星,在各行各业绽放出耀眼光芒。从互联网、金融到能源制造、交通运输,再到医疗、教育、公共服务,展现出前所未有的活力和潜力。 大模型的应用不仅改变了企业的运营模式,提升了企业的竞…...
企业邮箱安全必看:SPF、DKIM、DMARC 三件套配置实战(附常见错误排查)
企业邮箱安全必看:SPF、DKIM、DMARC 三件套配置实战(附常见错误排查) 当一封伪造CEO签名的钓鱼邮件成功进入财务部门邮箱时,企业面临的不仅是数据泄露风险——根据Verizon《2023年数据泄露调查报告》,83%的商务邮件入侵…...
Mojo+Python混合项目部署失败全记录(含完整错误日志溯源与跨运行时调试手册)
第一章:MojoPython混合项目部署失败全记录(含完整错误日志溯源与跨运行时调试手册)在将 Mojo 模块嵌入 Python 3.11 环境的 CI/CD 流水线中,首次构建即触发运行时崩溃。核心现象为 mojo_runtime_init() 在 Python 进程内调用后立即…...
SAS(Serial Attached SCSI)在企业级存储中的核心设计与实战解析
1. SAS技术在企业级存储中的核心价值 如果你拆开过企业级存储设备,大概率会看到那些带着蓝色或黑色连接器的硬盘背板——这就是SAS技术的战场。作为存储架构师,我经手过的全闪存阵列和磁盘柜里,90%的核心连接都依赖SAS协议。和消费级SATA相比…...
