Goland中使用zap日志库

380 阅读4分钟

运行下面的命令安装zap

go get -u go.uber.org/zap
一 简单的logger实例

通过调用zap.NewProduction()/zap.NewDevelopment()或者zap.Example()创建一个Logger

在性能很好但不是很关键的上下文中,使用SugaredLogger。它比其他结构化日志记录包快4-10倍,并且支持结构化和printf风格的日志记录。

在每一微秒和每一次内存分配都很重要的上下文中,使用Logger。它甚至比SugaredLogger更快,内存分配次数也更少,但它只支持强类型的结构化日志记录。

package main​import (    "github.com/gin-gonic/gin"    "go.uber.org/zap")// 两种类型的日志记录器sugarlogger 和loggervar sugarlogger *zap.SugaredLoggervar logger *zap.Logger​func 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 main​import (    "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}