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"}