如何初始化一个 zap 记录器并在其他 Go 文件中重用它?
How to initialize a zap logger once and reuse it in other Go files?
我正在尝试从漂亮的 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)
}
从其他 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
}
我的问题是:使用 Zap 日志框架,如何在 main 函数中初始化日志并从其他 Go 文件使用该记录器?
您可以在主函数中设置记录器并调用 zap.ReplaceGlobals
将其用作默认的全局记录器。
ReplaceGlobals replaces the global Logger and SugaredLogger, and returns a function to restore the original values. It's safe for concurrent use.
用 zaps 的实现替换默认的 go Global logger 是可能的,但不鼓励。
根据他们的 FAQ
Why include package-global loggers? Since so many other logging
packages include a global logger, many applications aren't designed to
accept loggers as explicit parameters. Changing function signatures is
often a breaking change, so zap includes global loggers to simplify
migration.
Avoid them where possible.
根据您的需要,您可以在 main 中创建一个记录器并传递它,或者在每个包中创建一个新的记录器。我选择在 main 中创建一个并传递它,因为我使用的是 Atomic 记录器,它允许我在我的应用程序处于 运行 时通过 API 调用更改日志级别。由于使用 DI 和整合代码的历史悠久,它确实感觉像是代码的味道,但显然,对于 zap 如何通过单例或全局传递它来说,它的性能要高得多。
@alessiosavi 的回答最初发布到问题中。
初始化一个新日志并设置为@Mikhail 指出的全局日志。
package main
import(
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
func initZapLog() *zap.Logger {
config := zap.NewDevelopmentConfig()
config.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
config.EncoderConfig.TimeKey = "timestamp"
config.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
logger, _ := config.Build()
return logger
}
func main() {
loggerMgr := initZapLog()
zap.ReplaceGlobals(loggerMgr)
defer loggerMgr.Sync() // flushes buffer, if any
logger := loggerMgr.Sugar()
logger.Debug("START!")
db2.GetToken(`alessio`, `savi`, `pass`)
datastructure.LoadConfiguration()
}
你可以在其他 Go 文件中使用记录器:
func GetToken(url, user, pass string) string {
var User datastructure.User
var data string
var jsonData []byte
User.User = user
User.Pass = pass
jsonData, err := json.Marshal(User)
if err != nil {
zap.S().Errorw("Error during marshalling...", err)
return ""
}
data = string(jsonData)
zap.S().Info("Data encoded => ", data)
return ""
}
我想避免替换默认的 go 记录器是个好主意,正如 @ammills01 所提到的那样。
我的方法是为 zap logger 的配置和初始化创建一个单独的包。它还提供了包装函数实现,这在将 zap 更改为另一个日志记录工具时非常有用。
package logger
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
var zapLog *zap.Logger
func init() {
var err error
config := zap.NewProductionConfig()
enccoderConfig := zap.NewProductionEncoderConfig()
zapcore.TimeEncoderOfLayout("Jan _2 15:04:05.000000000")
enccoderConfig.StacktraceKey = "" // to hide stacktrace info
config.EncoderConfig = enccoderConfig
zapLog, err = config.Build(zap.AddCallerSkip(1))
if err != nil {
panic(err)
}
}
func Info(message string, fields ...zap.Field) {
zapLog.Info(message, fields...)
}
func Debug(message string, fields ...zap.Field) {
zapLog.Debug(message, fields...)
}
func Error(message string, fields ...zap.Field) {
zapLog.Error(message, fields...)
}
func Fatal(message string, fields ...zap.Field) {
zapLog.Fatal(message, fields...)
}
因此您项目中的任何其他文件将如下所示
import "github.com/[your_repo_path_here]/logger"
func SomeFunc() datastructures.Configuration {
logger.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 {
logger.Fatal("VerifyCommandLineInput | Call the tool using --config conf/config.json")
}
file, err := os.Open(*c)
if err != nil {
logger.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 {
logger.Fatal("VerifyCommandLineInput | can't decode config JSON: ", err)
}
logger.Debug("VerifyCommandLineInput | Conf loaded -> ", cfg)
return cfg
}
我正在尝试从漂亮的 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)
}
从其他 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
}
我的问题是:使用 Zap 日志框架,如何在 main 函数中初始化日志并从其他 Go 文件使用该记录器?
您可以在主函数中设置记录器并调用 zap.ReplaceGlobals
将其用作默认的全局记录器。
ReplaceGlobals replaces the global Logger and SugaredLogger, and returns a function to restore the original values. It's safe for concurrent use.
用 zaps 的实现替换默认的 go Global logger 是可能的,但不鼓励。
根据他们的 FAQ
Why include package-global loggers? Since so many other logging packages include a global logger, many applications aren't designed to accept loggers as explicit parameters. Changing function signatures is often a breaking change, so zap includes global loggers to simplify migration.
Avoid them where possible.
根据您的需要,您可以在 main 中创建一个记录器并传递它,或者在每个包中创建一个新的记录器。我选择在 main 中创建一个并传递它,因为我使用的是 Atomic 记录器,它允许我在我的应用程序处于 运行 时通过 API 调用更改日志级别。由于使用 DI 和整合代码的历史悠久,它确实感觉像是代码的味道,但显然,对于 zap 如何通过单例或全局传递它来说,它的性能要高得多。
@alessiosavi 的回答最初发布到问题中。
初始化一个新日志并设置为@Mikhail 指出的全局日志。
package main
import(
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
func initZapLog() *zap.Logger {
config := zap.NewDevelopmentConfig()
config.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
config.EncoderConfig.TimeKey = "timestamp"
config.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
logger, _ := config.Build()
return logger
}
func main() {
loggerMgr := initZapLog()
zap.ReplaceGlobals(loggerMgr)
defer loggerMgr.Sync() // flushes buffer, if any
logger := loggerMgr.Sugar()
logger.Debug("START!")
db2.GetToken(`alessio`, `savi`, `pass`)
datastructure.LoadConfiguration()
}
你可以在其他 Go 文件中使用记录器:
func GetToken(url, user, pass string) string {
var User datastructure.User
var data string
var jsonData []byte
User.User = user
User.Pass = pass
jsonData, err := json.Marshal(User)
if err != nil {
zap.S().Errorw("Error during marshalling...", err)
return ""
}
data = string(jsonData)
zap.S().Info("Data encoded => ", data)
return ""
}
我想避免替换默认的 go 记录器是个好主意,正如 @ammills01 所提到的那样。 我的方法是为 zap logger 的配置和初始化创建一个单独的包。它还提供了包装函数实现,这在将 zap 更改为另一个日志记录工具时非常有用。
package logger
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
var zapLog *zap.Logger
func init() {
var err error
config := zap.NewProductionConfig()
enccoderConfig := zap.NewProductionEncoderConfig()
zapcore.TimeEncoderOfLayout("Jan _2 15:04:05.000000000")
enccoderConfig.StacktraceKey = "" // to hide stacktrace info
config.EncoderConfig = enccoderConfig
zapLog, err = config.Build(zap.AddCallerSkip(1))
if err != nil {
panic(err)
}
}
func Info(message string, fields ...zap.Field) {
zapLog.Info(message, fields...)
}
func Debug(message string, fields ...zap.Field) {
zapLog.Debug(message, fields...)
}
func Error(message string, fields ...zap.Field) {
zapLog.Error(message, fields...)
}
func Fatal(message string, fields ...zap.Field) {
zapLog.Fatal(message, fields...)
}
因此您项目中的任何其他文件将如下所示
import "github.com/[your_repo_path_here]/logger"
func SomeFunc() datastructures.Configuration {
logger.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 {
logger.Fatal("VerifyCommandLineInput | Call the tool using --config conf/config.json")
}
file, err := os.Open(*c)
if err != nil {
logger.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 {
logger.Fatal("VerifyCommandLineInput | can't decode config JSON: ", err)
}
logger.Debug("VerifyCommandLineInput | Conf loaded -> ", cfg)
return cfg
}