github.com/alibaba/ilogtail/pkg@v0.0.0-20250526110833-c53b480d046c/logger/logger_test.go (about) 1 // Copyright 2021 iLogtail Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package logger 16 17 import ( 18 "context" 19 "flag" 20 "fmt" 21 "os" 22 "path" 23 "regexp" 24 "strings" 25 "sync" 26 "testing" 27 "time" 28 29 "github.com/cihub/seelog" 30 "github.com/stretchr/testify/assert" 31 32 "github.com/alibaba/ilogtail/pkg" 33 "github.com/alibaba/ilogtail/pkg/config" 34 "github.com/alibaba/ilogtail/pkg/util" 35 ) 36 37 const ( 38 testProject = "mock-project" 39 testLogstore = "mock-logstore" 40 testConfigName = "mock-configname" 41 ) 42 43 var ctx context.Context 44 var mu sync.Mutex 45 46 func init() { 47 ctx, _ = pkg.NewLogtailContextMeta(testProject, testLogstore, testConfigName) 48 } 49 50 func clean() { 51 _ = os.Remove(path.Join(config.LoongcollectorGlobalConfig.LoongCollectorLogConfDir, "plugin_logger.xml")) 52 _ = os.Remove(path.Join(config.LoongcollectorGlobalConfig.LoongCollectorLogDir, config.LoongcollectorGlobalConfig.LoongCollectorPluginLogName)) 53 } 54 55 func readLog(index int) string { 56 bytes, _ := os.ReadFile(path.Join(config.LoongcollectorGlobalConfig.LoongCollectorLogDir, config.LoongcollectorGlobalConfig.LoongCollectorPluginLogName)) 57 logs := strings.Split(string(bytes), "\n") 58 if index > len(logs)-1 { 59 return "" 60 } 61 return logs[index] 62 } 63 64 func Test_generateLog(t *testing.T) { 65 mu.Lock() 66 defer mu.Unlock() 67 type args struct { 68 kvPairs []interface{} 69 } 70 tests := []struct { 71 name string 72 args args 73 want string 74 }{ 75 { 76 name: "empty", 77 args: args{}, 78 want: "", 79 }, 80 { 81 name: "odd number", 82 args: args{kvPairs: []interface{}{"a", "b", "c"}}, 83 want: "a:b\tc:\t", 84 }, 85 { 86 name: "even number", 87 args: args{kvPairs: []interface{}{"a", "b", "c", "d"}}, 88 want: "a:b\tc:d\t", 89 }, 90 } 91 for _, tt := range tests { 92 t.Run(tt.name, func(t *testing.T) { 93 if got := generateLog(tt.args.kvPairs...); got != tt.want { 94 t.Errorf("generateLog() = %v, want %v", got, tt.want) 95 } 96 }) 97 } 98 } 99 100 func Test_generateDefaultConfig(t *testing.T) { 101 mu.Lock() 102 defer mu.Unlock() 103 template = asyncPattern 104 tests := []struct { 105 name string 106 want string 107 flagSetter func() 108 }{ 109 { 110 name: "production", 111 want: fmt.Sprintf(template, "info", config.LoongcollectorGlobalConfig.LoongCollectorLogDir, "", ""), 112 flagSetter: func() {}, 113 }, 114 { 115 name: "test-debug-level", 116 want: fmt.Sprintf(template, "debug", config.LoongcollectorGlobalConfig.LoongCollectorLogDir, "", ""), 117 flagSetter: func() { 118 flag.Set(FlagLevelName, "debug") 119 }, 120 }, 121 { 122 name: "test-wrong-level", 123 want: fmt.Sprintf(template, "info", config.LoongcollectorGlobalConfig.LoongCollectorLogDir, "", ""), 124 flagSetter: func() { 125 flag.Set(FlagLevelName, "debug111") 126 }, 127 }, 128 { 129 name: "test-open-console", 130 want: fmt.Sprintf(template, "info", config.LoongcollectorGlobalConfig.LoongCollectorLogDir, "<console/>", ""), 131 flagSetter: func() { 132 flag.Set(FlagConsoleName, "true") 133 }, 134 }, 135 } 136 for _, tt := range tests { 137 t.Run(tt.name, func(t *testing.T) { 138 tt.flagSetter() 139 clean() 140 initNormalLogger() 141 if got := generateDefaultConfig(); got != tt.want { 142 t.Errorf("generateDefaultConfig() = %v, want %v", got, tt.want) 143 } 144 flag.Set(FlagConsoleName, "false") 145 flag.Set(FlagLevelName, "info") 146 }) 147 } 148 } 149 150 func TestDebug(t *testing.T) { 151 mu.Lock() 152 defer mu.Unlock() 153 flag.Set(FlagLevelName, seelog.DebugStr) 154 clean() 155 initNormalLogger() 156 type args struct { 157 ctx context.Context 158 kvPairs []interface{} 159 } 160 tests := []struct { 161 name string 162 args args 163 want string 164 }{ 165 { 166 name: "with-header", 167 args: args{ 168 ctx: ctx, 169 kvPairs: []interface{}{"a", "b"}, 170 }, 171 want: ".*\\[DBG\\] \\[logger_test.go:\\d{1,}\\] \\[func\\d{1,}\\] \\[mock-configname,mock-logstore\\]\t\\[a b\\]:.*", 172 }, 173 { 174 name: "without-header", 175 args: args{ 176 ctx: context.Background(), 177 kvPairs: []interface{}{"a", "b"}, 178 }, 179 want: ".*\\[DBG\\] \\[logger_test.go:\\d{1,}\\] \\[func\\d{1,}\\] \\[a b\\]:.*", 180 }, 181 } 182 for i, tt := range tests { 183 t.Run(tt.name, func(t *testing.T) { 184 Debug(tt.args.ctx, tt.args.kvPairs) 185 time.Sleep(time.Millisecond) 186 Flush() 187 log := readLog(i) 188 assert.True(t, regexp.MustCompile(tt.want).Match([]byte(log)), "want regexp %s, but got: %s", tt.want, log) 189 }) 190 } 191 } 192 193 func TestLogLevelFromEnv(t *testing.T) { 194 mu.Lock() 195 defer mu.Unlock() 196 clean() 197 os.Setenv("LOGTAIL_LOG_LEVEL", "debug") 198 initNormalLogger() 199 os.Unsetenv("LOGTAIL_LOG_LEVEL") 200 type args struct { 201 ctx context.Context 202 kvPairs []interface{} 203 } 204 tests := []struct { 205 name string 206 args args 207 want string 208 }{ 209 { 210 name: "with-header", 211 args: args{ 212 ctx: ctx, 213 kvPairs: []interface{}{"a", "b"}, 214 }, 215 want: ".*\\[DBG\\] \\[logger_test.go:\\d{1,}\\] \\[func\\d{1,}\\] \\[mock-configname,mock-logstore\\]\t\\[a b\\]:.*", 216 }, 217 { 218 name: "without-header", 219 args: args{ 220 ctx: context.Background(), 221 kvPairs: []interface{}{"a", "b"}, 222 }, 223 want: ".*\\[DBG\\] \\[logger_test.go:\\d{1,}\\] \\[func\\d{1,}\\] \\[a b\\]:.*", 224 }, 225 } 226 for i, tt := range tests { 227 t.Run(tt.name, func(t *testing.T) { 228 Debug(tt.args.ctx, tt.args.kvPairs) 229 time.Sleep(time.Millisecond) 230 Flush() 231 log := readLog(i) 232 assert.True(t, regexp.MustCompile(tt.want).Match([]byte(log)), "want regexp %s, but got: %s", tt.want, log) 233 }) 234 } 235 } 236 237 func TestDebugf(t *testing.T) { 238 mu.Lock() 239 defer mu.Unlock() 240 flag.Set(FlagLevelName, seelog.DebugStr) 241 clean() 242 initNormalLogger() 243 type args struct { 244 ctx context.Context 245 format string 246 params []interface{} 247 } 248 tests := []struct { 249 name string 250 args args 251 want string 252 }{ 253 { 254 name: "without-header", 255 args: args{ 256 ctx: context.Background(), 257 format: "test %s", 258 params: []interface{}{"a"}, 259 }, 260 want: ".*\\[DBG\\] \\[logger_test.go:\\d{1,}\\] \\[func\\d{1,}\\] test \\[a\\].*", 261 }, 262 { 263 name: "with-header", 264 args: args{ 265 ctx: ctx, 266 format: "test %s", 267 params: []interface{}{"a"}, 268 }, 269 want: ".*\\[DBG\\] \\[logger_test.go:\\d{1,}\\] \\[func\\d{1,}\\] \\[mock-configname,mock-logstore\\]\ttest \\[a\\].*", 270 }, 271 } 272 for i, tt := range tests { 273 t.Run(tt.name, func(t *testing.T) { 274 Debugf(tt.args.ctx, tt.args.format, tt.args.params) 275 time.Sleep(time.Millisecond) 276 Flush() 277 log := readLog(i) 278 assert.True(t, regexp.MustCompile(tt.want).Match([]byte(log)), "want regexp %s, but got: %s", tt.want, log) 279 }) 280 } 281 } 282 283 func TestInfo(t *testing.T) { 284 mu.Lock() 285 defer mu.Unlock() 286 clean() 287 initNormalLogger() 288 type args struct { 289 ctx context.Context 290 kvPairs []interface{} 291 } 292 tests := []struct { 293 name string 294 args args 295 want string 296 }{ 297 { 298 name: "with-header", 299 args: args{ 300 ctx: ctx, 301 kvPairs: []interface{}{"a", "b"}, 302 }, 303 want: ".*\\[INF\\] \\[logger_test.go:\\d{1,}\\] \\[func\\d{1,}\\] \\[mock-configname,mock-logstore\\]\t\\[a b\\]:.*", 304 }, 305 { 306 name: "without-header", 307 args: args{ 308 ctx: context.Background(), 309 kvPairs: []interface{}{"a", "b"}, 310 }, 311 want: ".*\\[INF\\] \\[logger_test.go:\\d{1,}\\] \\[func\\d{1,}\\] \\[a b\\]:.*", 312 }, 313 } 314 for i, tt := range tests { 315 t.Run(tt.name, func(t *testing.T) { 316 Info(tt.args.ctx, tt.args.kvPairs) 317 time.Sleep(time.Millisecond) 318 Flush() 319 log := readLog(i) 320 assert.True(t, regexp.MustCompile(tt.want).Match([]byte(log)), "want regexp %s, but got: %s", tt.want, log) 321 }) 322 } 323 } 324 325 func TestInfof(t *testing.T) { 326 mu.Lock() 327 defer mu.Unlock() 328 flag.Set(FlagLevelName, seelog.DebugStr) 329 clean() 330 initNormalLogger() 331 type args struct { 332 ctx context.Context 333 format string 334 params []interface{} 335 } 336 tests := []struct { 337 name string 338 args args 339 want string 340 }{ 341 { 342 name: "without-header", 343 args: args{ 344 ctx: context.Background(), 345 format: "test %s", 346 params: []interface{}{"a"}, 347 }, 348 want: ".*\\[INF\\] \\[logger_test.go:\\d{1,}\\] \\[func\\d{1,}\\] test \\[a\\].*", 349 }, 350 { 351 name: "with-header", 352 args: args{ 353 ctx: ctx, 354 format: "test %s", 355 params: []interface{}{"a"}, 356 }, 357 want: ".*\\[INF\\] \\[logger_test.go:\\d{1,}\\] \\[func\\d{1,}\\] \\[mock-configname,mock-logstore\\]\ttest \\[a\\].*", 358 }, 359 } 360 for i, tt := range tests { 361 t.Run(tt.name, func(t *testing.T) { 362 Infof(tt.args.ctx, tt.args.format, tt.args.params) 363 time.Sleep(time.Millisecond) 364 Flush() 365 log := readLog(i) 366 assert.True(t, regexp.MustCompile(tt.want).Match([]byte(log)), "want regexp %s, but got: %s", tt.want, log) 367 }) 368 } 369 } 370 371 func TestWarn(t *testing.T) { 372 mu.Lock() 373 defer mu.Unlock() 374 clean() 375 initNormalLogger() 376 type args struct { 377 ctx context.Context 378 alarmType string 379 kvPairs []interface{} 380 } 381 tests := []struct { 382 name string 383 args args 384 want string 385 getter func() *util.Alarm 386 }{ 387 { 388 name: "with-header", 389 args: args{ 390 ctx: ctx, 391 alarmType: "WITH_HEADER", 392 kvPairs: []interface{}{"a", "b"}, 393 }, 394 want: ".*\\[WRN\\] \\[logger_test.go:\\d{1,}\\] \\[func\\d{1,}\\] \\[mock-configname,mock-logstore\\]\tAlarmType:WITH_HEADER\t\\[a b\\]:.*", 395 getter: func() *util.Alarm { 396 return ctx.Value(pkg.LogTailMeta).(*pkg.LogtailContextMeta).GetAlarm() 397 }, 398 }, 399 { 400 name: "without-header", 401 args: args{ 402 ctx: context.Background(), 403 alarmType: "WITHOUT_HEADER", 404 kvPairs: []interface{}{"a", "b"}, 405 }, 406 want: ".*\\[WRN\\] \\[logger_test.go:\\d{1,}\\] \\[func\\d{1,}\\] AlarmType:WITHOUT_HEADER\t\\[a b\\]:.*", 407 getter: func() *util.Alarm { 408 return util.GlobalAlarm 409 }, 410 }, 411 } 412 for i, tt := range tests { 413 t.Run(tt.name, func(t *testing.T) { 414 Warning(tt.args.ctx, tt.args.alarmType, tt.args.kvPairs) 415 time.Sleep(time.Millisecond) 416 Flush() 417 log := readLog(i) 418 assert.True(t, regexp.MustCompile(tt.want).Match([]byte(log)), "want regexp %s, but got: %s", tt.want, log) 419 alarmMap := tt.getter().AlarmMap 420 assert.Equal(t, 1, len(alarmMap), "the size of alarm map; %d", len(alarmMap)) 421 for at, item := range alarmMap { 422 assert.Equal(t, tt.args.alarmType, at) 423 fmt.Printf("got alarm msg %s: %+v\n", tt.args.alarmType, item) 424 } 425 for k := range alarmMap { 426 delete(alarmMap, k) 427 } 428 }) 429 } 430 } 431 432 func TestWarnf(t *testing.T) { 433 mu.Lock() 434 defer mu.Unlock() 435 flag.Set(FlagLevelName, seelog.DebugStr) 436 clean() 437 initNormalLogger() 438 type args struct { 439 ctx context.Context 440 format string 441 alarmType string 442 params []interface{} 443 } 444 tests := []struct { 445 name string 446 args args 447 want string 448 getter func() *util.Alarm 449 }{ 450 { 451 name: "without-header", 452 args: args{ 453 ctx: context.Background(), 454 alarmType: "WITHOUT_HEADER", 455 format: "test %s", 456 params: []interface{}{"a"}, 457 }, 458 want: ".*\\[WRN\\] \\[logger_test.go:\\d{1,}\\] \\[func\\d{1,}\\] AlarmType:WITHOUT_HEADER\ttest \\[a\\].*", 459 getter: func() *util.Alarm { 460 return util.GlobalAlarm 461 }, 462 }, 463 { 464 name: "with-header", 465 args: args{ 466 ctx: ctx, 467 alarmType: "WITH_HEADER", 468 format: "test %s", 469 params: []interface{}{"a"}, 470 }, 471 want: ".*\\[WRN\\] \\[logger_test.go:\\d{1,}\\] \\[func\\d{1,}\\] \\[mock-configname,mock-logstore\\]\tAlarmType:WITH_HEADER\ttest \\[a\\].*", 472 473 getter: func() *util.Alarm { 474 return ctx.Value(pkg.LogTailMeta).(*pkg.LogtailContextMeta).GetAlarm() 475 }, 476 }, 477 } 478 for i, tt := range tests { 479 t.Run(tt.name, func(t *testing.T) { 480 Warningf(tt.args.ctx, tt.args.alarmType, tt.args.format, tt.args.params) 481 time.Sleep(time.Millisecond) 482 Flush() 483 log := readLog(i) 484 assert.True(t, regexp.MustCompile(tt.want).Match([]byte(log)), "want regexp %s, but got: %s", tt.want, log) 485 alarmMap := tt.getter().AlarmMap 486 assert.Equal(t, 1, len(alarmMap), "the size of alarm map; %d", len(alarmMap)) 487 for at, item := range alarmMap { 488 assert.Equal(t, tt.args.alarmType, at) 489 fmt.Printf("got alarm msg %s: %+v\n", tt.args.alarmType, item) 490 } 491 for k := range alarmMap { 492 delete(alarmMap, k) 493 } 494 for k := range alarmMap { 495 delete(alarmMap, k) 496 } 497 }) 498 } 499 } 500 501 func TestError(t *testing.T) { 502 mu.Lock() 503 defer mu.Unlock() 504 clean() 505 initNormalLogger() 506 type args struct { 507 ctx context.Context 508 alarmType string 509 kvPairs []interface{} 510 } 511 tests := []struct { 512 name string 513 args args 514 want string 515 getter func() *util.Alarm 516 }{ 517 { 518 name: "with-header", 519 args: args{ 520 ctx: ctx, 521 alarmType: "WITH_HEADER", 522 kvPairs: []interface{}{"a", "b"}, 523 }, 524 want: ".*\\[ERR\\] \\[logger_test.go:\\d{1,}\\] \\[func\\d{1,}\\] \\[mock-configname,mock-logstore\\]\tAlarmType:WITH_HEADER\t\\[a b\\]:.*", 525 getter: func() *util.Alarm { 526 return ctx.Value(pkg.LogTailMeta).(*pkg.LogtailContextMeta).GetAlarm() 527 }, 528 }, 529 { 530 name: "without-header", 531 args: args{ 532 ctx: context.Background(), 533 alarmType: "WITHOUT_HEADER", 534 kvPairs: []interface{}{"a", "b"}, 535 }, 536 want: ".*\\[ERR\\] \\[logger_test.go:\\d{1,}\\] \\[func\\d{1,}\\] AlarmType:WITHOUT_HEADER\t\\[a b\\]:.*", 537 getter: func() *util.Alarm { 538 return util.GlobalAlarm 539 }, 540 }, 541 } 542 for i, tt := range tests { 543 t.Run(tt.name, func(t *testing.T) { 544 Error(tt.args.ctx, tt.args.alarmType, tt.args.kvPairs) 545 time.Sleep(time.Millisecond) 546 Flush() 547 log := readLog(i) 548 assert.True(t, regexp.MustCompile(tt.want).Match([]byte(log)), "want regexp %s, but got: %s", tt.want, log) 549 alarmMap := tt.getter().AlarmMap 550 assert.Equal(t, 1, len(alarmMap), "the size of alarm map; %d", len(alarmMap)) 551 for at, item := range alarmMap { 552 assert.Equal(t, tt.args.alarmType, at) 553 fmt.Printf("got alarm msg %s: %+v\n", tt.args.alarmType, item) 554 } 555 for k := range alarmMap { 556 delete(alarmMap, k) 557 } 558 }) 559 } 560 } 561 562 func TestErrorf(t *testing.T) { 563 mu.Lock() 564 defer mu.Unlock() 565 flag.Set(FlagLevelName, seelog.DebugStr) 566 clean() 567 initNormalLogger() 568 type args struct { 569 ctx context.Context 570 format string 571 alarmType string 572 params []interface{} 573 } 574 tests := []struct { 575 name string 576 args args 577 want string 578 getter func() *util.Alarm 579 }{ 580 { 581 name: "without-header", 582 args: args{ 583 ctx: context.Background(), 584 alarmType: "WITHOUT_HEADER", 585 format: "test %s", 586 params: []interface{}{"a"}, 587 }, 588 want: ".*\\[ERR\\] \\[logger_test.go:\\d{1,}\\] \\[func\\d{1,}\\] AlarmType:WITHOUT_HEADER\ttest \\[a\\].*", 589 getter: func() *util.Alarm { 590 return util.GlobalAlarm 591 }, 592 }, 593 { 594 name: "with-header", 595 args: args{ 596 ctx: ctx, 597 alarmType: "WITH_HEADER", 598 format: "test %s", 599 params: []interface{}{"a"}, 600 }, 601 want: ".*\\[ERR\\] \\[logger_test.go:\\d{1,}\\] \\[func\\d{1,}\\] \\[mock-configname,mock-logstore\\]\tAlarmType:WITH_HEADER\ttest \\[a\\].*", 602 getter: func() *util.Alarm { 603 return ctx.Value(pkg.LogTailMeta).(*pkg.LogtailContextMeta).GetAlarm() 604 }, 605 }, 606 } 607 for i, tt := range tests { 608 t.Run(tt.name, func(t *testing.T) { 609 Errorf(tt.args.ctx, tt.args.alarmType, tt.args.format, tt.args.params) 610 time.Sleep(time.Millisecond) 611 Flush() 612 log := readLog(i) 613 assert.True(t, regexp.MustCompile(tt.want).Match([]byte(log)), "want regexp %s, but got: %s", tt.want, log) 614 alarmMap := tt.getter().AlarmMap 615 assert.Equal(t, 1, len(alarmMap), "the size of alarm map; %d", len(alarmMap)) 616 for at, item := range alarmMap { 617 assert.Equal(t, tt.args.alarmType, at) 618 fmt.Printf("got alarm msg %s: %+v\n", tt.args.alarmType, item) 619 } 620 for k := range alarmMap { 621 delete(alarmMap, k) 622 } 623 }) 624 } 625 } 626 627 func TestOffRemote(t *testing.T) { 628 mu.Lock() 629 defer mu.Unlock() 630 clean() 631 initTestLogger() 632 Warning(context.Background(), "ALARM_TYPE", "a", "b") 633 assert.Equal(t, 0, len(util.GlobalAlarm.AlarmMap)) 634 Error(context.Background(), "ALARM_TYPE", "a", "b") 635 assert.Equal(t, 0, len(util.GlobalAlarm.AlarmMap)) 636 Warningf(context.Background(), "ALARM_TYPE", "test %s", "b") 637 assert.Equal(t, 0, len(util.GlobalAlarm.AlarmMap)) 638 Errorf(context.Background(), "ALARM_TYPE", "test %s", "b") 639 assert.Equal(t, 0, len(util.GlobalAlarm.AlarmMap)) 640 delete(util.GlobalAlarm.AlarmMap, "ALARM_TYPE") 641 }