gitee.com/h79/goutils@v1.22.10/common/logger/log.go (about) 1 package logger 2 3 import ( 4 "context" 5 "flag" 6 "fmt" 7 "gitee.com/h79/goutils/common/system" 8 "github.com/natefinch/lumberjack" 9 "go.uber.org/zap" 10 "go.uber.org/zap/zapcore" 11 "io" 12 "os" 13 "path" 14 "runtime/debug" 15 ) 16 17 var ( 18 ndebugEnabled = false 19 logger *zap.Logger 20 ) 21 22 type Config struct { 23 Level int8 `json:"level" yaml:"level" xml:"level"` 24 NDebug bool `json:"nDebug" yaml:"nDebug" xml:"nDebug"` 25 WriterEnabled bool `json:"writerEnabled" yaml:"writerEnabled" xml:"writerEnabled"` // 自己实现writer 26 ConsoleEnabled bool `json:"console" yaml:"console" xml:"console"` 27 FileEnabled bool `json:"fileEnabled" yaml:"fileEnabled" xml:"fileEnabled"` 28 EncoderFormat string `json:"encoderFormat" yaml:"encoderFormat" xml:"encoderFormat"` 29 FilePath string `json:"filePath" yaml:"filePath" xml:"filePath"` 30 FileConfigs []FileConfig `json:"fileConfigs" yaml:"fileConfigs" xml:"fileConfigs"` 31 WriterConfigs []WriterConfig `json:"writerConfigs" yaml:"writerConfigs" xml:"writerConfigs"` 32 hooks []func(entry zapcore.Entry) error 33 } 34 35 type FileConfig struct { 36 MinLevel int8 `json:"minLevel" yaml:"minLevel" xml:"minLevel"` 37 MaxLevel int8 `json:"maxLevel" yaml:"maxLevel" xml:"maxLevel"` 38 // Name is the name of the logfile which will be placed inside the directory 39 Name string `json:"name" yaml:"name" xml:"name"` 40 // MaxSize the max size in MB of the logfile before it's rolled 41 MaxSize int `json:"maxSize" yaml:"maxSize" xml:"maxSize"` 42 // MaxBackups the max number of rolled files to keep 43 MaxBackups int `json:"backupNum" yaml:"backupNum" xml:"backupNum"` 44 // MaxAge the max age in days to keep a logfile 45 MaxAge int `json:"days" json:"days" xml:"days"` 46 } 47 48 type WriterConfig struct { 49 MinLevel int8 `json:"minLevel" yaml:"minLevel" xml:"minLevel"` 50 MaxLevel int8 `json:"maxLevel" yaml:"maxLevel" xml:"maxLevel"` 51 WriteType string `json:"type" yaml:"type" xml:"type"` 52 w io.Writer 53 } 54 55 // 对外进行统一的封装 56 const ( 57 // RecoverLevel () 58 RecoverLevel = int8(-3) 59 // NDebugLevel () 60 NDebugLevel = int8(-2) 61 62 DebugLevel = int8(zap.DebugLevel) 63 InfoLevel = int8(zap.InfoLevel) 64 // WarnLevel defines warn log level. 65 WarnLevel = int8(zap.WarnLevel) 66 // ErrorLevel defines error log level. 67 ErrorLevel = int8(zap.ErrorLevel) 68 // FatalLevel defines fatal log level. 69 FatalLevel = int8(zap.FatalLevel) 70 // PanicLevel defines panic log level. 71 PanicLevel = int8(zap.PanicLevel) 72 ) 73 74 func init() { 75 flag.BoolVar(&ndebugEnabled, "ndebug", true, "not output debug log in release.") 76 77 system.DefRecoverFunc = func(r any) { 78 Recover(6, r) 79 } 80 if err := Configure(Config{ConsoleEnabled: true, Level: int8(zap.DebugLevel)}); err != nil { 81 panic("init log failure") 82 } 83 } 84 85 func Byte2(level int8, b []byte) string { 86 if level == NDebugLevel { 87 if ndebugEnabled { 88 return string(b) 89 } 90 return "" 91 } 92 if zap.L().Core().Enabled(zapcore.Level(level)) { 93 return string(b) 94 } 95 return "" 96 } 97 98 const msg = "system" 99 100 // Info defines info log level. 101 func Info(format string, args ...interface{}) { 102 if ce := logger.Check(zapcore.InfoLevel, msg); ce != nil { 103 ce.Write(ZLogf(format, args...)) 104 } 105 } 106 107 // I defines info log level. 108 func I(msg, format string, args ...interface{}) { 109 if ce := logger.Check(zapcore.InfoLevel, msg); ce != nil { 110 ce.Write(ZLogf(format, args...)) 111 } 112 } 113 114 // Warn defines warn log level. 115 func Warn(format string, args ...interface{}) { 116 if ce := logger.Check(zapcore.WarnLevel, msg); ce != nil { 117 ce.Write(ZLogf(format, args...)) 118 } 119 } 120 121 // W defines warn log level. 122 func W(msg, format string, args ...interface{}) { 123 if ce := logger.Check(zapcore.WarnLevel, msg); ce != nil { 124 ce.Write(ZLogf(format, args...)) 125 } 126 } 127 128 // Error defines error log level. 129 func Error(format string, args ...interface{}) { 130 if ce := logger.Check(zapcore.ErrorLevel, msg); ce != nil { 131 ce.Write(ZLogf(format, args...)) 132 } 133 } 134 135 // E defines error log level. 136 func E(msg, format string, args ...interface{}) { 137 if ce := logger.Check(zapcore.ErrorLevel, msg); ce != nil { 138 ce.Write(ZLogf(format, args...)) 139 } 140 } 141 142 // Recover defines error log level. 143 func Recover(callerSkip int, err interface{}) { 144 if callerSkip <= 0 { 145 callerSkip = 2 146 } 147 lg := zap.L() 148 lg = lg.WithOptions(zap.AddCallerSkip(callerSkip)) 149 if ce := lg.Check(zapcore.Level(RecoverLevel), "recover"); ce != nil { 150 ce.Write(zap.Any("err", err), zap.String("stack", string(debug.Stack()))) 151 } 152 } 153 154 // Fatal defines fatal log level. 155 func Fatal(format string, args ...interface{}) { 156 if ce := logger.Check(zapcore.FatalLevel, msg); ce != nil { 157 ce.Write(ZLogf(format, args...)) 158 } 159 } 160 161 // F defines fatal log level. 162 func F(msg, format string, args ...interface{}) { 163 if ce := logger.Check(zapcore.FatalLevel, msg); ce != nil { 164 ce.Write(ZLogf(format, args...)) 165 } 166 } 167 168 // Panic (format string, args ...interface{}) { defines panic log level. 169 func Panic(format string, args ...interface{}) { 170 if ce := logger.Check(zapcore.PanicLevel, msg); ce != nil { 171 ce.Write(ZLogf(format, args...)) 172 } 173 } 174 175 // P (format string, args ...interface{}) { defines panic log level. 176 func P(msg, format string, args ...interface{}) { 177 if ce := logger.Check(zapcore.PanicLevel, msg); ce != nil { 178 ce.Write(ZLogf(format, args...)) 179 } 180 } 181 182 func Debug(format string, args ...interface{}) { 183 if ce := logger.Check(zapcore.DebugLevel, msg); ce != nil { 184 ce.Write(ZLogf(format, args...)) 185 } 186 } 187 188 func D(msg, format string, args ...interface{}) { 189 if ce := logger.Check(zapcore.DebugLevel, msg); ce != nil { 190 ce.Write(ZLogf(format, args...)) 191 } 192 } 193 194 // NDebug 195 /** 196 正式环境,不输出日志 197 */ 198 func NDebug(format string, args ...interface{}) { 199 if !ndebugEnabled { 200 return 201 } 202 if ce := logger.Check(zapcore.DebugLevel, msg); ce != nil { 203 ce.Write(ZLogf(format, args...)) 204 } 205 } 206 207 func N(msg, format string, args ...interface{}) { 208 if !ndebugEnabled { 209 return 210 } 211 if ce := logger.Check(zapcore.DebugLevel, msg); ce != nil { 212 ce.Write(ZLogf(format, args...)) 213 } 214 } 215 216 func SpecW(level int8, msg string, fields ...zapcore.Field) { 217 if ce := logger.Check(zapcore.Level(level), msg); ce != nil { 218 ce.Write(fields...) 219 } 220 } 221 222 func ZLog(info string) zap.Field { 223 return zap.String("log", info) 224 } 225 226 func ZLogf(format string, args ...any) zap.Field { 227 return zap.String("log", fmt.Sprintf(format, args...)) 228 } 229 230 func TraceId(ctx context.Context) string { 231 ret, ok := ctx.Value("traceId").(string) 232 if ok { 233 return ret 234 } 235 return "" 236 } 237 238 func NTraceId(ctx context.Context) string { 239 if !ndebugEnabled { 240 return "" 241 } 242 return DTraceId(ctx) 243 } 244 245 func DTraceId(ctx context.Context) string { 246 if L().Core().Enabled(zapcore.DebugLevel) { 247 return TraceId(ctx) 248 } 249 return "" 250 } 251 252 func WTraceId(ctx context.Context) string { 253 if L().Core().Enabled(zapcore.WarnLevel) { 254 return TraceId(ctx) 255 } 256 return "" 257 } 258 259 func ITraceId(ctx context.Context) string { 260 if L().Core().Enabled(zapcore.InfoLevel) { 261 return TraceId(ctx) 262 } 263 return "" 264 } 265 266 func ETraceId(ctx context.Context) string { 267 if L().Core().Enabled(zapcore.ErrorLevel) { 268 return TraceId(ctx) 269 } 270 return "" 271 } 272 273 type Option func(config *Config) 274 275 func WithWriterLevel(level int8, w io.Writer) Option { 276 return func(config *Config) { 277 if !config.WriterEnabled { 278 return 279 } 280 for i := range config.WriterConfigs { 281 if config.WriterConfigs[i].MinLevel == level { 282 config.WriterConfigs[i].w = w 283 } 284 } 285 } 286 } 287 288 func WithWriterType(writeType string, w io.Writer) Option { 289 return func(config *Config) { 290 if !config.WriterEnabled { 291 return 292 } 293 for i := range config.WriterConfigs { 294 if config.WriterConfigs[i].WriteType == writeType { 295 config.WriterConfigs[i].w = w 296 } 297 } 298 } 299 } 300 301 func WithHook(hook func(entry zapcore.Entry) error) Option { 302 return func(config *Config) { 303 config.hooks = append(config.hooks, hook) 304 } 305 } 306 307 func WithNDebug(enabled bool) Option { 308 return func(config *Config) { 309 config.NDebug = enabled 310 } 311 } 312 313 func SetNDebug(enable bool) { 314 ndebugEnabled = enable 315 } 316 317 func IsNDebug() bool { 318 return ndebugEnabled 319 } 320 321 func Configure(config Config, opts ...Option) error { 322 var cores []zapcore.Core 323 var encoder zapcore.Encoder 324 325 for ii := range opts { 326 opts[ii](&config) 327 } 328 SetNDebug(config.NDebug) 329 330 if config.ConsoleEnabled { 331 writer := zapcore.Lock(os.Stdout) 332 encoder = zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig()) 333 cores = append(cores, zapcore.NewCore(encoder, writer, zapcore.Level(config.Level))) 334 } 335 if config.EncoderFormat == "console" { 336 encoder = zapcore.NewConsoleEncoder(zap.NewProductionEncoderConfig()) 337 } else { 338 encoder = jsonEncoder() 339 } 340 341 //可以按级别写入不同的文件 zap.LevelEnablerFunc() 342 if config.FileEnabled { 343 if err := os.MkdirAll(config.FilePath, 0744); err != nil { 344 return fmt.Errorf("can't create log directory, path= '%s'", config.FilePath) 345 } 346 for i := range config.FileConfigs { 347 var fileConfig = config.FileConfigs[i] 348 var levelEnable = zap.LevelEnablerFunc(func(level zapcore.Level) bool { 349 return level < zapcore.Level(fileConfig.MaxLevel) && level >= zapcore.Level(fileConfig.MinLevel) 350 }) 351 fileConfig.Name = path.Join(config.FilePath, fileConfig.Name) 352 w, err := newRollingFile(fileConfig) 353 if err != nil { 354 return err 355 } 356 writer := zapcore.AddSync(w) 357 cores = append(cores, zapcore.NewCore(encoder, writer, levelEnable)) 358 } 359 } 360 if config.WriterEnabled { 361 for i := range config.WriterConfigs { 362 var writerConfig = config.WriterConfigs[i] 363 var levelEnable = zap.LevelEnablerFunc(func(level zapcore.Level) bool { 364 return level < zapcore.Level(writerConfig.MaxLevel) && level >= zapcore.Level(writerConfig.MinLevel) 365 }) 366 if writerConfig.w == nil { 367 continue 368 } 369 writer := zapcore.AddSync(writerConfig.w) 370 cores = append(cores, zapcore.NewCore(encoder, writer, levelEnable)) 371 } 372 } 373 if len(cores) > 0 { 374 lg := zap.New(zapcore.NewTee(cores...), zap.AddCaller()) 375 zap.ReplaceGlobals(lg) 376 } 377 logger = zap.L().WithOptions(zap.AddCallerSkip(1), zap.Hooks(config.hooks...)) 378 return nil 379 } 380 381 func jsonEncoder() zapcore.Encoder { 382 encoderConfig := zap.NewProductionEncoderConfig() 383 encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder 384 encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder 385 encoderConfig.EncodeDuration = zapcore.SecondsDurationEncoder 386 encoderConfig.EncodeCaller = zapcore.ShortCallerEncoder 387 encoderConfig.TimeKey = "time" 388 return zapcore.NewJSONEncoder(encoderConfig) 389 } 390 391 // Context 392 // Deprecated: this function simply calls L. 393 func Context() *zap.Logger { 394 return zap.L() 395 } 396 397 func L() *zap.Logger { 398 return zap.L() 399 } 400 401 func newRollingFile(file FileConfig) (io.Writer, error) { 402 return &lumberjack.Logger{ 403 Filename: file.Name, 404 MaxBackups: file.MaxBackups, // files 405 MaxSize: file.MaxSize, // megabytes 406 MaxAge: file.MaxAge, // days 407 }, nil 408 }