go语言优秀的日志包zap预置函数用法

zap 和 logrus 是 go 语言中日志包的佼佼者. 两者都推荐使用结构化的日志打印, 从速度上来说, zap 要比 logrus 快很多. zap 的官方案例中, 介绍了三种使用方式, 分别是 AdvancedConfiguration BasicConfigurationPresets 本篇文章介绍 zap 包的 Presets 用法

Presets(预置函数) 是 zap 日志包最简单的用法, 不需要过多的自定义配置就可以用起来

NewExample

预置函数 NewExample 的源码定义如下

1
2
3
4
5
6
7
8
9
10
11
12
func NewExample(options ...Option) *Logger {
encoderCfg := zapcore.EncoderConfig{
MessageKey: "msg",
LevelKey: "level",
NameKey: "logger",
EncodeLevel: zapcore.LowercaseLevelEncoder,
EncodeTime: zapcore.ISO8601TimeEncoder,
EncodeDuration: zapcore.StringDurationEncoder,
}
core := zapcore.NewCore(zapcore.NewJSONEncoder(encoderCfg), os.Stdout, DebugLevel)
return New(core).WithOptions(options...)
}

可以看到, 预置函数为我们配置了以上内容, 包括

  • 各个字段的名称
  • 日志级别的显示形式
  • 时间编码类型
  • 序列化时间的方式
  • 输出到标准输出
  • 以json形式打印
  • 日志打印级别为Debug

NewExample 输出信息比较少, 打印内容比较详细, 一般用于开发时临时调试使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package main

import (
"go.uber.org/zap"
"time"
)

func main() {
logger := zap.NewExample()
defer logger.Sync()

const url = "http://example.com"

// sugar 版本的 logger
sugar := logger.Sugar()
sugar.Infow("Failed to fetch URL.",
"url", url,
"attempt", 3,
"backoff", time.Second,
)
sugar.Infof("Failed to fetch URL: %s", url)

// 极速版 logger
logger.Info("Failed to fetch URL.",
zap.String("url", url),
zap.Int("attempt", 3),
zap.Duration("backoff", time.Second),
)
}

执行结果

1
2
3
{"level":"info","msg":"Failed to fetch URL.","url":"http://example.com","attempt":3,"backoff":"1s"}
{"level":"info","msg":"Failed to fetch URL: http://example.com"}
{"level":"info","msg":"Failed to fetch URL.","url":"http://example.com","attempt":3,"backoff":"1s"}

NewDevelopment

预置函数 NewDevelopment 的源码定义如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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"},
}
}

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

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

log := New(
zapcore.NewCore(enc, sink, cfg.Level),
cfg.buildOptions(errSink)...,
)
if len(opts) > 0 {
log = log.WithOptions(opts...)
}
return log, nil
}

NewDevelopment 打印内容比较详细(debug level), 输出格式为console

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package main

import (
"go.uber.org/zap"
"time"
)

func main() {
logger, err := zap.NewDevelopment()
if err != nil {
panic(err)
}
defer logger.Sync()

const url = "http://example.com"

// sugar 版本的 logger
sugar := logger.Sugar()
sugar.Infow("Failed to fetch URL.",
"url", url,
"attempt", 3,
"backoff", time.Second,
)
sugar.Infof("Failed to fetch URL: %s", url)

// 极速版 logger
logger.Info("Failed to fetch URL.",
zap.String("url", url),
zap.Int("attempt", 3),
zap.Duration("backoff", time.Second),
)
}

执行结果

1
2
3
2020-03-17T22:52:45.665+0800    INFO    log/l2.go:19    Failed to fetch URL.    {"url": "http://example.com", "attempt": 3, "backoff": "1s"}
2020-03-17T22:52:45.666+0800 INFO log/l2.go:24 Failed to fetch URL: http://example.com
2020-03-17T22:52:45.666+0800 INFO log/l2.go:27 Failed to fetch URL. {"url": "http://example.com", "attempt": 3, "backoff": "1s"}

NewProduction

源码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
 func NewProduction(options ...Option) (*Logger, error) {
return NewProductionConfig().Build(options...)
}

func NewProductionConfig() Config {
return Config{
Level: NewAtomicLevelAt(InfoLevel),
Development: false,
Sampling: &SamplingConfig{
Initial: 100,
Thereafter: 100,
},
Encoding: "json",
EncoderConfig: NewProductionEncoderConfig(),
OutputPaths: []string{"stderr"},
ErrorOutputPaths: []string{"stderr"},
}
}

func NewProductionEncoderConfig() zapcore.EncoderConfig {
return zapcore.EncoderConfig{
TimeKey: "ts",
LevelKey: "level",
NameKey: "logger",
CallerKey: "caller",
MessageKey: "msg",
StacktraceKey: "stacktrace",
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: zapcore.LowercaseLevelEncoder,
EncodeTime: zapcore.EpochTimeEncoder,
EncodeDuration: zapcore.SecondsDurationEncoder,
EncodeCaller: zapcore.ShortCallerEncoder,
}
}

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

log := New(
zapcore.NewCore(enc, sink, cfg.Level),
cfg.buildOptions(errSink)...,
)
if len(opts) > 0 {
log = log.WithOptions(opts...)
}
return log, nil
}

NewProduction 的日志级别提高到了info level, 而且默认使用了结构化更友好的json格式输出日志, 该预置函数, 适用于生产环境, 程序上线后使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
 func main() {
logger, err := zap.NewProduction()
if err != nil {
panic(err)
}
defer logger.Sync()

const url = "http://example.com"

// sugar 版本的 logger
sugar := logger.Sugar()
sugar.Infow("Failed to fetch URL.",
"url", url,
"attempt", 3,
"backoff", time.Second,
)
sugar.Infof("Failed to fetch URL: %s", url)

// 极速版 logger
logger.Info("Failed to fetch URL.",
zap.String("url", url),
zap.Int("attempt", 3),
zap.Duration("backoff", time.Second),
)
}

执行结果

1
2
3
{"level":"info","ts":1584457473.942979,"caller":"log/l2.go:19","msg":"Failed to fetch URL.","url":"http://example.com","attempt":3,"backoff":1}
{"level":"info","ts":1584457473.943102,"caller":"log/l2.go:24","msg":"Failed to fetch URL: http://example.com"}
{"level":"info","ts":1584457473.943125,"caller":"log/l2.go:27","msg":"Failed to fetch URL.","url":"http://example.com","attempt":3,"backoff":1}