运行下面的命令安装zap
go get -u go.uber.org/zap
一 简单的logger实例
通过调用
zap.NewProduction()/zap.NewDevelopment()或者zap.Example()创建一个Logger在性能很好但不是很关键的上下文中,使用
SugaredLogger。它比其他结构化日志记录包快4-10倍,并且支持结构化和printf风格的日志记录。在每一微秒和每一次内存分配都很重要的上下文中,使用
Logger。它甚至比SugaredLogger更快,内存分配次数也更少,但它只支持强类型的结构化日志记录。
package mainimport ( "github.com/gin-gonic/gin" "go.uber.org/zap")// 两种类型的日志记录器sugarlogger 和loggervar sugarlogger *zap.SugaredLoggervar logger *zap.Loggerfunc main() { InitLogger() defer sugarlogger.Sync() r := gin.Default() r.GET("/sugerlogger", func(c *gin.Context) { sugarlogger.Info("sugerlogger:访问了sugerlogger") logger.Info("logger:访问了sugerlogger") }) _ = r.Run(":8888")}func InitLogger() { // zap.NewExample() 用于测试 //{"level":"info","msg":"sugerlogger:访问了sugerlogger"} //zap.NewProduction() 用于生产环境 //{"level":"info","ts":"2021-09-22T20:17:15.979+0800","caller":"zap_log_demo/SugaredLoggerDemo.go:16","msg":"sugerlogger:访问了sugerlogger"} //zap.NewDevelopment() 用户开发环境 //2021-09-22T20:17:53.011+0800 INFO zap_log_demo/SugaredLoggerDemo.go:16 sugerlogger:访问了sugerlogger logger, _ = zap.NewDevelopment() sugarlogger = logger.Sugar()}
二 、定制logger
将日志写入文件而不是终端
我们将使用 zap.new()方法配置
package mainimport ( "github.com/gin-gonic/gin" "go.uber.org/zap" "go.uber.org/zap/zapcore" "os")var sugarlogger *zap.SugaredLogger//日志格式func getEncoder() zapcore.Encoder { // 以下两种都是EncoderConfig类型 可以使用源码中封装的 也可以自定义 // zap.NewProductionEncoderConfig() // zap.NewDevelopmentEncoderConfig() // return zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()) //自定义 我们可以修改里面的key和value值实现日志的定制 encodingConfig := zapcore.EncoderConfig{ TimeKey: "ts", LevelKey: "level", NameKey: "logger", CallerKey: "caller", FunctionKey: zapcore.OmitKey, MessageKey: "msg", StacktraceKey: "stacktrace", LineEnding: zapcore.DefaultLineEnding, EncodeLevel: zapcore.LowercaseLevelEncoder, EncodeTime: zapcore.ISO8601TimeEncoder, //时间格式更改 EncodeDuration: zapcore.SecondsDurationEncoder, EncodeCaller: zapcore.ShortCallerEncoder, } return zapcore.NewJSONEncoder(encodingConfig)}// 日志写到哪里func getWriteSyncer() zapcore.WriteSyncer { file, _ := os.OpenFile("path", os.O_CREATE|os.O_APPEND, 666) return zapcore.AddSync(file)}func InitLogger() { // zapcore.Core需要三个配置——Encoder,WriteSyncer,LogLevel encoder := getEncoder() writerSync := getWriteSyncer() core := zapcore.NewCore(encoder, writerSync, zapcore.DebugLevel) //logger := zap.New(core) //添加AddCaller方法可以查看那个文件那一行调用了该日志 //{"level":"info","ts":"2021-09-22T22:26:03.874+0800","caller":"go_zap_new/zap定制logger.go:55","msg":"Success...状态码200"} logger := zap.New(core,zap.AddCaller()) sugarlogger = logger.Sugar()}func main() { InitLogger() defer sugarlogger.Sync() r := gin.Default() r.GET("/sugerlogger", func(c *gin.Context) { sugarlogger.Info("自定义的log:访问了sugerlogger") }) _ = r.Run(":8888")}
三、日志切割
目前来说日志全是写在一个文件里的 当所有的日志全在一个文件里的时候 查阅是不友好的
安装
go get -u github.com/natefinch/lumberjack
使用
由于是文件的切割 所以我们只修改getWriteSyncer方法
func getWriteSyncer() zapcore.WriteSyncer { lumberJackLogger := &lumberjack.Logger{ Filename: "./go_zap_loglib/selflog.log", //文件位置 MaxSize: 10,//在进行切割之前,日志文件的最大大小(以MB为单位) MaxBackups: 5, //保留旧文件的最大个数 MaxAge: 30, // 保留旧文件的最大天数 Compress: false,// 是否压缩/归档旧文件 } return zapcore.AddSync(lumberJackLogger)}
四、分析源码 logger如何创建的
//默认的EncoderConfig结构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"` FunctionKey string `json:"functionKey" yaml:"functionKey"` 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"` // Configures the field separator used by the console encoder. Defaults // to tab. ConsoleSeparator string `json:"consoleSeparator" yaml:"consoleSeparator"`}//默认的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"`}
logger 实例创建的源码分析
func NewDevelopment(options ...Option) (*Logger, error) { return NewDevelopmentConfig().Build(options...)}func NewDevelopmentConfig() Config { return Config{ Level: NewAtomicLevelAt(DebugLevel), Development: true, Encoding: "console", EncoderConfig: NewDevelopmentEncoderConfig(), OutputPaths: []string{"stderr"}, ErrorOutputPaths: []string{"stderr"}, }}//config的build方法func (cfg Config) Build(opts ...Option) (*Logger, error) { enc, err := cfg.buildEncoder() if err != nil { return nil, err } sink, errSink, err := cfg.openSinks() if err != nil { return nil, err } if cfg.Level == (AtomicLevel{}) { return nil, fmt.Errorf("missing Level") } //关键之方法new()以及里面的zapcore.NewCore(enc, sink, cfg.Level)方法中的三个参数 // enc -> zapcore.Encoder 意思以什么格式写入日志 // sink -> zapcore.WriteSyncer 日志写到什么地方 // 日志等级cfg.Level 那种级别的日志将别写入 log := New( zapcore.NewCore(enc, sink, cfg.Level), cfg.buildOptions(errSink)..., ) if len(opts) > 0 { log = log.WithOptions(opts...) } return log, nil}