github.com/shuguocloud/go-zero@v1.3.0/core/logx/logs_test.go (about) 1 package logx 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "io" 8 "io/ioutil" 9 "log" 10 "os" 11 "runtime" 12 "strings" 13 "sync" 14 "sync/atomic" 15 "testing" 16 "time" 17 18 "github.com/stretchr/testify/assert" 19 ) 20 21 var ( 22 s = []byte("Sending #11 notification (id: 1451875113812010473) in #1 connection") 23 pool = make(chan []byte, 1) 24 ) 25 26 type mockWriter struct { 27 lock sync.Mutex 28 builder strings.Builder 29 } 30 31 func (mw *mockWriter) Write(data []byte) (int, error) { 32 mw.lock.Lock() 33 defer mw.lock.Unlock() 34 return mw.builder.Write(data) 35 } 36 37 func (mw *mockWriter) Close() error { 38 return nil 39 } 40 41 func (mw *mockWriter) Contains(text string) bool { 42 mw.lock.Lock() 43 defer mw.lock.Unlock() 44 return strings.Contains(mw.builder.String(), text) 45 } 46 47 func (mw *mockWriter) Reset() { 48 mw.lock.Lock() 49 defer mw.lock.Unlock() 50 mw.builder.Reset() 51 } 52 53 func (mw *mockWriter) String() string { 54 mw.lock.Lock() 55 defer mw.lock.Unlock() 56 return mw.builder.String() 57 } 58 59 func TestFileLineFileMode(t *testing.T) { 60 writer := new(mockWriter) 61 errorLog = writer 62 atomic.StoreUint32(&initialized, 1) 63 file, line := getFileLine() 64 Error("anything") 65 assert.True(t, writer.Contains(fmt.Sprintf("%s:%d", file, line+1))) 66 67 writer.Reset() 68 file, line = getFileLine() 69 Errorf("anything %s", "format") 70 assert.True(t, writer.Contains(fmt.Sprintf("%s:%d", file, line+1))) 71 } 72 73 func TestFileLineConsoleMode(t *testing.T) { 74 writer := new(mockWriter) 75 writeConsole = true 76 errorLog = newLogWriter(log.New(writer, "[ERROR] ", flags)) 77 atomic.StoreUint32(&initialized, 1) 78 file, line := getFileLine() 79 Error("anything") 80 assert.True(t, writer.Contains(fmt.Sprintf("%s:%d", file, line+1))) 81 82 writer.Reset() 83 file, line = getFileLine() 84 Errorf("anything %s", "format") 85 assert.True(t, writer.Contains(fmt.Sprintf("%s:%d", file, line+1))) 86 } 87 88 func TestStructedLogAlert(t *testing.T) { 89 doTestStructedLog(t, levelAlert, func(writer io.WriteCloser) { 90 errorLog = writer 91 }, func(v ...interface{}) { 92 Alert(fmt.Sprint(v...)) 93 }) 94 } 95 96 func TestStructedLogError(t *testing.T) { 97 doTestStructedLog(t, levelError, func(writer io.WriteCloser) { 98 errorLog = writer 99 }, func(v ...interface{}) { 100 Error(v...) 101 }) 102 } 103 104 func TestStructedLogErrorf(t *testing.T) { 105 doTestStructedLog(t, levelError, func(writer io.WriteCloser) { 106 errorLog = writer 107 }, func(v ...interface{}) { 108 Errorf("%s", fmt.Sprint(v...)) 109 }) 110 } 111 112 func TestStructedLogErrorv(t *testing.T) { 113 doTestStructedLog(t, levelError, func(writer io.WriteCloser) { 114 errorLog = writer 115 }, func(v ...interface{}) { 116 Errorv(fmt.Sprint(v...)) 117 }) 118 } 119 120 func TestStructedLogInfo(t *testing.T) { 121 doTestStructedLog(t, levelInfo, func(writer io.WriteCloser) { 122 infoLog = writer 123 }, func(v ...interface{}) { 124 Info(v...) 125 }) 126 } 127 128 func TestStructedLogInfof(t *testing.T) { 129 doTestStructedLog(t, levelInfo, func(writer io.WriteCloser) { 130 infoLog = writer 131 }, func(v ...interface{}) { 132 Infof("%s", fmt.Sprint(v...)) 133 }) 134 } 135 136 func TestStructedLogInfov(t *testing.T) { 137 doTestStructedLog(t, levelInfo, func(writer io.WriteCloser) { 138 infoLog = writer 139 }, func(v ...interface{}) { 140 Infov(fmt.Sprint(v...)) 141 }) 142 } 143 144 func TestStructedLogInfoConsoleAny(t *testing.T) { 145 doTestStructedLogConsole(t, func(writer io.WriteCloser) { 146 infoLog = writer 147 }, func(v ...interface{}) { 148 old := encoding 149 encoding = plainEncodingType 150 defer func() { 151 encoding = old 152 }() 153 154 Infov(v) 155 }) 156 } 157 158 func TestStructedLogInfoConsoleAnyString(t *testing.T) { 159 doTestStructedLogConsole(t, func(writer io.WriteCloser) { 160 infoLog = writer 161 }, func(v ...interface{}) { 162 old := encoding 163 encoding = plainEncodingType 164 defer func() { 165 encoding = old 166 }() 167 168 Infov(fmt.Sprint(v...)) 169 }) 170 } 171 172 func TestStructedLogInfoConsoleAnyError(t *testing.T) { 173 doTestStructedLogConsole(t, func(writer io.WriteCloser) { 174 infoLog = writer 175 }, func(v ...interface{}) { 176 old := encoding 177 encoding = plainEncodingType 178 defer func() { 179 encoding = old 180 }() 181 182 Infov(errors.New(fmt.Sprint(v...))) 183 }) 184 } 185 186 func TestStructedLogInfoConsoleAnyStringer(t *testing.T) { 187 doTestStructedLogConsole(t, func(writer io.WriteCloser) { 188 infoLog = writer 189 }, func(v ...interface{}) { 190 old := encoding 191 encoding = plainEncodingType 192 defer func() { 193 encoding = old 194 }() 195 196 Infov(ValStringer{ 197 val: fmt.Sprint(v...), 198 }) 199 }) 200 } 201 202 func TestStructedLogInfoConsoleText(t *testing.T) { 203 doTestStructedLogConsole(t, func(writer io.WriteCloser) { 204 infoLog = writer 205 }, func(v ...interface{}) { 206 old := encoding 207 encoding = plainEncodingType 208 defer func() { 209 encoding = old 210 }() 211 212 Info(fmt.Sprint(v...)) 213 }) 214 } 215 216 func TestStructedLogSlow(t *testing.T) { 217 doTestStructedLog(t, levelSlow, func(writer io.WriteCloser) { 218 slowLog = writer 219 }, func(v ...interface{}) { 220 Slow(v...) 221 }) 222 } 223 224 func TestStructedLogSlowf(t *testing.T) { 225 doTestStructedLog(t, levelSlow, func(writer io.WriteCloser) { 226 slowLog = writer 227 }, func(v ...interface{}) { 228 Slowf(fmt.Sprint(v...)) 229 }) 230 } 231 232 func TestStructedLogSlowv(t *testing.T) { 233 doTestStructedLog(t, levelSlow, func(writer io.WriteCloser) { 234 slowLog = writer 235 }, func(v ...interface{}) { 236 Slowv(fmt.Sprint(v...)) 237 }) 238 } 239 240 func TestStructedLogStat(t *testing.T) { 241 doTestStructedLog(t, levelStat, func(writer io.WriteCloser) { 242 statLog = writer 243 }, func(v ...interface{}) { 244 Stat(v...) 245 }) 246 } 247 248 func TestStructedLogStatf(t *testing.T) { 249 doTestStructedLog(t, levelStat, func(writer io.WriteCloser) { 250 statLog = writer 251 }, func(v ...interface{}) { 252 Statf(fmt.Sprint(v...)) 253 }) 254 } 255 256 func TestStructedLogSevere(t *testing.T) { 257 doTestStructedLog(t, levelSevere, func(writer io.WriteCloser) { 258 severeLog = writer 259 }, func(v ...interface{}) { 260 Severe(v...) 261 }) 262 } 263 264 func TestStructedLogSeveref(t *testing.T) { 265 doTestStructedLog(t, levelSevere, func(writer io.WriteCloser) { 266 severeLog = writer 267 }, func(v ...interface{}) { 268 Severef(fmt.Sprint(v...)) 269 }) 270 } 271 272 func TestStructedLogWithDuration(t *testing.T) { 273 const message = "hello there" 274 writer := new(mockWriter) 275 infoLog = writer 276 atomic.StoreUint32(&initialized, 1) 277 WithDuration(time.Second).Info(message) 278 var entry logEntry 279 if err := json.Unmarshal([]byte(writer.builder.String()), &entry); err != nil { 280 t.Error(err) 281 } 282 assert.Equal(t, levelInfo, entry.Level) 283 assert.Equal(t, message, entry.Content) 284 assert.Equal(t, "1000.0ms", entry.Duration) 285 } 286 287 func TestSetLevel(t *testing.T) { 288 SetLevel(ErrorLevel) 289 const message = "hello there" 290 writer := new(mockWriter) 291 infoLog = writer 292 atomic.StoreUint32(&initialized, 1) 293 Info(message) 294 assert.Equal(t, 0, writer.builder.Len()) 295 } 296 297 func TestSetLevelTwiceWithMode(t *testing.T) { 298 testModes := []string{ 299 "mode", 300 "console", 301 "volumn", 302 } 303 for _, mode := range testModes { 304 testSetLevelTwiceWithMode(t, mode) 305 } 306 } 307 308 func TestSetLevelWithDuration(t *testing.T) { 309 SetLevel(ErrorLevel) 310 const message = "hello there" 311 writer := new(mockWriter) 312 infoLog = writer 313 atomic.StoreUint32(&initialized, 1) 314 WithDuration(time.Second).Info(message) 315 assert.Equal(t, 0, writer.builder.Len()) 316 } 317 318 func TestErrorfWithWrappedError(t *testing.T) { 319 SetLevel(ErrorLevel) 320 const message = "there" 321 writer := new(mockWriter) 322 errorLog = writer 323 atomic.StoreUint32(&initialized, 1) 324 Errorf("hello %w", errors.New(message)) 325 assert.True(t, strings.Contains(writer.builder.String(), "hello there")) 326 } 327 328 func TestMustNil(t *testing.T) { 329 Must(nil) 330 } 331 332 func TestSetup(t *testing.T) { 333 MustSetup(LogConf{ 334 ServiceName: "any", 335 Mode: "console", 336 }) 337 MustSetup(LogConf{ 338 ServiceName: "any", 339 Mode: "file", 340 Path: os.TempDir(), 341 }) 342 MustSetup(LogConf{ 343 ServiceName: "any", 344 Mode: "volume", 345 Path: os.TempDir(), 346 }) 347 assert.NotNil(t, setupWithVolume(LogConf{})) 348 assert.NotNil(t, setupWithFiles(LogConf{})) 349 assert.Nil(t, setupWithFiles(LogConf{ 350 ServiceName: "any", 351 Path: os.TempDir(), 352 Compress: true, 353 KeepDays: 1, 354 })) 355 setupLogLevel(LogConf{ 356 Level: levelInfo, 357 }) 358 setupLogLevel(LogConf{ 359 Level: levelError, 360 }) 361 setupLogLevel(LogConf{ 362 Level: levelSevere, 363 }) 364 _, err := createOutput("") 365 assert.NotNil(t, err) 366 Disable() 367 } 368 369 func TestDisable(t *testing.T) { 370 Disable() 371 372 var opt logOptions 373 WithKeepDays(1)(&opt) 374 WithGzip()(&opt) 375 assert.Nil(t, Close()) 376 writeConsole = false 377 assert.Nil(t, Close()) 378 } 379 380 func TestDisableStat(t *testing.T) { 381 DisableStat() 382 383 const message = "hello there" 384 writer := new(mockWriter) 385 statLog = writer 386 atomic.StoreUint32(&initialized, 1) 387 Stat(message) 388 assert.Equal(t, 0, writer.builder.Len()) 389 } 390 391 func TestWithGzip(t *testing.T) { 392 fn := WithGzip() 393 var opt logOptions 394 fn(&opt) 395 assert.True(t, opt.gzipEnabled) 396 } 397 398 func TestWithKeepDays(t *testing.T) { 399 fn := WithKeepDays(1) 400 var opt logOptions 401 fn(&opt) 402 assert.Equal(t, 1, opt.keepDays) 403 } 404 405 func BenchmarkCopyByteSliceAppend(b *testing.B) { 406 for i := 0; i < b.N; i++ { 407 var buf []byte 408 buf = append(buf, getTimestamp()...) 409 buf = append(buf, ' ') 410 buf = append(buf, s...) 411 _ = buf 412 } 413 } 414 415 func BenchmarkCopyByteSliceAllocExactly(b *testing.B) { 416 for i := 0; i < b.N; i++ { 417 now := []byte(getTimestamp()) 418 buf := make([]byte, len(now)+1+len(s)) 419 n := copy(buf, now) 420 buf[n] = ' ' 421 copy(buf[n+1:], s) 422 } 423 } 424 425 func BenchmarkCopyByteSlice(b *testing.B) { 426 var buf []byte 427 for i := 0; i < b.N; i++ { 428 buf = make([]byte, len(s)) 429 copy(buf, s) 430 } 431 fmt.Fprint(ioutil.Discard, buf) 432 } 433 434 func BenchmarkCopyOnWriteByteSlice(b *testing.B) { 435 var buf []byte 436 for i := 0; i < b.N; i++ { 437 size := len(s) 438 buf = s[:size:size] 439 } 440 fmt.Fprint(ioutil.Discard, buf) 441 } 442 443 func BenchmarkCacheByteSlice(b *testing.B) { 444 for i := 0; i < b.N; i++ { 445 dup := fetch() 446 copy(dup, s) 447 put(dup) 448 } 449 } 450 451 func BenchmarkLogs(b *testing.B) { 452 b.ReportAllocs() 453 454 log.SetOutput(ioutil.Discard) 455 for i := 0; i < b.N; i++ { 456 Info(i) 457 } 458 } 459 460 func fetch() []byte { 461 select { 462 case b := <-pool: 463 return b 464 default: 465 } 466 return make([]byte, 4096) 467 } 468 469 func getFileLine() (string, int) { 470 _, file, line, _ := runtime.Caller(1) 471 short := file 472 473 for i := len(file) - 1; i > 0; i-- { 474 if file[i] == '/' { 475 short = file[i+1:] 476 break 477 } 478 } 479 480 return short, line 481 } 482 483 func put(b []byte) { 484 select { 485 case pool <- b: 486 default: 487 } 488 } 489 490 func doTestStructedLog(t *testing.T, level string, setup func(writer io.WriteCloser), 491 write func(...interface{})) { 492 const message = "hello there" 493 writer := new(mockWriter) 494 setup(writer) 495 atomic.StoreUint32(&initialized, 1) 496 write(message) 497 var entry logEntry 498 if err := json.Unmarshal([]byte(writer.builder.String()), &entry); err != nil { 499 t.Error(err) 500 } 501 assert.Equal(t, level, entry.Level) 502 val, ok := entry.Content.(string) 503 assert.True(t, ok) 504 assert.True(t, strings.Contains(val, message)) 505 } 506 507 func doTestStructedLogConsole(t *testing.T, setup func(writer io.WriteCloser), 508 write func(...interface{})) { 509 const message = "hello there" 510 writer := new(mockWriter) 511 setup(writer) 512 atomic.StoreUint32(&initialized, 1) 513 write(message) 514 println(writer.String()) 515 assert.True(t, strings.Contains(writer.String(), message)) 516 } 517 518 func testSetLevelTwiceWithMode(t *testing.T, mode string) { 519 SetUp(LogConf{ 520 Mode: mode, 521 Level: "error", 522 Path: "/dev/null", 523 }) 524 SetUp(LogConf{ 525 Mode: mode, 526 Level: "info", 527 Path: "/dev/null", 528 }) 529 const message = "hello there" 530 writer := new(mockWriter) 531 infoLog = writer 532 atomic.StoreUint32(&initialized, 1) 533 Info(message) 534 assert.Equal(t, 0, writer.builder.Len()) 535 Infof(message) 536 assert.Equal(t, 0, writer.builder.Len()) 537 ErrorStack(message) 538 assert.Equal(t, 0, writer.builder.Len()) 539 ErrorStackf(message) 540 assert.Equal(t, 0, writer.builder.Len()) 541 } 542 543 type ValStringer struct { 544 val string 545 } 546 547 func (v ValStringer) String() string { 548 return v.val 549 }