golang使用Zap日志库
1. 为什么使用zap
因为它很快,而且我写不出比他更快的日志库😭
当然他还有其他优点,比如:它同时提供了结构化日志记录和printf风格的日志记录
2. 安装zap
go get -u go.uber.org/zap
3. 配置zap
zap提供两种日志记录器,如下表
名称 | 优点 | 缺点 |
---|---|---|
Sugared Logger | 支持结构化和printf风格的日志记录 | 较Logger慢 |
Logger | 较Sugared Logger快 | 只支持强类型的结构化日志记录 |
Logger
- 创建Logger
- zap.NewProduction()
- zap.NewDevelopment()
- zap.Example()
- 上述函数均可创建Logger只是输出信息不同
- 默认情况下日志会打印到控制台
举个例子
package main
import "go.uber.org/zap"
var logger *zap.Logger
func ProductionLogger(){
logger,_ = zap.NewProduction()
logger.Info("log info")
}
func DevelopmentLogger(){
logger,_ = zap.NewDevelopment()
logger.Info("log info")
}
func ExampleLogger(){
logger = zap.NewExample()
logger.Info("log info")
}
func main() {
ProductionLogger()
DevelopmentLogger()
ExampleLogger()
}
运行结果
{"level":"info","ts":1594976812.8990078,"caller":"go_zap/main.go:9","msg":"log info"}
2020-07-17T17:06:52.899+0800 INFO go_zap/main.go:14 log info
{"level":"info","msg":"log info"}
除DevelopmentLogger之外其余都是json格式输出
Sugared Logger
- 创建Sugared Logger
- 由Logger调用Sugar()方法获得
- 支持printf风格输出日志
举个例子
package main
import "go.uber.org/zap"
var sugarLogger *zap.SugaredLogger
func ProductionLogger(){
logger,_ := zap.NewProduction()
sugarLogger = logger.Sugar()
sugarLogger.Infof("sugar loger %s", "yes!")
}
func DevelopmentLogger(){
logger,_ := zap.NewDevelopment()
sugarLogger = logger.Sugar()
sugarLogger.Infof("sugar loger %s", "yes!")
}
func ExampleLogger(){
logger := zap.NewExample()
sugarLogger = logger.Sugar()
sugarLogger.Infof("sugar loger %s", "yes!")
}
func main() {
ProductionLogger()
DevelopmentLogger()
ExampleLogger()
}
运行结果
{"level":"info","ts":1594977351.4368348,"caller":"go_zap/main.go:10","msg":"sugar loger yes!"}
2020-07-17T17:15:51.436+0800 INFO go_zap/main.go:16 sugar loger yes!
{"level":"info","msg":"sugar loger yes!"}
输出结果和Logger类似,但是sugarLogger支持printf
4. 定制Logger
上面所介绍到的三个创建Logger的方法包含了一些预置的配置,如果我们想要完全自定义,那我们就需要自己写好自己需要的配置。
这些配置将被赋值给zap.Config
结构体,然后这个结构体对象调用Build
方法构造Logger,大概就像这样
config := zap.Config{
...
}
log, err := config.Build()
zap.Config
type Config struct {
// Level is the minimum enabled logging level. Note that this is a dynamic
// level, so calling Config.Level.SetLevel will atomically change the log
// level of all loggers descended from this config.
Level AtomicLevel `json:"level" yaml:"level"`
// Development puts the logger in development mode, which changes the
// behavior of DPanicLevel and takes stacktraces more liberally.
Development bool `json:"development" yaml:"development"`
// DisableCaller stops annotating logs with the calling function's file
// name and line number. By default, all logs are annotated.
DisableCaller bool `json:"disableCaller" yaml:"disableCaller"`
// DisableStacktrace completely disables automatic stacktrace capturing. By
// default, stacktraces are captured for WarnLevel and above logs in
// development and ErrorLevel and above in production.
DisableStacktrace bool `json:"disableStacktrace" yaml:"disableStacktrace"`
// Sampling sets a sampling policy. A nil SamplingConfig disables sampling.
Sampling *SamplingConfig `json:"sampling" yaml:"sampling"`
// Encoding sets the logger's encoding. Valid values are "json" and
// "console", as well as any third-party encodings registered via
// RegisterEncoder.
Encoding string `json:"encoding" yaml:"encoding"`
// EncoderConfig sets options for the chosen encoder. See
// zapcore.EncoderConfig for details.
EncoderConfig zapcore.EncoderConfig `json:"encoderConfig" yaml:"encoderConfig"`
// OutputPaths is a list of URLs or file paths to write logging output to.
// See Open for details.
OutputPaths []string `json:"outputPaths" yaml:"outputPaths"`
// ErrorOutputPaths is a list of URLs to write internal logger errors to.
// The default is standard error.
//
// Note that this setting only affects internal errors; for sample code that
// sends error-level logs to a different location from info- and debug-level
// logs, see the package-level AdvancedConfiguration example.
ErrorOutputPaths []string `json:"errorOutputPaths" yaml:"errorOutputPaths"`
// InitialFields is a collection of fields to add to the root logger.
InitialFields map[string]interface{} `json:"initialFields" yaml:"initialFields"`
}
我们需要特别注意的是EncoderConfig
这个字段,它定义了我们输出的格式,根据他的提示我们来看看zapcore.EncoderConfig
结构体
zapcore.EncoderConfig
// An EncoderConfig allows users to configure the concrete encoders supplied by
// zapcore.
type EncoderConfig struct {
// Set the keys used for each log entry. If any key is empty, that portion
// of the entry is omitted.
MessageKey string `json:"messageKey" yaml:"messageKey"`
LevelKey string `json:"levelKey" yaml:"levelKey"`
TimeKey string `json:"timeKey" yaml:"timeKey"`
NameKey string `json:"nameKey" yaml:"nameKey"`
CallerKey string `json:"callerKey" yaml:"callerKey"`
StacktraceKey string `json:"stacktraceKey" yaml:"stacktraceKey"`
LineEnding string `json:"lineEnding" yaml:"lineEnding"`
// Configure the primitive representations of common complex types. For
// example, some users may want all time.Times serialized as floating-point
// seconds since epoch, while others may prefer ISO8601 strings.
EncodeLevel LevelEncoder `json:"levelEncoder" yaml:"levelEncoder"`
EncodeTime TimeEncoder `json:"timeEncoder" yaml:"timeEncoder"`
EncodeDuration DurationEncoder `json:"durationEncoder" yaml:"durationEncoder"`
EncodeCaller CallerEncoder `json:"callerEncoder" yaml:"callerEncoder"`
// Unlike the other primitive type encoders, EncodeName is optional. The
// zero value falls back to FullNameEncoder.
EncodeName NameEncoder `json:"nameEncoder" yaml:"nameEncoder"`
}
这些字段都不难理解,让我们来写一个例子吧
一个简单的样例
一般情况下我们都是用SugaredLogger因为它的速度足够快,而其功能更加强大,如果呢不知道该怎么选择不如就把两个都选上吧,就像这样
package main
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
var Logger *zap.Logger
var SugarLogger *zap.SugaredLogger
func Init() error{
var err error
// 构造EncoderConfig
encoderConfig := zapcore.EncoderConfig{
TimeKey: "timestamp",
LevelKey: "severity",
NameKey: "logger",
CallerKey: "caller",
MessageKey: "message",
StacktraceKey: "stacktrace",
LineEnding: "\n",
EncodeLevel: zapcore.LowercaseLevelEncoder,
EncodeTime: zapcore.ISO8601TimeEncoder,
EncodeDuration: zapcore.SecondsDurationEncoder,
EncodeCaller: zapcore.FullCallerEncoder,
}
// 构造 Config
config := zap.Config{
Level: zap.NewAtomicLevelAt(zapcore.DebugLevel),
Development: true,
Encoding: "json",
EncoderConfig: encoderConfig,
InitialFields: map[string]interface{}{"MyName": "kainhuck"},
OutputPaths: []string{"stdout"},
ErrorOutputPaths: []string{"stdout"},
}
// 可以构造Logger了
Logger, err = config.Build()
if err != nil {
return err
}
// 然后是SugarLogger
SugarLogger = Logger.Sugar()
return nil
}
func main(){
err := Init()
if err != nil {
panic(err)
}
SugarLogger.Debugf("ohhhhhhh err:%s", "horika")
}
运行结果
{"severity":"debug","timestamp":"2020-07-17T17:58:56.626+0800","caller":"D:/Coding/go_module/go_zap/main.go:56","message":"ohhhhhhh err:horika","MyName":"kainhuck"}