在golang中封装日志设置的正确模式

Vla*_*nko 3 go deferred-execution

当尝试将日志设置代码移动到单独的函数时,我遇到无法从main函数中隐藏目标文件对象.在下面的INCORRECT简化示例中,尝试通过单个函数调用设置日志写入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")
}
Run Code Online (Sandbox Code Playgroud)

显然是不起作用因为deferSetupLogging函数结束时关闭日志文件.

下面的一个工作示例添加了额外的代码,如果在较大的应用程序中重复作为模式,IMHO会失去一些清晰度:

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")
}
Run Code Online (Sandbox Code Playgroud)

有没有不同的方法将打开文件管理完全封装到一个函数中,但仍然很好地释放句柄?

Vla*_*nko 6

我现在已经成功地在多个项目中使用了以下方法大约一年.我们的想法是从设置调用中返回一个函数.结果函数包含销毁逻辑.这是一个例子:

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")
}
Run Code Online (Sandbox Code Playgroud)

它正在使用一个关闭清理逻辑的闭包.

使用这种方法的一个更精细的公共示例是在Viper代码中:这是从测试初始化​​器返回的,这里它用于封装清理逻辑和对象