当试图将日志设置代码移动到一个单独的函数中时,我遇到了无法从 main
函数中隐藏目标文件对象的问题。在下面的不正确简化示例中,尝试通过单个函数调用将日志写入 Stderr 和文件:
package main
import (
"io"
"log"
"os"
)
func SetupLogging() {
logFile, err := os.OpenFile("test.log", os.O_APPEND|os.O_CREATE, 0666)
if err != nil {
log.Panicln(err)
}
defer logFile.Close()
log.SetOutput(io.MultiWriter(os.Stderr, logFile))
}
func main() {
SetupLogging()
log.Println("Test message")
}
显然这是行不通的,因为 defer
会在 SetupLogging
函数结束时关闭日志文件。
下面的工作示例添加了额外的代码,恕我直言,如果在更大的应用程序中作为模式重复,则会失去一些清晰度:
package main
import (
"io"
"log"
"os"
)
func SetupLogging() *os.File {
logFile, err := os.OpenFile("test.log", os.O_APPEND|os.O_CREATE, 0666)
if err != nil {
log.Panicln(err)
}
log.SetOutput(io.MultiWriter(os.Stderr, logFile))
return logFile
}
func main() {
logf := SetupLogging()
defer logf.Close()
log.Println("Test message")
}
是否有不同的方法可以将打开的文件管理完全封装到一个函数中,同时又能很好地释放句柄?
最佳答案
我现在已经在多个项目中成功使用以下方法大约一年了。这个想法是从设置调用返回一个函数。结果函数包含销毁逻辑。这是一个例子:
package main
import (
"fmt"
"io"
"log"
"os"
)
func LogSetupAndDestruct() func() {
logFile, err := os.OpenFile("test.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666)
if err != nil {
log.Panicln(err)
}
log.SetOutput(io.MultiWriter(os.Stderr, logFile))
return func() {
e := logFile.Close()
if e != nil {
fmt.Fprintf(os.Stderr, "Problem closing the log file: %s\n", e)
}
}
}
func main() {
defer LogSetupAndDestruct()()
log.Println("Test message")
}
它在被延迟的清理逻辑周围使用闭包。
在 Viper 代码中有一个使用这种方法的更详细的公开示例:here is the return from a test initializer , 和 here it is used to encapsulate the cleanup logic and objects
关于go - 在 golang 中封装日志设置的正确模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25342806/