uber-go/zap 是 Go 中的快速、结构化、分级日志记录方案。
介绍
- 性能优异,比标准库更快,相关数据参考
- 支持结构化日志记录和 printf 风格的日志
- Zap 提供了两种类型的日志记录器
Sugared Logger
要求性能很好但不是很关键的上下文中使用,比其他结构化日志记录包快 4-10 倍,支持结构化日志记录和 printf 风格的日志Logger
在每一微秒和每一次内存分配都很重要的上下文中,只支持强类型的结构化日志记录
使用
基本使用
示例一:
示例二:
输出:
{"level":"error","ts":1734856179.3081212,"caller":"zap/demo1.go:20","msg":"Error fetching url..","url":"www.xiexianbin.cn","error":"Get \"www.xiexianbin.cn\": unsupported protocol scheme \"\"","stacktrace":"main.simpleHttpGet\n\t/Users/xiexianbin/workspace/code/github.com/xiexianbin/note/static/code/golang/zap/demo1.go:20\nmain.main\n\t/Users/xiexianbin/workspace/code/github.com/xiexianbin/note/static/code/golang/zap/demo1.go:39\nruntime.main\n\t/usr/local/Cellar/go/1.23.2/libexec/src/runtime/proc.go:272"}
{"level":"error","ts":1734856179.308209,"caller":"zap/demo1.go:25","msg":"Error fetching url..","type":"sugar","url":"www.xiexianbin.cn","error":"Get \"www.xiexianbin.cn\": unsupported protocol scheme \"\"","stacktrace":"main.simpleHttpGet\n\t/Users/xiexianbin/workspace/code/github.com/xiexianbin/note/static/code/golang/zap/demo1.go:25\nmain.main\n\t/Users/xiexianbin/workspace/code/github.com/xiexianbin/note/static/code/golang/zap/demo1.go:39\nruntime.main\n\t/usr/local/Cellar/go/1.23.2/libexec/src/runtime/proc.go:272"}
{"level":"info","ts":1734856179.342197,"caller":"zap/demo1.go:27","msg":"Success..","statusCode":"200 OK","url":"https://www.xiexianbin.cn"}
{"level":"info","ts":1734856179.3422499,"caller":"zap/demo1.go:31","msg":"Success..","type":"sugar","statusCode":"200 OK","url":"https://www.xiexianbin.cn"}
定制日志级别、输出文件、打印代码的位置和格式
输出:
$ cat test.log
2024-12-22T16:33:12.346+0800 ERROR zap/demo2.go:38 Error fetching url.. {"url": "www.xiexianbin.cn", "error": "Get \"www.xiexianbin.cn\": unsupported protocol scheme \"\""}
2024-12-22T16:33:12.381+0800 INFO zap/demo2.go:43 Success.. {"statusCode": "200 OK", "url": "https://www.xiexianbin.cn"}
$ cat test.err.log
2024-12-22T16:33:12.346+0800 ERROR zap/demo2.go:38 Error fetching url.. {"url": "www.xiexianbin.cn", "error": "Get \"www.xiexianbin.cn\": unsupported protocol scheme \"\""}
说明:
- 封装 zap 日志后,可以使用
zap.AddCallerSkip(1)
获取准确的代码位置
Lumberjack 分割日志文件
配置
zap.NewExample()
- 构建一个测试使用的 Logger,DebugLevel 及以上日志,JSON 格式
// https://github.com/uber-go/zap/blob/v1.27.0/logger.go#L125C1-L141C1
// NewExample builds a Logger that's designed for use in zap's testable
// examples. It writes DebugLevel and above logs to standard out as JSON, but
// omits the timestamp and calling function to keep example output
// short and deterministic.
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...)
}
zap.NewDevelopment()
- 构建一个开发使用的 Logger,DebugLevel 及以上人性化的格式
// https://github.com/uber-go/zap/blob/v1.27.0/logger.go#L104C1-L110C2
// NewDevelopment builds a development Logger that writes DebugLevel and above
// logs to standard error in a human-friendly format.
//
// It's a shortcut for NewDevelopmentConfig().Build(...Option).
func NewDevelopment(options ...Option) (*Logger, error) {
return NewDevelopmentConfig().Build(options...)
}
zap.NewProduction()
- 构建一个开发使用的生产使用呢的 Logger,InfoLevel 及以上日志,JSON 格式
// https://github.com/uber-go/zap/blob/v1.27.0/logger.go#L96C1-L102C2
// NewProduction builds a sensible production Logger that writes InfoLevel and
// above logs to standard error as JSON.
//
// It's a shortcut for NewProductionConfig().Build(...Option).
func NewProduction(options ...Option) (*Logger, error) {
return NewProductionConfig().Build(options...)
}
配置 Option 示例
logger, _ := zap.NewProduction(zap.Fields(
zap.String("log_name", "testlog"),
zap.String("log_author", "prometheus"),
))
defer logger.Sync()
配置 Hook 示例
logger := zap.NewExample(zap.Hooks(func(entry zapcore.Entry) error {
fmt.Println("zap test Hooks")
return nil
}))
defer logger.Sync()
package main
import (
"go.uber.org/zap"
)
func main() {
logger := zap.NewExample()
defer logger.Sync()
logger.Info("some message",
zap.Namespace("shop"),
zap.String("name", "LiLei"),
zap.String("grade", "No2"),
)
logger.Error("some error message",
zap.Namespace("shop"),
zap.String("name", "LiLei"),
zap.String("grade", "No3"),
)
}
全局 Logger
package main
import (
"go.uber.org/zap"
)
func main() {
// 直接调用是不会记录日志信息的,下面日志信息不会输出
zap.L().Info("no log info")
zap.S().Info("no log info [sugared]")
// 初始化日志
logger := zap.NewExample()
defer logger.Sync()
// 全局logger,zap.L() 和 zap.S() 需要调用 ReplaceGlobals 函数才会记录日志信息
zap.ReplaceGlobals(logger)
zap.L().Info("log info")
zap.S().Info("log info [sugared]")
}
重定向标准日志库 log
package main
import (
"log"
"go.uber.org/zap"
)
func main() {
logger := zap.NewExample()
defer logger.Sync()
// RedirectStdLogAt 可以添加日志级别
undo := zap.RedirectStdLog(logger)
log.Print("redirected standard library")
undo()
log.Print("this zap logger")
}
输出调用堆栈
package main
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
func Hello() {
Warn("hello", zap.String("h", "world"), zap.Int("c", 1))
}
func Warn(msg string, fields ...zap.Field) {
zap.L().Warn(msg, fields...)
}
func main() {
logger, _ := zap.NewProduction(zap.AddStacktrace(zapcore.WarnLevel))
defer logger.Sync()
zap.ReplaceGlobals(logger)
Hello()
}
输出文件名和行号
package main
import (
"go.uber.org/zap"
)
func main() {
logger, _ := zap.NewProduction(zap.AddCaller())
defer logger.Sync()
logger.Info("AddCaller:line No and filename")
}