如何初始化一个 zap 记录器并在其他 Go 文件中重用它?

ale*_*avi 14 logging go go-zap

我正在尝试从漂亮的 Logrus(对调试非常有帮助)迁移我的应用程序并引入 Uber 日志框架 Zap。

使用 Logrus,我只能初始化记录器一次并从其他 Go 文件中重用它,例如:

package main
import(
    // Print filename on log
    filename "github.com/onrik/logrus/filename"
    // Very nice log library
    log "github.com/sirupsen/logrus"
)

func main(){

// ==== SET LOGGING
    Formatter := new(log.TextFormatter)
    Formatter.TimestampFormat = "Jan _2 15:04:05.000000000"
    Formatter.FullTimestamp = true
    Formatter.ForceColors = true
    log.AddHook(filename.NewHook()) // Print filename + line at every log
    log.SetFormatter(Formatter)

}
Run Code Online (Sandbox Code Playgroud)

从其他 Go 文件中,我可以重用该记录器而无需任何其他初始化:

// VerifyCommandLineInput is delegated to manage the inputer parameter provide with the input flag from command line
func VerifyCommandLineInput() datastructures.Configuration {
    log.Debug("VerifyCommandLineInput | Init a new configuration from the conf file")
    c := flag.String("config", "./conf/test.json", "Specify the configuration file.")
    flag.Parse()
    if strings.Compare(*c, "") == 0 {
        log.Fatal("VerifyCommandLineInput | Call the tool using --config conf/config.json")
    }
    file, err := os.Open(*c)
    if err != nil {
        log.Fatal("VerifyCommandLineInput | can't open config file: ", err)
    }
    defer file.Close()
    decoder := json.NewDecoder(file)
    cfg := datastructures.Configuration{}
    err = decoder.Decode(&cfg)
    if err != nil {
        log.Fatal("VerifyCommandLineInput | can't decode config JSON: ", err)
    }
    log.Debug("VerifyCommandLineInput | Conf loaded -> ", cfg)

    return cfg
}

Run Code Online (Sandbox Code Playgroud)

我的问题是:使用 Zap 日志框架,如何在主函数中初始化日志并使用其他 Go 文件中的记录器?

小智 14

我认为避免替换默认的 go 记录器是个好主意,正如@ammills01所提到的提到的那样。\n我的方法是为 zap logger\xe2\x80\x99s 配置和初始化创建一个单独的包。它还提供了包装函数实现,在将 zap 更改为另一个日志记录工具时非常有用。

\n
package logger\nimport (\n    "go.uber.org/zap"\n    "go.uber.org/zap/zapcore"\n)\n\nvar zapLog *zap.Logger\n\nfunc init() {\n    var err error\n    config := zap.NewProductionConfig()\n    enccoderConfig := zap.NewProductionEncoderConfig()\n    zapcore.TimeEncoderOfLayout("Jan _2 15:04:05.000000000")\n    enccoderConfig.StacktraceKey = "" // to hide stacktrace info\n    config.EncoderConfig = enccoderConfig\n\n    zapLog, err = config.Build(zap.AddCallerSkip(1))\n    if err != nil {\n        panic(err)\n    }\n}\n\nfunc Info(message string, fields ...zap.Field) {\n    zapLog.Info(message, fields...)\n}\n\nfunc Debug(message string, fields ...zap.Field) {\n    zapLog.Debug(message, fields...)\n}\n\nfunc Error(message string, fields ...zap.Field) {\n    zapLog.Error(message, fields...)\n}\n\nfunc Fatal(message string, fields ...zap.Field) {\n    zapLog.Fatal(message, fields...)\n}\n
Run Code Online (Sandbox Code Playgroud)\n

所以您的项目中的任何其他文件将如下所示

\n
import "github.com/[your_repo_path_here]/logger"\n\nfunc SomeFunc() datastructures.Configuration {\n    logger.Debug("VerifyCommandLineInput | Init a new configuration from the conf file")\n    c := flag.String("config", "./conf/test.json", "Specify the configuration file.")\n    flag.Parse()if strings.Compare(*c, "") == 0 {\n        logger.Fatal("VerifyCommandLineInput | Call the tool using --config conf/config.json")\n    }\n    file, err := os.Open(*c)\n    if err != nil {\n        logger.Fatal("VerifyCommandLineInput | can\'t open config file: ", err)\n    }\n    defer file.Close()\n    decoder := json.NewDecoder(file)\n    cfg := datastructures.Configuration{}\n    err = decoder.Decode(&cfg)\n    if err != nil {\n        logger.Fatal("VerifyCommandLineInput | can\'t decode config JSON: ", err)\n    }\n    logger.Debug("VerifyCommandLineInput | Conf loaded -> ", cfg)\n\n    return cfg\n}\n
Run Code Online (Sandbox Code Playgroud)\n


Mik*_*ail 7

您可以在主函数中设置记录器并调用https://godoc.org/go.uber.org/zap#ReplaceGlobals将其用作默认全局记录器。

  • 我认为这个主题更复杂是公平的,并且上面提到的注释并不是不这样做的明确原因。事实上,将 zap 与像 cobra 这样的框架结合起来并传递所有内容是不切实际的。 (5认同)
  • Github 上的常见问题解答特别指出,您应该尽可能避免使用全局变量。 (2认同)

amm*_*s01 7

用 zaps 的实现替换默认的 go Global 记录器是可能的,但不鼓励。

根据他们的常见问题解答

为什么包括包全局记录器?由于许多其他日志包包含全局记录器,因此许多应用程序并未设计为接受记录器作为显式参数。更改函数签名通常是一个重大更改,因此 zap 包含全局记录器以简化迁移。

尽可能避免它们。

根据您的需要,您可以在 main 中创建一个记录器并将其传递或在每个包中创建一个新的记录器。我选择在 main 中创建一个并传递它,因为我使用的是 Atomic 记录器,它允许我在我的应用程序通过 API 调用运行时更改日志级别。使用 DI 和合并代码的历史悠久,一般来说确实感觉像代码异味,但显然 zap 如何在单例或全局上传递它的性能要高得多。

  • 完全同意:“感觉像代码味道”。 (3认同)