聊聊golang的zap的NewDevelopment

模式星云
• 阅读 5090

本文主要研究一下golang的zap的NewDevelopment

NewDevelopment

zap@v1.16.0/logger.go

func NewDevelopment(options ...Option) (*Logger, error) {
    return NewDevelopmentConfig().Build(options...)
}
NewDevelopment使用NewDevelopmentConfig进行build

NewDevelopmentConfig

zap@v1.16.0/config.go

func NewDevelopmentConfig() Config {
    return Config{
        Level:            NewAtomicLevelAt(DebugLevel),
        Development:      true,
        Encoding:         "console",
        EncoderConfig:    NewDevelopmentEncoderConfig(),
        OutputPaths:      []string{"stderr"},
        ErrorOutputPaths: []string{"stderr"},
    }
}
NewDevelopmentConfig创建Config,其Level为NewAtomicLevelAt(DebugLevel),Development为true,Encoding为console,EncoderConfig为NewDevelopmentEncoderConfig,OutputPaths及ErrorOutputPaths均为stderr

NewDevelopmentEncoderConfig

zap@v1.16.0/config.go

func NewDevelopmentEncoderConfig() zapcore.EncoderConfig {
    return zapcore.EncoderConfig{
        // Keys can be anything except the empty string.
        TimeKey:        "T",
        LevelKey:       "L",
        NameKey:        "N",
        CallerKey:      "C",
        FunctionKey:    zapcore.OmitKey,
        MessageKey:     "M",
        StacktraceKey:  "S",
        LineEnding:     zapcore.DefaultLineEnding,
        EncodeLevel:    zapcore.CapitalLevelEncoder,
        EncodeTime:     zapcore.ISO8601TimeEncoder,
        EncodeDuration: zapcore.StringDurationEncoder,
        EncodeCaller:   zapcore.ShortCallerEncoder,
    }
}
NewDevelopmentEncoderConfig创建zapcore.EncoderConfig,其LineEnding为zapcore.DefaultLineEnding,EncodeLevel为zapcore.CapitalLevelEncoder,EncodeTime为zapcore.ISO8601TimeEncoder,EncodeDuration为zapcore.StringDurationEncoder,EncodeCaller为zapcore.ShortCallerEncoder

encoder

zap@v1.16.0/zapcore/encoder.go

const DefaultLineEnding = "\n"

func CapitalLevelEncoder(l Level, enc PrimitiveArrayEncoder) {
    enc.AppendString(l.CapitalString())
}

func ISO8601TimeEncoder(t time.Time, enc PrimitiveArrayEncoder) {
    encodeTimeLayout(t, "2006-01-02T15:04:05.000Z0700", enc)
}

func StringDurationEncoder(d time.Duration, enc PrimitiveArrayEncoder) {
    enc.AppendString(d.String())
}

func ShortCallerEncoder(caller EntryCaller, enc PrimitiveArrayEncoder) {
    // TODO: consider using a byte-oriented API to save an allocation.
    enc.AppendString(caller.TrimmedPath())
}

encoder

zap@v1.16.0/encoder.go

var (
    errNoEncoderNameSpecified = errors.New("no encoder name specified")

    _encoderNameToConstructor = map[string]func(zapcore.EncoderConfig) (zapcore.Encoder, error){
        "console": func(encoderConfig zapcore.EncoderConfig) (zapcore.Encoder, error) {
            return zapcore.NewConsoleEncoder(encoderConfig), nil
        },
        "json": func(encoderConfig zapcore.EncoderConfig) (zapcore.Encoder, error) {
            return zapcore.NewJSONEncoder(encoderConfig), nil
        },
    }
    _encoderMutex sync.RWMutex
)
_encoderNameToConstructor内置了console、json两种encoder

NewConsoleEncoder

zap@v1.16.0/console_encoder.go

func NewConsoleEncoder(cfg EncoderConfig) Encoder {
    if len(cfg.ConsoleSeparator) == 0 {
        // Use a default delimiter of '\t' for backwards compatibility
        cfg.ConsoleSeparator = "\t"
    }
    return consoleEncoder{newJSONEncoder(cfg, true)}
}

type consoleEncoder struct {
    *jsonEncoder
}

