trpc.group/trpc-go/trpc-go@v1.0.3/log/zaplogger_test.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_test 15 16 import ( 17 "errors" 18 "fmt" 19 "runtime" 20 "strings" 21 "testing" 22 "time" 23 24 "github.com/stretchr/testify/assert" 25 "github.com/stretchr/testify/require" 26 "go.uber.org/zap" 27 "go.uber.org/zap/buffer" 28 "go.uber.org/zap/zapcore" 29 "go.uber.org/zap/zaptest/observer" 30 31 "trpc.group/trpc-go/trpc-go/log" 32 "trpc.group/trpc-go/trpc-go/plugin" 33 ) 34 35 func TestNewZapLog(t *testing.T) { 36 logger := log.NewZapLog(defaultConfig) 37 assert.NotNil(t, logger) 38 39 logger.SetLevel("0", log.LevelInfo) 40 lvl := logger.GetLevel("0") 41 assert.Equal(t, lvl, log.LevelInfo) 42 43 l := logger.With(log.Field{Key: "test", Value: "a"}) 44 l.SetLevel("output", log.LevelDebug) 45 assert.Equal(t, log.LevelDebug, l.GetLevel("output")) 46 } 47 48 func TestNewZapLog_WriteMode(t *testing.T) { 49 logDir := t.TempDir() 50 t.Run("invalid write mode", func(t *testing.T) { 51 const invalidWriteMode = 4 52 require.Panics(t, func() { 53 log.NewZapLog([]log.OutputConfig{{ 54 Writer: log.OutputFile, 55 WriteConfig: log.WriteConfig{ 56 LogPath: logDir, 57 Filename: "trpc.log", 58 WriteMode: invalidWriteMode, 59 }, 60 }}) 61 }) 62 }) 63 t.Run("valid write mode", func(t *testing.T) { 64 const ( 65 syncFileName = "trpc.syncLog" 66 asyncFileName = "trpc.asyncLog" 67 fastFileName = "trpc.fastLog" 68 ) 69 tests := []struct { 70 name string 71 config log.OutputConfig 72 }{ 73 {"sync", log.OutputConfig{ 74 Writer: log.OutputFile, 75 WriteConfig: log.WriteConfig{ 76 LogPath: logDir, 77 Filename: syncFileName, 78 WriteMode: log.WriteSync, 79 }, 80 }}, 81 {"async", log.OutputConfig{ 82 Writer: log.OutputFile, 83 WriteConfig: log.WriteConfig{ 84 LogPath: logDir, 85 Filename: asyncFileName, 86 WriteMode: log.WriteAsync, 87 }, 88 }}, 89 {"fast", log.OutputConfig{ 90 Writer: log.OutputFile, 91 WriteConfig: log.WriteConfig{ 92 LogPath: logDir, 93 Filename: fastFileName, 94 WriteMode: log.WriteFast, 95 }, 96 }}, 97 } 98 for _, tt := range tests { 99 t.Run(tt.name, func(t *testing.T) { 100 require.NotNil(t, log.NewZapLog([]log.OutputConfig{tt.config})) 101 }) 102 } 103 }) 104 } 105 106 func TestZapLogWithLevel(t *testing.T) { 107 logger := log.NewZapLog(defaultConfig) 108 assert.NotNil(t, logger) 109 110 l := logger.With(log.Field{Key: "test", Value: "a"}) 111 l.SetLevel("0", log.LevelFatal) 112 assert.Equal(t, log.LevelFatal, l.GetLevel("0")) 113 114 l = l.With(log.Field{Key: "key1", Value: "val1"}) 115 l.SetLevel("0", log.LevelError) 116 assert.Equal(t, log.LevelError, l.GetLevel("0")) 117 } 118 119 func BenchmarkDefaultTimeFormat(b *testing.B) { 120 t := time.Now() 121 for i := 0; i < b.N; i++ { 122 log.DefaultTimeFormat(t) 123 } 124 } 125 126 func BenchmarkCustomTimeFormat(b *testing.B) { 127 t := time.Now() 128 for i := 0; i < b.N; i++ { 129 log.CustomTimeFormat(t, "2006-01-02 15:04:05.000") 130 } 131 } 132 133 func TestCustomTimeFormat(t *testing.T) { 134 date := time.Date(2006, 1, 2, 15, 4, 5, 0, time.Local) 135 dateStr := log.CustomTimeFormat(date, "2006-01-02 15:04:05.000") 136 assert.Equal(t, dateStr, "2006-01-02 15:04:05.000") 137 } 138 139 func TestDefaultTimeFormat(t *testing.T) { 140 date := time.Date(2006, 1, 2, 15, 4, 5, 0, time.Local) 141 dateStr := string(log.DefaultTimeFormat(date)) 142 assert.Equal(t, dateStr, "2006-01-02 15:04:05.000") 143 } 144 145 func TestGetLogEncoderKey(t *testing.T) { 146 tests := []struct { 147 name string 148 defKey string 149 key string 150 want string 151 }{ 152 {"custom", "T", "Time", "Time"}, 153 {"default", "T", "", "T"}, 154 } 155 156 for _, tt := range tests { 157 t.Run(tt.name, func(t *testing.T) { 158 if got := log.GetLogEncoderKey(tt.defKey, tt.key); got != tt.want { 159 assert.Equal(t, got, tt.want) 160 } 161 }) 162 } 163 } 164 165 func TestNewTimeEncoder(t *testing.T) { 166 encoder := log.NewTimeEncoder("") 167 assert.NotNil(t, encoder) 168 169 encoder = log.NewTimeEncoder("2006-01-02 15:04:05") 170 assert.NotNil(t, encoder) 171 172 tests := []struct { 173 name string 174 fmt string 175 }{ 176 {"seconds timestamp", "seconds"}, 177 {"milliseconds timestamp", "milliseconds"}, 178 {"nanoseconds timestamp", "nanoseconds"}, 179 } 180 181 for _, tt := range tests { 182 t.Run(tt.name, func(t *testing.T) { 183 got := log.NewTimeEncoder(tt.fmt) 184 assert.NotNil(t, got) 185 }) 186 } 187 } 188 189 func TestWithFields(t *testing.T) { 190 // register Writer. 191 // use zap observer to support test. 192 core, ob := observer.New(zap.InfoLevel) 193 log.RegisterWriter(observewriter, &observeWriter{core: core}) 194 195 // config is configuration. 196 cfg := []log.OutputConfig{ 197 { 198 Writer: observewriter, 199 }, 200 } 201 202 // create a zap logger. 203 zl := log.NewZapLog(cfg) 204 assert.NotNil(t, zl) 205 206 // test With. 207 field := log.Field{Key: "abc", Value: int32(123)} 208 logger := zl.With(field) 209 assert.NotNil(t, logger) 210 log.SetLogger(logger) 211 log.Warn("with fields warning") 212 assert.Equal(t, 1, ob.Len()) 213 entry := ob.All()[0] 214 assert.Equal(t, zap.WarnLevel, entry.Level) 215 assert.Equal(t, "with fields warning", entry.Message) 216 assert.Equal(t, []zapcore.Field{{Key: "abc", Type: zapcore.Int32Type, Integer: 123}}, entry.Context) 217 } 218 219 func TestOptionLogger2(t *testing.T) { 220 t.Run("test option logger add caller skip", func(t *testing.T) { 221 core, ob := observer.New(zap.InfoLevel) 222 log.RegisterWriter(observewriter, &observeWriter{core: core}) 223 cfg := []log.OutputConfig{{Writer: observewriter}} 224 225 l := log.NewZapLogWithCallerSkip(cfg, 1) 226 l.Info("this is option logger test, the current caller skip is correct") 227 228 _, file, _, ok := runtime.Caller(0) 229 require.True(t, ok) 230 require.Equal(t, file, ob.All()[0].Caller.File) 231 232 ol, ok := l.(log.OptionLogger) 233 require.True(t, ok) 234 l = ol.WithOptions(log.WithAdditionalCallerSkip(1)) 235 l.Info("this is option logger test, the current caller skip is incorrect(added 1)") 236 237 _, file, _, ok = runtime.Caller(1) 238 require.True(t, ok) 239 require.Equal(t, file, ob.All()[1].Caller.File) 240 }) 241 t.Run("test option logger wrapper add caller skip", func(t *testing.T) { 242 core, ob := observer.New(zap.InfoLevel) 243 log.RegisterWriter(observewriter, &observeWriter{core: core}) 244 cfg := []log.OutputConfig{{Writer: observewriter}} 245 246 l := log.NewZapLogWithCallerSkip(cfg, 1) 247 l = l.With(log.Field{Key: "k", Value: "v"}) 248 l.Info("this is option logger wrapper test, the current caller skip is correct") 249 250 _, file, _, ok := runtime.Caller(0) 251 require.True(t, ok) 252 require.Equal(t, file, ob.All()[0].Caller.File) 253 254 ol, ok := l.(log.OptionLogger) 255 require.True(t, ok) 256 l = ol.WithOptions(log.WithAdditionalCallerSkip(1)) 257 l.Info("this is option logger wrapper test, the current caller skip is incorrect(added 1)") 258 259 _, file, _, ok = runtime.Caller(1) 260 require.True(t, ok) 261 require.Equal(t, file, ob.All()[1].Caller.File) 262 }) 263 } 264 265 const observewriter = "observewriter" 266 267 type observeWriter struct { 268 core zapcore.Core 269 } 270 271 func (f *observeWriter) Type() string { return "log" } 272 273 func (f *observeWriter) Setup(name string, dec plugin.Decoder) error { 274 if dec == nil { 275 return errors.New("empty decoder") 276 } 277 decoder, ok := dec.(*log.Decoder) 278 if !ok { 279 return errors.New("invalid decoder") 280 } 281 decoder.Core = f.core 282 decoder.ZapLevel = zap.NewAtomicLevel() 283 return nil 284 } 285 286 func TestLogLevel(t *testing.T) { 287 config := []log.OutputConfig{ 288 { 289 Writer: "console", 290 Level: "", 291 }, 292 { 293 Writer: "console", 294 Level: "trace", 295 }, 296 { 297 Writer: "console", 298 Level: "debug", 299 }, 300 { 301 Writer: "console", 302 Level: "info", 303 }, 304 { 305 Writer: "console", 306 Level: "warn", 307 }, 308 { 309 Writer: "console", 310 Level: "error", 311 }, 312 { 313 Writer: "console", 314 Level: "fatal", 315 }, 316 } 317 l := log.NewZapLog(config) 318 319 var ( 320 got []string 321 want []string 322 ) 323 for i, c := range config { 324 got = append(got, log.LevelStrings[l.GetLevel(fmt.Sprint(i))]) 325 want = append(want, log.Levels[c.Level].String()) 326 } 327 require.Equal(t, want, got) 328 } 329 330 func TestLogEnableColor(t *testing.T) { 331 cfg := []log.OutputConfig{{Writer: "console", Level: "trace", EnableColor: true}} 332 l := log.NewZapLog(cfg) 333 l.Trace("hello") 334 l.Debug("hello") 335 l.Info("hello") 336 l.Warn("hello") 337 l.Error("hello") 338 } 339 340 func TestLogNewFormatEncoder(t *testing.T) { 341 const myFormatter = "myformatter" 342 log.RegisterFormatEncoder(myFormatter, func(ec zapcore.EncoderConfig) zapcore.Encoder { 343 return &consoleEncoder{ 344 Encoder: zapcore.NewJSONEncoder(zapcore.EncoderConfig{}), 345 pool: buffer.NewPool(), 346 cfg: ec, 347 } 348 }) 349 cfg := []log.OutputConfig{{Writer: "console", Level: "trace", Formatter: myFormatter}} 350 l := log.NewZapLog(cfg).With(log.Field{Key: "trace-id", Value: "xx"}) 351 l.Trace("hello") 352 l.Debug("hello") 353 l.Info("hello") 354 l.Warn("hello") 355 l.Error("hello") 356 // 2023/12/14 10:54:55 {"trace-id":"xx"} DEBUG hello 357 // 2023/12/14 10:54:55 {"trace-id":"xx"} DEBUG hello 358 // 2023/12/14 10:54:55 {"trace-id":"xx"} INFO hello 359 // 2023/12/14 10:54:55 {"trace-id":"xx"} WARN hello 360 // 2023/12/14 10:54:55 {"trace-id":"xx"} ERROR hello 361 } 362 363 type consoleEncoder struct { 364 zapcore.Encoder 365 pool buffer.Pool 366 cfg zapcore.EncoderConfig 367 } 368 369 func (c consoleEncoder) Clone() zapcore.Encoder { 370 return consoleEncoder{Encoder: c.Encoder.Clone(), pool: buffer.NewPool(), cfg: c.cfg} 371 } 372 373 func (c consoleEncoder) EncodeEntry(entry zapcore.Entry, fields []zapcore.Field) (*buffer.Buffer, error) { 374 buf, err := c.Encoder.EncodeEntry(zapcore.Entry{}, nil) 375 if err != nil { 376 return nil, err 377 } 378 buffer := c.pool.Get() 379 buffer.AppendString(entry.Time.Format("2006/01/02 15:04:05")) 380 field := buf.String() 381 buffer.AppendString(" " + field[:len(field)-1] + " ") 382 buffer.AppendString(strings.ToUpper(entry.Level.String()) + " ") 383 buffer.AppendString(entry.Message + "\n") 384 return buffer, nil 385 }