trpc.group/trpc-go/trpc-go@v1.0.3/log/zaplogger.go (about) 1 // 2 // 3 // Tencent is pleased to support the open source community by making tRPC available. 4 // 5 // Copyright (C) 2023 THL A29 Limited, a Tencent company. 6 // All rights reserved. 7 // 8 // If you have downloaded a copy of the tRPC source code from Tencent, 9 // please note that tRPC source code is licensed under the Apache 2.0 License, 10 // A copy of the Apache 2.0 License is included in this file. 11 // 12 // 13 14 package log 15 16 import ( 17 "fmt" 18 "os" 19 "strconv" 20 "time" 21 22 "trpc.group/trpc-go/trpc-go/internal/report" 23 "trpc.group/trpc-go/trpc-go/log/rollwriter" 24 25 "go.uber.org/zap" 26 "go.uber.org/zap/zapcore" 27 ) 28 29 var defaultConfig = []OutputConfig{ 30 { 31 Writer: "console", 32 Level: "debug", 33 Formatter: "console", 34 }, 35 } 36 37 // Some ZapCore constants. 38 const ( 39 ConsoleZapCore = "console" 40 FileZapCore = "file" 41 ) 42 43 // Levels is the map from string to zapcore.Level. 44 var Levels = map[string]zapcore.Level{ 45 "": zapcore.DebugLevel, 46 "trace": zapcore.DebugLevel, 47 "debug": zapcore.DebugLevel, 48 "info": zapcore.InfoLevel, 49 "warn": zapcore.WarnLevel, 50 "error": zapcore.ErrorLevel, 51 "fatal": zapcore.FatalLevel, 52 } 53 54 var levelToZapLevel = map[Level]zapcore.Level{ 55 LevelTrace: zapcore.DebugLevel, 56 LevelDebug: zapcore.DebugLevel, 57 LevelInfo: zapcore.InfoLevel, 58 LevelWarn: zapcore.WarnLevel, 59 LevelError: zapcore.ErrorLevel, 60 LevelFatal: zapcore.FatalLevel, 61 } 62 63 var zapLevelToLevel = map[zapcore.Level]Level{ 64 zapcore.DebugLevel: LevelDebug, 65 zapcore.InfoLevel: LevelInfo, 66 zapcore.WarnLevel: LevelWarn, 67 zapcore.ErrorLevel: LevelError, 68 zapcore.FatalLevel: LevelFatal, 69 } 70 71 // NewZapLog creates a trpc default Logger from zap whose caller skip is set to 2. 72 func NewZapLog(c Config) Logger { 73 return NewZapLogWithCallerSkip(c, 2) 74 } 75 76 // NewZapLogWithCallerSkip creates a trpc default Logger from zap. 77 func NewZapLogWithCallerSkip(cfg Config, callerSkip int) Logger { 78 var ( 79 cores []zapcore.Core 80 levels []zap.AtomicLevel 81 ) 82 for _, c := range cfg { 83 writer := GetWriter(c.Writer) 84 if writer == nil { 85 panic("log: writer core: " + c.Writer + " no registered") 86 } 87 decoder := &Decoder{OutputConfig: &c} 88 if err := writer.Setup(c.Writer, decoder); err != nil { 89 panic("log: writer core: " + c.Writer + " setup fail: " + err.Error()) 90 } 91 cores = append(cores, decoder.Core) 92 levels = append(levels, decoder.ZapLevel) 93 } 94 return &zapLog{ 95 levels: levels, 96 logger: zap.New( 97 zapcore.NewTee(cores...), 98 zap.AddCallerSkip(callerSkip), 99 zap.AddCaller(), 100 ), 101 } 102 } 103 104 func newEncoder(c *OutputConfig) zapcore.Encoder { 105 encoderCfg := zapcore.EncoderConfig{ 106 TimeKey: GetLogEncoderKey("T", c.FormatConfig.TimeKey), 107 LevelKey: GetLogEncoderKey("L", c.FormatConfig.LevelKey), 108 NameKey: GetLogEncoderKey("N", c.FormatConfig.NameKey), 109 CallerKey: GetLogEncoderKey("C", c.FormatConfig.CallerKey), 110 FunctionKey: GetLogEncoderKey(zapcore.OmitKey, c.FormatConfig.FunctionKey), 111 MessageKey: GetLogEncoderKey("M", c.FormatConfig.MessageKey), 112 StacktraceKey: GetLogEncoderKey("S", c.FormatConfig.StacktraceKey), 113 LineEnding: zapcore.DefaultLineEnding, 114 EncodeLevel: zapcore.CapitalLevelEncoder, 115 EncodeTime: NewTimeEncoder(c.FormatConfig.TimeFmt), 116 EncodeDuration: zapcore.StringDurationEncoder, 117 EncodeCaller: zapcore.ShortCallerEncoder, 118 } 119 if c.EnableColor { 120 encoderCfg.EncodeLevel = zapcore.CapitalColorLevelEncoder 121 } 122 if newFormatEncoder, ok := formatEncoders[c.Formatter]; ok { 123 return newFormatEncoder(encoderCfg) 124 } 125 // Defaults to console encoder. 126 return zapcore.NewConsoleEncoder(encoderCfg) 127 } 128 129 var formatEncoders = map[string]NewFormatEncoder{ 130 "console": zapcore.NewConsoleEncoder, 131 "json": zapcore.NewJSONEncoder, 132 } 133 134 // NewFormatEncoder is the function type for creating a format encoder out of an encoder config. 135 type NewFormatEncoder func(zapcore.EncoderConfig) zapcore.Encoder 136 137 // RegisterFormatEncoder registers a NewFormatEncoder with the specified formatName key. 138 // The existing formats include "console" and "json", but you can override these format encoders 139 // or provide a new custom one. 140 func RegisterFormatEncoder(formatName string, newFormatEncoder NewFormatEncoder) { 141 formatEncoders[formatName] = newFormatEncoder 142 } 143 144 // GetLogEncoderKey gets user defined log output name, uses defKey if empty. 145 func GetLogEncoderKey(defKey, key string) string { 146 if key == "" { 147 return defKey 148 } 149 return key 150 } 151 152 func newConsoleCore(c *OutputConfig) (zapcore.Core, zap.AtomicLevel) { 153 lvl := zap.NewAtomicLevelAt(Levels[c.Level]) 154 return zapcore.NewCore( 155 newEncoder(c), 156 zapcore.Lock(os.Stdout), 157 lvl), lvl 158 } 159 160 func newFileCore(c *OutputConfig) (zapcore.Core, zap.AtomicLevel, error) { 161 opts := []rollwriter.Option{ 162 rollwriter.WithMaxAge(c.WriteConfig.MaxAge), 163 rollwriter.WithMaxBackups(c.WriteConfig.MaxBackups), 164 rollwriter.WithCompress(c.WriteConfig.Compress), 165 rollwriter.WithMaxSize(c.WriteConfig.MaxSize), 166 } 167 // roll by time. 168 if c.WriteConfig.RollType != RollBySize { 169 opts = append(opts, rollwriter.WithRotationTime(c.WriteConfig.TimeUnit.Format())) 170 } 171 writer, err := rollwriter.NewRollWriter(c.WriteConfig.Filename, opts...) 172 if err != nil { 173 return nil, zap.AtomicLevel{}, err 174 } 175 176 // write mode. 177 var ws zapcore.WriteSyncer 178 switch m := c.WriteConfig.WriteMode; m { 179 case 0, WriteFast: 180 // Use WriteFast as default mode. 181 // It has better performance, discards logs on full and avoid blocking service. 182 ws = rollwriter.NewAsyncRollWriter(writer, rollwriter.WithDropLog(true)) 183 case WriteSync: 184 ws = zapcore.AddSync(writer) 185 case WriteAsync: 186 ws = rollwriter.NewAsyncRollWriter(writer, rollwriter.WithDropLog(false)) 187 default: 188 return nil, zap.AtomicLevel{}, fmt.Errorf("validating WriteMode parameter: got %d, "+ 189 "but expect one of WriteFast(%d), WriteAsync(%d), or WriteSync(%d)", m, WriteFast, WriteAsync, WriteSync) 190 } 191 192 // log level. 193 lvl := zap.NewAtomicLevelAt(Levels[c.Level]) 194 return zapcore.NewCore( 195 newEncoder(c), 196 ws, lvl, 197 ), lvl, nil 198 } 199 200 // NewTimeEncoder creates a time format encoder. 201 func NewTimeEncoder(format string) zapcore.TimeEncoder { 202 switch format { 203 case "": 204 return func(t time.Time, enc zapcore.PrimitiveArrayEncoder) { 205 enc.AppendByteString(defaultTimeFormat(t)) 206 } 207 case "seconds": 208 return zapcore.EpochTimeEncoder 209 case "milliseconds": 210 return zapcore.EpochMillisTimeEncoder 211 case "nanoseconds": 212 return zapcore.EpochNanosTimeEncoder 213 default: 214 return func(t time.Time, enc zapcore.PrimitiveArrayEncoder) { 215 enc.AppendString(t.Format(format)) 216 } 217 } 218 } 219 220 // defaultTimeFormat returns the default time format "2006-01-02 15:04:05.000", 221 // which performs better than https://pkg.go.dev/time#Time.AppendFormat. 222 func defaultTimeFormat(t time.Time) []byte { 223 t = t.Local() 224 year, month, day := t.Date() 225 hour, minute, second := t.Clock() 226 micros := t.Nanosecond() / 1000 227 228 buf := make([]byte, 23) 229 buf[0] = byte((year/1000)%10) + '0' 230 buf[1] = byte((year/100)%10) + '0' 231 buf[2] = byte((year/10)%10) + '0' 232 buf[3] = byte(year%10) + '0' 233 buf[4] = '-' 234 buf[5] = byte((month)/10) + '0' 235 buf[6] = byte((month)%10) + '0' 236 buf[7] = '-' 237 buf[8] = byte((day)/10) + '0' 238 buf[9] = byte((day)%10) + '0' 239 buf[10] = ' ' 240 buf[11] = byte((hour)/10) + '0' 241 buf[12] = byte((hour)%10) + '0' 242 buf[13] = ':' 243 buf[14] = byte((minute)/10) + '0' 244 buf[15] = byte((minute)%10) + '0' 245 buf[16] = ':' 246 buf[17] = byte((second)/10) + '0' 247 buf[18] = byte((second)%10) + '0' 248 buf[19] = '.' 249 buf[20] = byte((micros/100000)%10) + '0' 250 buf[21] = byte((micros/10000)%10) + '0' 251 buf[22] = byte((micros/1000)%10) + '0' 252 return buf 253 } 254 255 // zapLog is a Logger implementation based on zaplogger. 256 type zapLog struct { 257 levels []zap.AtomicLevel 258 logger *zap.Logger 259 } 260 261 func (l *zapLog) WithOptions(opts ...Option) Logger { 262 o := &options{} 263 for _, opt := range opts { 264 opt(o) 265 } 266 return &zapLog{ 267 levels: l.levels, 268 logger: l.logger.WithOptions(zap.AddCallerSkip(o.skip)), 269 } 270 } 271 272 // With add user defined fields to Logger. Fields support multiple values. 273 func (l *zapLog) With(fields ...Field) Logger { 274 zapFields := make([]zap.Field, len(fields)) 275 for i := range fields { 276 zapFields[i] = zap.Any(fields[i].Key, fields[i].Value) 277 } 278 279 return &zapLog{ 280 levels: l.levels, 281 logger: l.logger.With(zapFields...)} 282 } 283 284 func getLogMsg(args ...interface{}) string { 285 msg := fmt.Sprintln(args...) 286 msg = msg[:len(msg)-1] 287 report.LogWriteSize.IncrBy(float64(len(msg))) 288 return msg 289 } 290 291 func getLogMsgf(format string, args ...interface{}) string { 292 msg := fmt.Sprintf(format, args...) 293 report.LogWriteSize.IncrBy(float64(len(msg))) 294 return msg 295 } 296 297 // Trace logs to TRACE log. Arguments are handled in the manner of fmt.Println. 298 func (l *zapLog) Trace(args ...interface{}) { 299 if l.logger.Core().Enabled(zapcore.DebugLevel) { 300 l.logger.Debug(getLogMsg(args...)) 301 } 302 } 303 304 // Tracef logs to TRACE log. Arguments are handled in the manner of fmt.Printf. 305 func (l *zapLog) Tracef(format string, args ...interface{}) { 306 if l.logger.Core().Enabled(zapcore.DebugLevel) { 307 l.logger.Debug(getLogMsgf(format, args...)) 308 } 309 } 310 311 // Debug logs to DEBUG log. Arguments are handled in the manner of fmt.Println. 312 func (l *zapLog) Debug(args ...interface{}) { 313 if l.logger.Core().Enabled(zapcore.DebugLevel) { 314 l.logger.Debug(getLogMsg(args...)) 315 } 316 } 317 318 // Debugf logs to DEBUG log. Arguments are handled in the manner of fmt.Printf. 319 func (l *zapLog) Debugf(format string, args ...interface{}) { 320 if l.logger.Core().Enabled(zapcore.DebugLevel) { 321 l.logger.Debug(getLogMsgf(format, args...)) 322 } 323 } 324 325 // Info logs to INFO log. Arguments are handled in the manner of fmt.Println. 326 func (l *zapLog) Info(args ...interface{}) { 327 if l.logger.Core().Enabled(zapcore.InfoLevel) { 328 l.logger.Info(getLogMsg(args...)) 329 } 330 } 331 332 // Infof logs to INFO log. Arguments are handled in the manner of fmt.Printf. 333 func (l *zapLog) Infof(format string, args ...interface{}) { 334 if l.logger.Core().Enabled(zapcore.InfoLevel) { 335 l.logger.Info(getLogMsgf(format, args...)) 336 } 337 } 338 339 // Warn logs to WARNING log. Arguments are handled in the manner of fmt.Println. 340 func (l *zapLog) Warn(args ...interface{}) { 341 if l.logger.Core().Enabled(zapcore.WarnLevel) { 342 l.logger.Warn(getLogMsg(args...)) 343 } 344 } 345 346 // Warnf logs to WARNING log. Arguments are handled in the manner of fmt.Printf. 347 func (l *zapLog) Warnf(format string, args ...interface{}) { 348 if l.logger.Core().Enabled(zapcore.WarnLevel) { 349 l.logger.Warn(getLogMsgf(format, args...)) 350 } 351 } 352 353 // Error logs to ERROR log. Arguments are handled in the manner of fmt.Println. 354 func (l *zapLog) Error(args ...interface{}) { 355 if l.logger.Core().Enabled(zapcore.ErrorLevel) { 356 l.logger.Error(getLogMsg(args...)) 357 } 358 } 359 360 // Errorf logs to ERROR log. Arguments are handled in the manner of fmt.Printf. 361 func (l *zapLog) Errorf(format string, args ...interface{}) { 362 if l.logger.Core().Enabled(zapcore.ErrorLevel) { 363 l.logger.Error(getLogMsgf(format, args...)) 364 } 365 } 366 367 // Fatal logs to FATAL log. Arguments are handled in the manner of fmt.Println. 368 func (l *zapLog) Fatal(args ...interface{}) { 369 if l.logger.Core().Enabled(zapcore.FatalLevel) { 370 l.logger.Fatal(getLogMsg(args...)) 371 } 372 } 373 374 // Fatalf logs to FATAL log. Arguments are handled in the manner of fmt.Printf. 375 func (l *zapLog) Fatalf(format string, args ...interface{}) { 376 if l.logger.Core().Enabled(zapcore.FatalLevel) { 377 l.logger.Fatal(getLogMsgf(format, args...)) 378 } 379 } 380 381 // Sync calls the zap logger's Sync method, and flushes any buffered log entries. 382 // Applications should take care to call Sync before exiting. 383 func (l *zapLog) Sync() error { 384 return l.logger.Sync() 385 } 386 387 // SetLevel sets output log level. 388 func (l *zapLog) SetLevel(output string, level Level) { 389 i, e := strconv.Atoi(output) 390 if e != nil { 391 return 392 } 393 if i < 0 || i >= len(l.levels) { 394 return 395 } 396 l.levels[i].SetLevel(levelToZapLevel[level]) 397 } 398 399 // GetLevel gets output log level. 400 func (l *zapLog) GetLevel(output string) Level { 401 i, e := strconv.Atoi(output) 402 if e != nil { 403 return LevelDebug 404 } 405 if i < 0 || i >= len(l.levels) { 406 return LevelDebug 407 } 408 return zapLevelToLevel[l.levels[i].Level()] 409 } 410 411 // CustomTimeFormat customize time format. 412 // Deprecated: Use https://pkg.go.dev/time#Time.Format instead. 413 func CustomTimeFormat(t time.Time, format string) string { 414 return t.Format(format) 415 } 416 417 // DefaultTimeFormat returns the default time format "2006-01-02 15:04:05.000". 418 // Deprecated: Use https://pkg.go.dev/time#Time.AppendFormat instead. 419 func DefaultTimeFormat(t time.Time) []byte { 420 return defaultTimeFormat(t) 421 }