引言
我现在好困呐,明明现在才十二点半,困死了
之前一直想着别人写的日志怎么这么高大上,自己写的怎么四五行就结束了——定制化
在小项目中其实自己随便用用就够了,但是项目规模一大,就需要吧日志也定制一下,毕竟定制版肯定比简陋版要快乐一点
定制化
go get -u go.uber.org/zap
设置
代码将从这两方面进行定制,配置设置中的定制指的是某些输出,显示的定制,而代码设置这是日志的结构之类的设置,因此我们需要两个文件(如何读入配置这里不再赘述)
思路
下面将从定制的代码逻辑开始讲解,其实相比于模块化的代码呈现和逻辑性的代码梳理,其实前者更容易被观摩,后者更容易理清楚思路
Config
本来觉得先从core开始会比较好,现在感觉还是从config开始比较好
1 2 3 4 5 6 7 8 9 10 11
| type Zap struct { Prefix string `default:"gin-vue-admin" yaml:"prefix" json:"prefix"` Level string `default:"panic" yaml:"level" json:"level"` Path string `yaml:"path" json:"path"` Director string `default:"director" yaml:"director" json:"director"` EncoderLevel string `default:"LowercaseLevelEncoder" yaml:"encoder_level" json:"encoder_level"` MaxAge int `default:"7" yaml:"max_age" json:"max_age"` ShowLine bool `default:"true" yaml:"show_line" json:"show_line"` LogInConsole bool `default:"true" yaml:"log_in_console" json:"log_in_console" ` Encoder string `default:"console" yaml:"encoder" json:"encoder"` }
|
下面还要进行一些转换,方便后面的代码书写主要
- 把 Level string 类型 转化为 zapcore.level 类型
- 把 EncoderLevel 类型 转化为 zapcore.LevelEncoder 类型
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
| func (z *Zap) GetLevelEncoder() zapcore.LevelEncoder { switch { case z.EncoderLevel == "LowercaseLevelEncoder": return zapcore.LowercaseLevelEncoder case z.EncoderLevel == "LowercaseColorLevelEncoder": return zapcore.LowercaseColorLevelEncoder case z.EncoderLevel == "CapitalLevelEncoder": return zapcore.CapitalLevelEncoder case z.EncoderLevel == "CapitalColorLevelEncoder": return zapcore.CapitalColorLevelEncoder default: return zapcore.LowercaseLevelEncoder } }
func (z *Zap) TransmitLvl() zapcore.Level { switch z.Level { case "debug": return zapcore.DebugLevel case "Info": return zapcore.InfoLevel case "warn": return zapcore.WarnLevel case "error": return zapcore.ErrorLevel case "Dpanic": return zapcore.DPanicLevel case "panic": return zapcore.PanicLevel default: return zapcore.DebugLevel } }
|
核心
一切的万恶资源都来自于这个方法,他让我们明白我们需要准备什么
- encoder 配置器
- WriteSyncer 写入器
- levelEnabler 表示有哪些特定级别会被记录下来
1
| func NewCore(enc Encoder, ws WriteSyncer, enab LevelEnabler) Core
|
Encoder
这是配置器的配置,我们可以用这些配置配置自己需要的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| type EncoderConfig struct { MessageKey string `json:"messageKey" yaml:"messageKey"` LevelKey string `json:"levelKey" yaml:"levelKey"` TimeKey string `json:"timeKey" yaml:"timeKey"` NameKey string `json:"nameKey" yaml:"nameKey"` CallerKey string `json:"callerKey" yaml:"callerKey"` FunctionKey string `json:"functionKey" yaml:"functionKey"` StacktraceKey string `json:"stacktraceKey" yaml:"stacktraceKey"` SkipLineEnding bool `json:"skipLineEnding" yaml:"skipLineEnding"` LineEnding string `json:"lineEnding" yaml:"lineEnding"` EncodeLevel LevelEncoder `json:"levelEncoder" yaml:"levelEncoder"` EncodeTime TimeEncoder `json:"timeEncoder" yaml:"timeEncoder"` EncodeDuration DurationEncoder `json:"durationEncoder" yaml:"durationEncoder"` EncodeCaller CallerEncoder `json:"callerEncoder" yaml:"callerEncoder"` EncodeName NameEncoder `json:"nameEncoder" yaml:"nameEncoder"` }
|
总定制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| func (z *_zap) GetEncoderConfig() zapcore.EncoderConfig { return zapcore.EncoderConfig{ MessageKey: "msg", LevelKey: "level", TimeKey: "ts", NameKey: "logger", CallerKey: "caller", StacktraceKey: "stacktrace", LineEnding: zapcore.DefaultLineEnding, EncodeLevel: Z.GetLevelEncoder(), EncodeTime: Z.CustomTimeEncoder, EncodeDuration: zapcore.SecondsDurationEncoder, EncodeCaller: zapcore.FullCallerEncoder, } }
|
大小写编码配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| func (z *Zap) GetLevelEncoder() zapcore.LevelEncoder { switch { case z.EncoderLevel == "LowercaseLevelEncoder": return zapcore.LowercaseLevelEncoder case z.EncoderLevel == "LowercaseColorLevelEncoder": return zapcore.LowercaseColorLevelEncoder case z.EncoderLevel == "CapitalLevelEncoder": return zapcore.CapitalLevelEncoder case z.EncoderLevel == "CapitalColorLevelEncoder": return zapcore.CapitalColorLevelEncoder default: return zapcore.LowercaseLevelEncoder } }
|
时间配置
1 2 3
| func (z *Zap) CustomTimeEncoder(t time.Time, encoder zapcore.PrimitiveArrayEncoder) { encoder.AppendString(Z.Prefix + t.Format("2006/01/02 - 15:04:05.000")) }
|
zapcore自带配置
1 2 3 4
| zapcore.DefaultLineEnding zapcore.SecondsDurationEncoder zapcore.FullCallerEncoder
|
有了定制器的配置后,就可以组装定制器了,这个就是再包装一下,表示这个日志形式为json/console形式
1 2 3 4 5 6
| func (z *_zap) GetEncoder() zapcore.Encoder { if Z.Encoder == "json" { return zapcore.NewJSONEncoder(z.GetEncoderConfig()) } return zapcore.NewConsoleEncoder(z.GetEncoderConfig()) }
|
1 2 3 4
| {"xx":"xx","xx":"xx","xx":"xx","xx":"xx","xx":"xx"}
2024/05/07 - 00:38:17.213 debug log Debug成功
|
ok,至此位置我们的定制器配置完成
LevelEnabler
这个东西,还记得我们之前有方法把 配置中的level string变成了level zapcore。下面我们就需要把level zapcore变成 zap.levelEnablerFunc
这个其实很好写出来,看看返回 值的类型就行了
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
| func (z *_zap) GetLevelPriority(level zapcore.Level) zap.LevelEnablerFunc { switch level { case zapcore.DebugLevel: return func(level zapcore.Level) bool { return level == zap.DebugLevel } case zapcore.InfoLevel: return func(level zapcore.Level) bool { return level == zap.InfoLevel } case zapcore.WarnLevel: return func(level zapcore.Level) bool { return level == zap.WarnLevel } case zapcore.ErrorLevel: return func(level zapcore.Level) bool { return level == zap.ErrorLevel } case zapcore.DPanicLevel: return func(level zapcore.Level) bool { return level == zap.DPanicLevel } case zapcore.PanicLevel: return func(level zapcore.Level) bool { return level == zap.PanicLevel } case zapcore.FatalLevel: return func(level zapcore.Level) bool { return level == zap.FatalLevel } default: return func(level zapcore.Level) bool { return level == zap.DebugLevel } } }
|
生成Core
其实到了这里我们肯定疑惑为什么要定制zap,但是我们为什么不想一下,我们之前的代码都是把各种等级的日志放在一起,那么我们能不能把这些不同等级的日志都分开来,那么我们就需要多个core,不同的core表示不同的等级,就这样
1 2 3 4 5 6 7 8 9 10 11 12
| func (z *_zap) GetEncoderCore(l zapcore.Level, level zap.LevelEnablerFunc) zapcore.Core { return zapcore.NewCore(z.GetEncoder(), os.Stdout, level) }
func (z *_zap) GetZapCores() []zapcore.Core { cores := make([]zapcore.Core, 0, 7) for level := Z.TransmitLvl(); level <= zapcore.FatalLevel; level++ { cores = append(cores, z.GetEncoderCore(level, z.GetLevelPriority(level))) } return cores }
|
Init
1 2 3 4 5 6 7 8 9
| func Init() (logger *zap.Logger) { if exists, _ := PathExists(Z.Path); !exists { fmt.Println("create " + Z.Path + " directory") _ = os.MkdirAll(Z.Path, os.ModePerm) } cores := z.GetZapCores() logger = zap.New(zapcore.NewTee(cores...)) return logger }
|
ok,至此,所有的逻辑理清楚了,是不是感觉特别简单,其实真正写起来还是有点问题的,下面看一下我的随意发挥
注意点
1 2 3 4 5 6 7 8 9 10 11 12
| func (z *_zap) GetEncoderCore() zapcore.Core { writer := GetWriteSyncer(Z.TransmitLvl().String()) return zapcore.NewCore(z.GetEncoder(), writer, z.GetLevelPriority()) }
func (z *_zap) GetZapCores() []zapcore.Core { cores := make([]zapcore.Core, 0) for level := Z.TransmitLvl(); level <= zapcore.FatalLevel; level++ { cores = append(cores, z.GetEncoderCore()) } return cores }
|
这个代码是我一开始写出来的代码,这里有个很大的问题,就是测试的时候会输出很多相同的log
询问GPT:
根据您提供的代码,问题可能出在 GetZapCores
方法中。在这个方法中,您为每个日志级别创建了相应的核心,但是在创建核心时,对于每个级别,您都使用了相同的编码器和写入器。
这意味着尽管您为每个级别创建了不同的核心,但它们实际上都共享了相同的编码器和写入器。因此,无论记录哪个级别的日志,它们都会使用相同的编码器和写入器进行处理。
要解决这个问题,您需要为每个级别创建不同的编码器和写入器,而不是共享相同的实例。这样,即使使用相同的日志级别,也会使用相应级别的编码器和写入器。
由此发现,好像无论是什么level,我都是直接使用分离的函数调用
因此我们把直接的函数调用中,往函数中加入参数,保证其中的级别统一,就是上述的代码
完整代码
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
| package Z
import ( "go.uber.org/zap/zapcore" "time" )
var Z Zap
type Zap struct { Prefix string `default:"gin-vue-admin" yaml:"prefix" json:"prefix"` Level string `default:"panic" yaml:"level" json:"level"` Path string `default:"./logs/" yaml:"path" json:"path"` Director string `default:"director" yaml:"director" json:"director"` EncoderLevel string `default:"LowercaseLevelEncoder" yaml:"encoder_level" json:"encoder_level"` MaxAge int `default:"7" yaml:"max_age" json:"max_age"` ShowLine bool `default:"true" yaml:"show_line" json:"show_line"` LogInConsole bool `default:"true" yaml:"log_in_console" json:"log_in_console"` Encoder string `default:"console" yaml:"encoder" json:"encoder"` }
func (z *Zap) GetLevelEncoder() zapcore.LevelEncoder { switch { case z.EncoderLevel == "LowercaseLevelEncoder": return zapcore.LowercaseLevelEncoder case z.EncoderLevel == "LowercaseColorLevelEncoder": return zapcore.LowercaseColorLevelEncoder case z.EncoderLevel == "CapitalLevelEncoder": return zapcore.CapitalLevelEncoder case z.EncoderLevel == "CapitalColorLevelEncoder": return zapcore.CapitalColorLevelEncoder default: return zapcore.LowercaseLevelEncoder } }
func (z *Zap) CustomTimeEncoder(t time.Time, encoder zapcore.PrimitiveArrayEncoder) { encoder.AppendString(Z.Prefix + t.Format("2006/01/02 - 15:04:05.000")) }
func (z *Zap) TransmitLvl() zapcore.Level { switch z.Level { case "debug": return zapcore.DebugLevel case "Info": return zapcore.InfoLevel case "warn": return zapcore.WarnLevel case "error": return zapcore.ErrorLevel case "Dpanic": return zapcore.DPanicLevel case "panic": return zapcore.PanicLevel default: return zapcore.DebugLevel } }
|
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 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
| package Z
import ( "errors" "fmt" "go.uber.org/zap" "go.uber.org/zap/zapcore" "os" )
var z = &_zap{}
type _zap struct{}
func Init() (logger *zap.Logger) { if exists, _ := PathExists(Z.Path); !exists { fmt.Println("create " + Z.Path + " directory") _ = os.MkdirAll(Z.Path, os.ModePerm) } cores := z.GetZapCores() logger = zap.New(zapcore.NewTee(cores...)) return logger }
func PathExists(path string) (bool, error) { fi, err := os.Stat(path) if err == nil { if fi.IsDir() { return true, nil } return false, errors.New("存在同名文件") } if os.IsNotExist(err) { return false, nil } return false, err }
func (z *_zap) GetEncoderCore(l zapcore.Level, level zap.LevelEnablerFunc) zapcore.Core { return zapcore.NewCore(z.GetEncoder(), os.Stdout, level) }
func (z *_zap) GetZapCores() []zapcore.Core { cores := make([]zapcore.Core, 0, 7) for level := Z.TransmitLvl(); level <= zapcore.FatalLevel; level++ { cores = append(cores, z.GetEncoderCore(level, z.GetLevelPriority(level))) } return cores }
func (z *_zap) GetEncoderConfig() zapcore.EncoderConfig { return zapcore.EncoderConfig{ MessageKey: "msg", LevelKey: "level", TimeKey: "ts", NameKey: "logger", CallerKey: "caller", StacktraceKey: "stacktrace", LineEnding: zapcore.DefaultLineEnding, EncodeLevel: Z.GetLevelEncoder(), EncodeTime: Z.CustomTimeEncoder, EncodeDuration: zapcore.SecondsDurationEncoder, EncodeCaller: zapcore.FullCallerEncoder, } }
func (z *_zap) GetEncoder() zapcore.Encoder { if Z.Encoder == "json" { return zapcore.NewJSONEncoder(z.GetEncoderConfig()) } return zapcore.NewConsoleEncoder(z.GetEncoderConfig()) }
func (z *_zap) GetLevelPriority(level zapcore.Level) zap.LevelEnablerFunc { switch level { case zapcore.DebugLevel: return func(level zapcore.Level) bool { return level == zap.DebugLevel } case zapcore.InfoLevel: return func(level zapcore.Level) bool { return level == zap.InfoLevel } case zapcore.WarnLevel: return func(level zapcore.Level) bool { return level == zap.WarnLevel } case zapcore.ErrorLevel: return func(level zapcore.Level) bool { return level == zap.ErrorLevel } case zapcore.DPanicLevel: return func(level zapcore.Level) bool { return level == zap.DPanicLevel } case zapcore.PanicLevel: return func(level zapcore.Level) bool { return level == zap.PanicLevel } case zapcore.FatalLevel: return func(level zapcore.Level) bool { return level == zap.FatalLevel } default: return func(level zapcore.Level) bool { return level == zap.DebugLevel } } }
|
1 2 3 4 5 6 7 8 9 10 11
| func GetWriteSyncer(lvl string) zapcore.WriteSyncer { lumberJackLogger := &lumberjack.Logger{ Filename: fmt.Sprintf("%s/%s.log", Z.Path, lvl), MaxSize: 10, MaxBackups: 3, MaxAge: 28, Compress: true, } return zapcore.NewMultiWriteSyncer(zapcore.AddSync(lumberJackLogger), zapcore.AddSync(os.Stdout)) }
|
test
1 2 3 4 5 6
| func TestInit(t *testing.T) { l := Init() l.Debug("log Debug成功") l.Warn("log Warn成功") l.Info("log Info成功") }
|
1 2 3 4 5 6 7
| === RUN TestInit create directory 2024/05/07 - 01:19:04.204 debug log Debug成功 2024/05/07 - 01:19:04.220 warn log Warn成功 2024/05/07 - 01:19:04.220 info log Info成功 --- PASS: TestInit (0.02s) PASS
|
总结
🆗,完工睡觉去了