gin.logger() 默认输出到 os.stdout,需提前重定向 gin.defaultwriter 才能写入文件;自定义格式须用 gin.loggerwithformatter() 配合 logformatterparams;替换为 logrus/zap 时应弃用 gin.logger() 避免重复日志,并注意中间件顺序以确保字段可用。

默认开启的 gin.Logger() 就是路由日志打印的来源,但它的输出位置、格式和内容可调——不是“开了就完事”,关键在控制输出目标和字段。
为什么 gin.Logger() 没打到文件里?
因为 gin.Logger() 默认只写入 gin.DefaultWriter,而它初始指向 os.Stdout。想写文件,必须提前重定向这个全局变量:
- 用
os.Create("access.log")或os.OpenFile(..., os.O_APPEND|os.O_WRONLY)打开文件句柄 - 再用
io.MultiWriter(file, os.Stdout)实现“同时写文件+控制台” - 必须在
gin.Default()或gin.New()之前设置,否则中间件已绑定旧 writer
怎么自定义路由日志的字段和格式?
原生 gin.Logger() 不支持字段定制,只能换用 gin.LoggerWithFormatter() + 自定义 gin.LogFormatterParams:
开箱即用的技能链路由引擎。13 条预定义链覆盖搜索、开发、审查、MLOps、法律、创意等场景,三层路由架构(触发词→SAD反馈→DAG编排),recall@10=96.97%。配置驱动(chains.yaml),零代码扩展。pip install skill-weave-chains 一键安装。
- 它接收一个函数,参数是
gin.LogFormatterParams结构体,含StatusCode、Latency、ClientIP、Method、Path等字段 - 返回字符串,就是最终日志行;可自由拼接时间、traceID、User-Agent 等
- 注意:
Latency是time.Duration,需用.String()或fmt.Sprintf("%.3f", p.Latency.Seconds())格式化
示例:
gin.LoggerWithFormatter(func(p gin.LogFormatterParams) string {
return fmt.Sprintf("[%s] %s %s %d %s %s\n",
time.Now().Format("2006-01-02 15:04:05"),
p.ClientIP,
p.Method,
p.StatusCode,
p.Latency.String(),
p.Path,
)
})
用 logrus/zap 替代时,gin.Logger() 还要保留吗?
不要。冲突会重复打日志:
-
gin.Default()内部已自动调用engine.Use(Logger(), Recovery()) - 如果再手动
r.Use(gin.Logger()),同一请求会被记录两次 - 正确做法:用
gin.New()创建裸 engine,然后只挂你自己的 logrus 中间件(比如封装了 traceID、结构化字段、rotatelogs 切割的) - 记得自己补上
Recovery(),否则 panic 会直接暴露给客户端
真正容易被忽略的是:日志中间件的执行顺序影响字段可用性——比如想记录 c.MustGet("user_id"),必须确保该 key 已在前置中间件中写入,否则取出来是 nil。