func (c consoleEncoder) EncodeEntry(ent Entry, fields []Field) (*buffer.Buffer, error) {
    line := bufferpool.Get()

    // We don't want the entry's metadata to be quoted and escaped (if it's
    // encoded as strings), which means that we can't use the JSON encoder. The
    // simplest option is to use the memory encoder and fmt.Fprint.
    //
    // If this ever becomes a performance bottleneck, we can implement
    // ArrayEncoder for our plain-text format.
    arr := getSliceEncoder()
    if c.TimeKey != "" && c.EncodeTime != nil {
        c.EncodeTime(ent.Time, arr)
    }
    if c.LevelKey != "" && c.EncodeLevel != nil {
        c.EncodeLevel(ent.Level, arr)
    }
    if ent.LoggerName != "" && c.NameKey != "" {
        nameEncoder := c.EncodeName

        if nameEncoder == nil {
            // Fall back to FullNameEncoder for backward compatibility.
            nameEncoder = FullNameEncoder
        }

        nameEncoder(ent.LoggerName, arr)
    }
    if ent.Caller.Defined {
        if c.CallerKey != "" && c.EncodeCaller != nil {
            c.EncodeCaller(ent.Caller, arr)
        }
        if c.FunctionKey != "" {
            arr.AppendString(ent.Caller.Function)
        }
    }
    for i := range arr.elems {
        if i > 0 {
            line.AppendString(c.ConsoleSeparator)
        }
        fmt.Fprint(line, arr.elems[i])
    }
    putSliceEncoder(arr)

    // Add the message itself.
    if c.MessageKey != "" {
        c.addSeparatorIfNecessary(line)
        line.AppendString(ent.Message)
    }

    // Add any structured context.
    c.writeContext(line, fields)

    // If there's no stacktrace key, honor that; this allows users to force
    // single-line output.
    if ent.Stack != "" && c.StacktraceKey != "" {
        line.AppendByte('\n')
        line.AppendString(ent.Stack)
    }

    if c.LineEnding != "" {
        line.AppendString(c.LineEnding)
    } else {
        line.AppendString(DefaultLineEnding)
    }
    return line, nil
}
consoleEncoder内嵌了*jsonEncoder,其EncodeEntry方法通过getSliceEncoder()获取`*sliceArrayEncoder,然后依次往arr添加time、level、loggerName、caller,最后再添加业务的message本身,对于有stacktrace还会追加stacktrace

实例

func developmentDemo() {
    logger, _ := zap.NewDevelopment()
    defer logger.Sync() // flushes buffer, if any
    sugar := logger.Sugar()
    sugar.Info("this will be logged")
    sugar.Panic("test panic")
}

输出

2020-12-06T23:29:08.081+0800    INFO    log-demo/zap_demo.go:17 this will be logged
2020-12-06T23:29:08.082+0800    PANIC   log-demo/zap_demo.go:18 test panic
main.developmentDemo
        /zap_demo.go:18
main.main
        /zap_demo.go:10
runtime.main
        /usr/local/go/src/runtime/proc.go:204
panic: test panic

goroutine 1 [running]:
go.uber.org/zap/zapcore.(*CheckedEntry).Write(0xc0000f20c0, 0x0, 0x0, 0x0)
        /go/pkg/mod/go.uber.org/zap@v1.16.0/zapcore/entry.go:234 +0x585
go.uber.org/zap.(*SugaredLogger).log(0xc0000fbed0, 0x4, 0x0, 0x0, 0xc0000fbed8, 0x1, 0x1, 0x0, 0x0, 0x0)
        /go/pkg/mod/go.uber.org/zap@v1.16.0/sugar.go:234 +0xf6
go.uber.org/zap.(*SugaredLogger).Panic(...)
        /go/pkg/mod/go.uber.org/zap@v1.16.0/sugar.go:123
main.developmentDemo()
        /zap_demo.go:18 +0x199
main.main()
        /zap_demo.go:10 +0x25
exit status 2

小结

NewDevelopmentEncoderConfig创建zapcore.EncoderConfig,其LineEnding为zapcore.DefaultLineEnding,EncodeLevel为zapcore.CapitalLevelEncoder,EncodeTime为zapcore.ISO8601TimeEncoder,EncodeDuration为zapcore.StringDurationEncoder,EncodeCaller为zapcore.ShortCallerEncoder

doc

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
科工人 科工人
4年前
聊聊golang的DDD项目结构
序本文主要研究一下golang的DDD项目结构interfacesfoodappserver/interfacesinterfacesgit:(master)tree.|____fileupload||____fileformat.go||____fileupload.go|____food_handler.go|__
Wesley13 Wesley13
3年前
Go gRPC进阶
前言上篇介绍了gogrpcmiddleware(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fgithub.com%2Fgrpcecosystem%2Fgogrpcmiddleware)的grpc_zap、grpc_auth和grpc_recovery
Stella981 Stella981
3年前
Golang学习系列第一天:安装golang
0. ssh连接linux(我用的centos7),略1. golang下载由于Golang官网https://golang.org/(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fgola
Stella981 Stella981
3年前
Python+Selenium自动化篇
本篇文字主要学习selenium定位页面元素的集中方法,以百度首页为例子。0.元素定位方法主要有:id定位:find\_element\_by\_id('')name定位:find\_element\_by\_name('')class定位:find\_element\_by\_class\_name(''
Wesley13 Wesley13
3年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Stella981 Stella981
3年前
RedisTemplate读取slowlog
序本文主要研究一下如何使用RedisTemplate(lettuce类库)读取slowlogmaven<dependency<groupIdorg.springframework.boot</groupId<artifactIdspringbootstarterdata
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
4个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(