Gin框架运行时动态切换日志写入器问题求助
问题原因分析
- 核心问题:你在调用
gin.Default()创建路由实例之后才修改gin.DefaultWriter,而gin.Default()内部初始化Logger时已经绑定了当时的全局gin.DefaultWriter,后续修改全局变量不会影响已创建的路由实例的日志输出逻辑。 - 次要问题:每次生成新日志文件时,旧的文件句柄未关闭,会导致系统文件资源泄漏。
解决方案
方案一:调整初始化顺序(简单直接)
先确保拿到第一个日志文件的Writer,再初始化Gin路由,让路由创建时直接使用正确的gin.DefaultWriter:
func main() { models.InitDB() // 初始化日志通道并启动定时任务 logChannel := make(chan io.Writer) defer close(logChannel) go logger.InitLogTimer(logChannel) // 等待第一个日志Writer就绪,设置全局默认Writer firstWriter := <-logChannel gin.DefaultWriter = io.MultiWriter(firstWriter, colorable.NewColorableStdout()) // 启动goroutine监听后续日志文件更新 go func() { for c := range logChannel { gin.DefaultWriter = io.MultiWriter(c, colorable.NewColorableStdout()) } }() // 现在创建路由,会使用已更新的全局Writer route := gin.Default() route.Static("/assets", "../../view") route.LoadHTMLGlob("../../view/html/**/**/*") routes.RegisterUserPublicRoutes(route) routes.RegisterUserProtectedRoutes(route) route.Run(":8080") }
方案二:自定义动态Writer(更灵活可控)
不依赖全局gin.DefaultWriter,直接给路由绑定一个能动态切换输出目标的Writer,避免全局变量的局限性:
// main.go 中定义动态Writer结构 type dynamicLogWriter struct { currentWriter io.Writer } func (w *dynamicLogWriter) Write(p []byte) (n int, err error) { return w.currentWriter.Write(p) } func main() { models.InitDB() // 初始化默认Writer为控制台输出 dynamicWriter := &dynamicLogWriter{ currentWriter: colorable.NewColorableStdout(), } // 初始化日志通道并启动定时任务 logChannel := make(chan io.Writer) defer close(logChannel) go logger.InitLogTimer(logChannel) // 监听日志文件更新,动态切换Writer go func() { for fileWriter := range logChannel { dynamicWriter.currentWriter = io.MultiWriter(fileWriter, colorable.NewColorableStdout()) } }() // 手动创建路由并绑定自定义Logger route := gin.New() route.Use(gin.LoggerWithWriter(dynamicWriter)) route.Use(gin.Recovery()) route.Static("/assets", "../../view") route.LoadHTMLGlob("../../view/html/**/**/*") routes.RegisterUserPublicRoutes(route) routes.RegisterUserProtectedRoutes(route) route.Run(":8080") }
修复文件句柄泄漏问题
修改logger包中的代码,每次生成新日志文件时关闭旧文件:
var oldLogFile *os.File func generateLogFile(write chan io.Writer) { // 关闭旧日志文件,释放资源 if oldLogFile != nil { _ = oldLogFile.Close() } dest := fmt.Sprintf("../../cmd/main/log/%v", time.Now().UnixMilli()) if err := os.MkdirAll(dest, os.ModePerm); err != nil { log.Fatal("error while creating log directory: ", err.Error()) } filename := fmt.Sprintf("%v/%v.txt", dest, time.Now().Format("01_02_2006")) logFile, err := os.OpenFile(filename, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666) if err != nil { log.Fatal("error while creating log file: ", err.Error()) } oldLogFile = logFile write <- logFile }
内容的提问来源于stack exchange,提问作者Mohammad Derakhshan
相关产品推荐
相关产品推荐

