本文正在参加金石计划
你需要先知道的
-
什么是日志?
日志可以理解为事件,它是某一个程序行为的描述。比如吃饭对应的日志描述可以理解为——我在2022年3月10日14点50分吃饭。
-
日志滚动
如果所有日志都保存在一个文件里,首先这个文件会非常大,并且在日常的日志查询中会非常的繁琐,因此我们会按照某一种规则进行日志的拆分,其通常分为两个维度——一是按照时间拆分;一是按照文件大小拆分。因此所谓的日志滚动,就是通过某种滚动的规则进行拆分(或进行压缩)
zap —— 日志生成(需要go1.19以上)
为什么不用log?简单来说zap更快!!!
-
获取 ——
go get -u go.uber.org/zap -
设置配置 ——
Encoderencoder := zapcore.NewxxxEncoder(zapcore.NewxxxEncoderConfig)-
Encoder —— 日志输出的样式 内置两种样式,ConsoleEncoder & JSONEncoder
logger.Info("info 日志", zap.Int("line", 1))- ConsoleEncoder
1.6785843672904398e+09 info info 日志 {"line": 1} - JSONEncoder
{"level":"info","ts":1678584703.859508,"msg":"info 日志","line":1}
- ConsoleEncoder
-
EncoderConfig —— 样式的配置文件 zap内置两种默认配置,production、development
-
EncoderConfig结构
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.EpochTimeEncoder, EncodeDuration: zapcore.SecondsDurationEncoder, EncodeCaller: zapcore.ShortCallerEncoder, }
-
zap.NewProductionEncoderConfig()
{"level":"info","ts":1678584703.859508,"msg":"info 日志","line":1}EncodeLevel: zapcore.LowercaseLevelEncoder, EncodeTime: zapcore.EpochTimeEncoder, EncodeDuration: zapcore.SecondsDurationEncoder, -
zap.NewDevelopmentEncoderConfig()
{"L":"INFO","T":"2023-03-12T09:37:33.573+0800","M":"info 日志","line":1}EncodeLevel: zapcore.CapitalLevelEncoder, EncodeTime: zapcore.ISO8601TimeEncoder, EncodeDuration: zapcore.StringDurationEncoder, -
总结⚠️ 两个样式的最主要变化就是日志等级、日志时间与Duration的解析方式
-
-
-
设置日志输出目的地 ——
WriterSyncerzapcore支持输出到多个地方,比如说输出到控制台的同时也输出到文件中-
单个目的地
consoleSyncer := zapcore.AddSync(os.Stderr) -
多个目的地
logSyncer := zapcore.AddSync(os.Stdout) consoleSyncer := zapcore.AddSync(os.Stderr) // 通过NewMultiWriteSyncer方法组合多个WriteSyncer // 将会multiWriteSyncer的自定义类型,其底层类型其实是WriteSyncer数组 // 而multiWriteSyncer实现了Sync,又成为了WriteSyncer syncer := zapcore.NewMultiWriteSyncer(logSyncer, consoleSyncer)
-
-
获取core
core := zapcore.NewCore(encoder, syncer, zapcore.InfoLevel)- LevelEnabler
zap只会输出大于或等于某一个日志等级的日志,其中LevelEnabler就是判断此项的关键。LevelEnabler是一个接口,包含Enabled(Level) bool,当传入的Level大于或等于自己本身的时候将会返回true,反之则返回false
- LevelEnabler
-
获取logger
logger := zap.new(core) -
总结⚠️
-
zap的核心是zapcore
-
zap在日志输出系列的函数,会通过Write函数进入到内部函数。其中会执行一个for循环,它的里面会执行所有
WriteSyncer的Write函数 -
zapcore.EncoderConfigzapcore.EncoderConfig{ TimeKey: "ts", LevelKey: "level", NameKey: "logger", CallerKey: "caller", FunctionKey: zapcore.OmitKey, MessageKey: "msg", StacktraceKey: "stacktrace", LineEnding: zapcore.DefaultLineEnding, EncodeLevel: zapcore.LowercaseLevelEncoder, EncodeTime: zapcore.EpochTimeEncoder, EncodeDuration: zapcore.SecondsDurationEncoder, EncodeCaller: zapcore.ShortCallerEncoder, } -
zap中有一个
_globalL变量,可以通过L()获取,并且可以被ReplaceGlobals(*Logger)替换猜测是用于互斥访问的单例,因为注释说他是线程安全的
-
lumberjack —— 日志保存
-
获取 ——
import "gopkg.in/natefinch/lumberjack.v2" -
添加到
log的io.Writer中log.SetOutput(&lumberjack.Logger{ Filename: "/var/log/myapp/foo.log", MaxSize: 500, // megabytes MaxBackups: 3, MaxAge: 28, // days Compress: true, // disabled by default })Filename: 文件路径MaxSize: 单个文件的最大大小MaxBackups: 老文件最多能保留多少个MaxAge: 老文件最多保留多少天Compress: 决定了回滚的文件是否需要压缩
-
总结⚠️
- lumberjack是什么做到只需要一行就能够将日志输出到文件里的?
lumberjack + zap
zap通过为io.Writer增加Sync继承了WriteSyncer,在每次的日志输出,如Info()的时候,都会调用Write()。并且zapcore能将多个io.Writer同时注册到zap内部,同时使每个都生效。而lumberjack就符合io.Writer。因此只需要将lumberjack注册到zap里面就行
l := &lumberjack.Logger{
Filename: "/var/log/myapp/foo.log",
MaxSize: 500, // megabytes
MaxBackups: 3,
MaxAge: 28, // days
Compress: true, // disabled by default
}
s1 := zapcore.AddSync(l)
s2 := zapcore.AddSync(os.Stderr)
s := zapcore.NewMultiWriteSyncer(s1, s2)
core := zapcore.NewCore(encoder, s, zapcore.InfoLevel)
logger := zap.New(core)