zap 使用
uber 开源,zap 可以在控制面板、文档甚至发送数据到其他系统中,以此来记录日志。我们可以指定日志的级别,支持 json 结构化,方便查询。
和 logrus 类似,简单来讲,日志有两个概念:字段和消息。字段用来结构化输出错误相关的上下文环境,而消息简明扼要的阐述错误本身。
开发模式下是普通文本的结构:
package main
import (
"go.uber.org/zap"
"time"
)
func () {
logger, _ := zap.NewDevelopment()
defer logger.Sync()
logger.Info("无法获取网址",
zap.String("url", "http://www.baidu.com"),
zap.Int("attempt", 3),
zap.Duration("backoff", time.Second),
)
}
|
自定义配置:
func main() {
encoderConfig := zapcore.EncoderConfig{
TimeKey: "time",
LevelKey: "level",
NameKey: "logger",
CallerKey: "caller",
MessageKey: "msg",
StacktraceKey: "stacktrace",
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: zapcore.LowercaseLevelEncoder,
EncodeTime: zapcore.ISO8601TimeEncoder, // ISO8601 UTC 时间格式
EncodeDuration: zapcore.SecondsDurationEncoder,
EncodeCaller: zapcore.FullCallerEncoder, // 全路径编码器
}
// 设置日志级别
atom := zap.NewAtomicLevelAt(zap.DebugLevel)
config := zap.Config{
Level: atom, // 日志级别
Development: true, // 开发模式,堆栈跟踪
Encoding: "json", // 输出格式 console 或 json
EncoderConfig: encoderConfig, // 编码器配置
InitialFields: map[string]interface{}{"serviceName": "spikeProxy"}, // 初始化字段,如:添加一个服务器名称
OutputPaths: []string{"stdout", "./logs/spikeProxy.log"}, // 输出到指定文档 stdout(标准输出,正常颜色) stderr(错误输出,红色)
ErrorOutputPaths: []string{"stderr"},
}
// 构建日志
logger, err := config.Build()
if err != nil {
panic(fmt.Sprintf("log 初始化失败: %v", err))
}
logger.Info("log 初始化成功")
logger.Info("无法获取网址",
zap.String("url", "http://www.baidu.com"),
zap.Int("attempt", 3),
zap.Duration("backoff", time.Second),
)
}
lumberjack 归档
日志文档越来越大,我们根据大小、日期进行归档。
zap 可以写入文档,但是并没有归档的功能。借助于 lumberjack 第三方库,利用 hook 进行归档。
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"time"
"gopkg.in/natefinch/lumberjack.v2"
"os"
)
func main() {
hook := lumberjack.Logger{
Filename: "./logs/spikeProxy1.log", // 日志文档路径
MaxSize: 128, // 每个日志文档保存的最大尺寸 单位:M
MaxBackups: 30, // 日志文档最多保存多少个备份
MaxAge: 7, // 文档最多保存多少天
Compress: true, // 是否压缩
}
encoderConfig := zapcore.EncoderConfig{
TimeKey: "time",
LevelKey: "level",
NameKey: "logger",
CallerKey: "linenum",
MessageKey: "msg",
StacktraceKey: "stacktrace",
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: zapcore.LowercaseLevelEncoder, // 小写编码器
EncodeTime: zapcore.ISO8601TimeEncoder, // ISO8601 UTC 时间格式
EncodeDuration: zapcore.SecondsDurationEncoder, //
EncodeCaller: zapcore.FullCallerEncoder, // 全路径编码器
EncodeName: zapcore.FullNameEncoder,
}
// 设置日志级别
atomicLevel := zap.NewAtomicLevel()
atomicLevel.SetLevel(zap.InfoLevel)
core := zapcore.NewCore(
zapcore.NewJSONEncoder(encoderConfig), // 编码器配置
zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout), zapcore.AddSync(&hook)), // 打印到控制面板和文档
atomicLevel, // 日志级别
)
// 开启开发模式,堆栈跟踪
caller := zap.AddCaller()
// 开启文档及行号
development := zap.Development()
// 设置初始化字段
filed := zap.Fields(zap.String("serviceName", "serviceName"))
// 构造日志
logger := zap.New(core, caller, development, filed)
logger.Info("log 初始化成功")
logger.Info("无法获取网址",
zap.String("url", "[http://www.baidu.com](http://www.baidu.com/)"),
zap.Int("attempt", 3),
zap.Duration("backoff", time.Second))
}
zap 优势及原理
标准 log 没有日志分级。seelog 可分级,支持归档,比较灵活,但是利用反射,效率低。
避免 GC: 对象复用
zap 通过 sync.Pool 提供的对象池,复用了大量可以复用的对象,避开了 gc 这个大麻烦。
内建的 Encoder: 避免反射
标准库中的 json.Marshaler 提供的是基于类型反射的拼接方式,代价是高昂的。
zap 选择了自己实现 json Encoder。 通过明确的类型调用,直接拼接字符串,最小化性能开销。
level handler
level handler 是 zap 提供的一种 level 的处理方式,通过 http 请求动态改变日志组件级别。