github.com/goravel/framework@v1.13.9/log/logrus_writer_test.go (about) 1 package log 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 nethttp "net/http" 8 "os" 9 "os/exec" 10 "reflect" 11 "testing" 12 13 "github.com/stretchr/testify/assert" 14 15 configmock "github.com/goravel/framework/contracts/config/mocks" 16 "github.com/goravel/framework/contracts/filesystem" 17 contractshttp "github.com/goravel/framework/contracts/http" 18 "github.com/goravel/framework/contracts/validation" 19 "github.com/goravel/framework/support/carbon" 20 "github.com/goravel/framework/support/file" 21 ) 22 23 var singleLog = "storage/logs/goravel.log" 24 var dailyLog = fmt.Sprintf("storage/logs/goravel-%s.log", carbon.Now().ToDateString()) 25 26 func TestLogrus(t *testing.T) { 27 var ( 28 mockConfig *configmock.Config 29 log *Application 30 ) 31 32 beforeEach := func() { 33 mockConfig = initMockConfig() 34 } 35 36 tests := []struct { 37 name string 38 setup func() 39 assert func() 40 }{ 41 { 42 name: "WithContext", 43 setup: func() { 44 mockConfig.On("GetString", "logging.channels.daily.level").Return("debug").Once() 45 mockConfig.On("GetString", "logging.channels.single.level").Return("debug").Once() 46 47 log = NewApplication(mockConfig) 48 }, 49 assert: func() { 50 writer := log.WithContext(context.Background()) 51 assert.Equal(t, reflect.TypeOf(writer).String(), reflect.TypeOf(&Writer{}).String()) 52 }, 53 }, 54 { 55 name: "Debug", 56 setup: func() { 57 mockDriverConfig(mockConfig) 58 59 log = NewApplication(mockConfig) 60 log.Debug("Debug Goravel") 61 }, 62 assert: func() { 63 assert.True(t, file.Contain(singleLog, "test.debug: Debug Goravel")) 64 assert.True(t, file.Contain(dailyLog, "test.debug: Debug Goravel")) 65 }, 66 }, 67 { 68 name: "No Debug", 69 setup: func() { 70 mockConfig.On("GetString", "logging.channels.daily.level").Return("info").Once() 71 mockConfig.On("GetString", "logging.channels.single.level").Return("info").Once() 72 mockConfig.On("GetString", "app.timezone").Return("UTC").Once() 73 mockConfig.On("GetString", "app.env").Return("test").Once() 74 log = NewApplication(mockConfig) 75 log.Debug("No Debug Goravel") 76 }, 77 assert: func() { 78 assert.False(t, file.Contain(singleLog, "test.debug: No Debug Goravel")) 79 assert.False(t, file.Contain(dailyLog, "test.debug: No Debug Goravel")) 80 }, 81 }, 82 { 83 name: "Debugf", 84 setup: func() { 85 mockDriverConfig(mockConfig) 86 87 log = NewApplication(mockConfig) 88 log.Debugf("Goravel: %s", "World") 89 }, 90 assert: func() { 91 assert.True(t, file.Contain(singleLog, "test.debug: Goravel: World")) 92 assert.True(t, file.Contain(dailyLog, "test.debug: Goravel: World")) 93 }, 94 }, 95 { 96 name: "Info", 97 setup: func() { 98 mockDriverConfig(mockConfig) 99 100 log = NewApplication(mockConfig) 101 log.Info("Goravel") 102 }, 103 assert: func() { 104 assert.True(t, file.Contain(singleLog, "test.info: Goravel")) 105 assert.True(t, file.Contain(dailyLog, "test.info: Goravel")) 106 }, 107 }, 108 { 109 name: "Infof", 110 setup: func() { 111 mockDriverConfig(mockConfig) 112 113 log = NewApplication(mockConfig) 114 log.Infof("Goravel: %s", "World") 115 }, 116 assert: func() { 117 assert.True(t, file.Contain(singleLog, "test.info: Goravel: World")) 118 assert.True(t, file.Contain(dailyLog, "test.info: Goravel: World")) 119 }, 120 }, 121 { 122 name: "Warning", 123 setup: func() { 124 mockDriverConfig(mockConfig) 125 126 log = NewApplication(mockConfig) 127 log.Warning("Goravel") 128 }, 129 assert: func() { 130 assert.True(t, file.Contain(singleLog, "test.warning: Goravel")) 131 assert.True(t, file.Contain(dailyLog, "test.warning: Goravel")) 132 }, 133 }, 134 { 135 name: "Warningf", 136 setup: func() { 137 mockDriverConfig(mockConfig) 138 139 log = NewApplication(mockConfig) 140 log.Warningf("Goravel: %s", "World") 141 }, 142 assert: func() { 143 assert.True(t, file.Contain(singleLog, "test.warning: Goravel: World")) 144 assert.True(t, file.Contain(dailyLog, "test.warning: Goravel: World")) 145 }, 146 }, 147 { 148 name: "Error", 149 setup: func() { 150 mockDriverConfig(mockConfig) 151 152 log = NewApplication(mockConfig) 153 log.Error("Goravel") 154 }, 155 assert: func() { 156 assert.True(t, file.Contain(singleLog, "test.error: Goravel")) 157 assert.True(t, file.Contain(dailyLog, "test.error: Goravel")) 158 }, 159 }, 160 { 161 name: "Errorf", 162 setup: func() { 163 mockDriverConfig(mockConfig) 164 165 log = NewApplication(mockConfig) 166 log.Errorf("Goravel: %s", "World") 167 }, 168 assert: func() { 169 assert.True(t, file.Contain(singleLog, "test.error: Goravel: World")) 170 assert.True(t, file.Contain(dailyLog, "test.error: Goravel: World")) 171 }, 172 }, 173 { 174 name: "Panic", 175 setup: func() { 176 mockDriverConfig(mockConfig) 177 178 log = NewApplication(mockConfig) 179 }, 180 assert: func() { 181 assert.Panics(t, func() { 182 log.Panic("Goravel") 183 }) 184 assert.True(t, file.Contain(singleLog, "test.panic: Goravel")) 185 assert.True(t, file.Contain(dailyLog, "test.panic: Goravel")) 186 }, 187 }, 188 { 189 name: "Panicf", 190 setup: func() { 191 mockDriverConfig(mockConfig) 192 193 log = NewApplication(mockConfig) 194 }, 195 assert: func() { 196 assert.Panics(t, func() { 197 log.Panicf("Goravel: %s", "World") 198 }) 199 assert.True(t, file.Contain(singleLog, "test.panic: Goravel: World")) 200 assert.True(t, file.Contain(dailyLog, "test.panic: Goravel: World")) 201 }, 202 }, 203 { 204 name: "Code", 205 setup: func() { 206 mockDriverConfig(mockConfig) 207 208 log = NewApplication(mockConfig) 209 log.Code("code").Info("Goravel") 210 }, 211 assert: func() { 212 assert.True(t, file.Contain(singleLog, "test.info: Goravel\ncode: \"code\"")) 213 assert.True(t, file.Contain(dailyLog, "test.info: Goravel\ncode: \"code\"")) 214 }, 215 }, 216 { 217 name: "Hint", 218 setup: func() { 219 mockDriverConfig(mockConfig) 220 221 log = NewApplication(mockConfig) 222 log.Hint("hint").Info("Goravel") 223 }, 224 assert: func() { 225 assert.True(t, file.Contain(singleLog, "test.info: Goravel\nhint: \"hint\"")) 226 assert.True(t, file.Contain(dailyLog, "test.info: Goravel\nhint: \"hint\"")) 227 }, 228 }, 229 { 230 name: "In", 231 setup: func() { 232 mockDriverConfig(mockConfig) 233 234 log = NewApplication(mockConfig) 235 log.In("domain").Info("Goravel") 236 }, 237 assert: func() { 238 assert.True(t, file.Contain(singleLog, "test.info: Goravel\ndomain: \"domain\"")) 239 assert.True(t, file.Contain(dailyLog, "test.info: Goravel\ndomain: \"domain\"")) 240 }, 241 }, 242 { 243 name: "Owner", 244 setup: func() { 245 mockDriverConfig(mockConfig) 246 247 log = NewApplication(mockConfig) 248 log.Owner("team@goravel.dev").Info("Goravel") 249 }, 250 assert: func() { 251 assert.True(t, file.Contain(singleLog, "test.info: Goravel\nowner: \"team@goravel.dev\"")) 252 assert.True(t, file.Contain(dailyLog, "test.info: Goravel\nowner: \"team@goravel.dev\"")) 253 }, 254 }, 255 { 256 name: "Request", 257 setup: func() { 258 mockDriverConfig(mockConfig) 259 260 log = NewApplication(mockConfig) 261 log.Request(&TestRequest{}).Info("Goravel") 262 }, 263 assert: func() { 264 expectedParts := []string{ 265 `test.info: Goravel`, 266 `request: {`, 267 `"method":"GET`, 268 `"uri":"http://localhost:3000/"`, 269 `"Sec-Fetch-User":["?1"]`, 270 `"Host":["localhost:3000"]`, 271 `"body":{`, 272 `"key1":"value1"`, 273 `"key2":"value2"`, 274 } 275 276 for _, part := range expectedParts { 277 assert.True(t, file.Contain(singleLog, part), part) 278 assert.True(t, file.Contain(dailyLog, part), part) 279 } 280 }, 281 }, 282 { 283 name: "Response", 284 setup: func() { 285 mockDriverConfig(mockConfig) 286 287 log = NewApplication(mockConfig) 288 log.Response(&TestResponse{}).Info("Goravel") 289 }, 290 assert: func() { 291 expectedParts := []string{ 292 `test.info: Goravel`, 293 `response: {`, 294 `"status":200`, 295 `"header":{"Content-Type":["text/plain; charset=utf-8"]}`, 296 `"body":{}`, 297 `"size":4`, 298 } 299 300 for _, part := range expectedParts { 301 assert.True(t, file.Contain(singleLog, part)) 302 assert.True(t, file.Contain(dailyLog, part)) 303 } 304 }, 305 }, 306 { 307 name: "Tags", 308 setup: func() { 309 mockDriverConfig(mockConfig) 310 311 log = NewApplication(mockConfig) 312 log.Tags("tag").Info("Goravel") 313 }, 314 assert: func() { 315 assert.True(t, file.Contain(singleLog, "test.info: Goravel\ntags: [\"tag\"]")) 316 assert.True(t, file.Contain(dailyLog, "test.info: Goravel\ntags: [\"tag\"]")) 317 }, 318 }, 319 { 320 name: "User", 321 setup: func() { 322 mockDriverConfig(mockConfig) 323 324 log = NewApplication(mockConfig) 325 log.User(map[string]any{"name": "kkumar-gcc"}).Info("Goravel") 326 }, 327 assert: func() { 328 assert.True(t, file.Contain(singleLog, "test.info: Goravel\nuser: {\"name\":\"kkumar-gcc\"}")) 329 assert.True(t, file.Contain(dailyLog, "test.info: Goravel\nuser: {\"name\":\"kkumar-gcc\"}")) 330 }, 331 }, 332 { 333 name: "With", 334 setup: func() { 335 mockDriverConfig(mockConfig) 336 337 log = NewApplication(mockConfig) 338 log.With(map[string]any{"key": "value"}).Info("Goravel") 339 }, 340 assert: func() { 341 assert.True(t, file.Contain(singleLog, "test.info: Goravel\ncontext: {\"key\":\"value\"}")) 342 assert.True(t, file.Contain(dailyLog, "test.info: Goravel\ncontext: {\"key\":\"value\"}")) 343 }, 344 }, 345 } 346 347 for _, test := range tests { 348 t.Run(test.name, func(t *testing.T) { 349 beforeEach() 350 test.setup() 351 test.assert() 352 353 mockConfig.AssertExpectations(t) 354 }) 355 } 356 357 _ = file.Remove("storage") 358 } 359 360 func TestLogrus_Fatal(t *testing.T) { 361 mockConfig := initMockConfig() 362 mockDriverConfig(mockConfig) 363 log := NewApplication(mockConfig) 364 365 if os.Getenv("FATAL") == "1" { 366 log.Fatal("Goravel") 367 return 368 } 369 cmd := exec.Command(os.Args[0], "-test.run=TestLogrus_Fatal") 370 cmd.Env = append(os.Environ(), "FATAL=1") 371 err := cmd.Run() 372 373 assert.EqualError(t, err, "exit status 1") 374 assert.True(t, file.Contain(singleLog, "test.fatal: Goravel")) 375 assert.True(t, file.Contain(dailyLog, "test.fatal: Goravel")) 376 377 _ = file.Remove("storage") 378 } 379 380 func TestLogrus_Fatalf(t *testing.T) { 381 mockConfig := initMockConfig() 382 mockDriverConfig(mockConfig) 383 log := NewApplication(mockConfig) 384 385 if os.Getenv("FATAL") == "1" { 386 log.Fatalf("Goravel") 387 return 388 } 389 cmd := exec.Command(os.Args[0], "-test.run=TestLogrus_Fatal") 390 cmd.Env = append(os.Environ(), "FATAL=1") 391 err := cmd.Run() 392 393 assert.EqualError(t, err, "exit status 1") 394 assert.True(t, file.Contain(singleLog, "test.fatal: Goravel")) 395 assert.True(t, file.Contain(dailyLog, "test.fatal: Goravel")) 396 397 _ = file.Remove("storage") 398 } 399 400 func initMockConfig() *configmock.Config { 401 mockConfig := &configmock.Config{} 402 403 mockConfig.On("GetString", "logging.default").Return("stack").Once() 404 mockConfig.On("GetString", "logging.channels.stack.driver").Return("stack").Once() 405 mockConfig.On("Get", "logging.channels.stack.channels").Return([]string{"single", "daily"}).Once() 406 mockConfig.On("GetString", "logging.channels.daily.driver").Return("daily").Once() 407 mockConfig.On("GetString", "logging.channels.daily.path").Return(singleLog).Once() 408 mockConfig.On("GetInt", "logging.channels.daily.days").Return(7).Once() 409 mockConfig.On("GetBool", "logging.channels.daily.print").Return(false).Once() 410 mockConfig.On("GetString", "logging.channels.single.driver").Return("single").Once() 411 mockConfig.On("GetString", "logging.channels.single.path").Return(singleLog).Once() 412 mockConfig.On("GetBool", "logging.channels.single.print").Return(false).Once() 413 414 return mockConfig 415 } 416 417 func mockDriverConfig(mockConfig *configmock.Config) { 418 mockConfig.On("GetString", "logging.channels.daily.level").Return("debug").Once() 419 mockConfig.On("GetString", "logging.channels.single.level").Return("debug").Once() 420 mockConfig.On("GetString", "app.timezone").Return("UTC") 421 mockConfig.On("GetString", "app.env").Return("test") 422 } 423 424 type TestRequest struct{} 425 426 func (r *TestRequest) Header(key string, defaultValue ...string) string { 427 panic("do not need to implement it") 428 } 429 430 func (r *TestRequest) Headers() nethttp.Header { 431 return nethttp.Header{ 432 "Sec-Fetch-User": []string{"?1"}, 433 "Host": []string{"localhost:3000"}, 434 } 435 } 436 437 func (r *TestRequest) Method() string { 438 return "GET" 439 } 440 441 func (r *TestRequest) Path() string { 442 return "/test" 443 } 444 445 func (r *TestRequest) Url() string { 446 panic("do not need to implement it") 447 } 448 449 func (r *TestRequest) FullUrl() string { 450 return "http://localhost:3000/" 451 } 452 453 func (r *TestRequest) Ip() string { 454 panic("do not need to implement it") 455 } 456 457 func (r *TestRequest) Host() string { 458 panic("do not need to implement it") 459 } 460 461 func (r *TestRequest) All() map[string]any { 462 return map[string]interface{}{ 463 "key1": "value1", 464 "key2": "value2", 465 } 466 } 467 468 func (r *TestRequest) Bind(obj any) error { 469 panic("do not need to implement it") 470 } 471 472 func (r *TestRequest) Route(key string) string { 473 panic("do not need to implement it") 474 } 475 476 func (r *TestRequest) RouteInt(key string) int { 477 panic("do not need to implement it") 478 } 479 480 func (r *TestRequest) RouteInt64(key string) int64 { 481 panic("do not need to implement it") 482 } 483 484 func (r *TestRequest) Query(key string, defaultValue ...string) string { 485 panic("do not need to implement it") 486 } 487 488 func (r *TestRequest) QueryInt(key string, defaultValue ...int) int { 489 panic("do not need to implement it") 490 } 491 492 func (r *TestRequest) QueryInt64(key string, defaultValue ...int64) int64 { 493 panic("do not need to implement it") 494 } 495 496 func (r *TestRequest) QueryBool(key string, defaultValue ...bool) bool { 497 panic("do not need to implement it") 498 } 499 500 func (r *TestRequest) QueryArray(key string) []string { 501 panic("do not need to implement it") 502 } 503 504 func (r *TestRequest) QueryMap(key string) map[string]string { 505 panic("do not need to implement it") 506 } 507 508 func (r *TestRequest) Queries() map[string]string { 509 panic("do not need to implement it") 510 } 511 512 func (r *TestRequest) Form(key string, defaultValue ...string) string { 513 panic("do not need to implement it") 514 } 515 516 func (r *TestRequest) Json(key string, defaultValue ...string) string { 517 panic("do not need to implement it") 518 } 519 520 func (r *TestRequest) Input(key string, defaultValue ...string) string { 521 panic("do not need to implement it") 522 } 523 524 func (r *TestRequest) InputArray(key string, defaultValue ...[]string) []string { 525 panic("do not need to implement it") 526 } 527 528 func (r *TestRequest) InputMap(key string, defaultValue ...map[string]string) map[string]string { 529 panic("do not need to implement it") 530 } 531 532 func (r *TestRequest) InputInt(key string, defaultValue ...int) int { 533 panic("do not need to implement it") 534 } 535 536 func (r *TestRequest) InputInt64(key string, defaultValue ...int64) int64 { 537 panic("do not need to implement it") 538 } 539 540 func (r *TestRequest) InputBool(key string, defaultValue ...bool) bool { 541 panic("do not need to implement it") 542 } 543 544 func (r *TestRequest) File(name string) (filesystem.File, error) { 545 panic("do not need to implement it") 546 } 547 548 func (r *TestRequest) AbortWithStatus(code int) {} 549 550 func (r *TestRequest) AbortWithStatusJson(code int, jsonObj any) { 551 panic("do not need to implement it") 552 } 553 554 func (r *TestRequest) Next() {} 555 556 func (r *TestRequest) Origin() *nethttp.Request { 557 panic("do not need to implement it") 558 } 559 560 func (r *TestRequest) Validate(rules map[string]string, options ...validation.Option) (validation.Validator, error) { 561 panic("do not need to implement it") 562 } 563 564 func (r *TestRequest) ValidateRequest(request contractshttp.FormRequest) (validation.Errors, error) { 565 panic("do not need to implement it") 566 } 567 568 type TestResponse struct { 569 } 570 571 func (r *TestResponse) Data(code int, contentType string, data []byte) contractshttp.Response { 572 panic("do not need to implement it") 573 } 574 575 func (r *TestResponse) Download(filepath, filename string) contractshttp.Response { 576 panic("do not need to implement it") 577 } 578 579 func (r *TestResponse) File(filepath string) contractshttp.Response { 580 panic("do not need to implement it") 581 } 582 583 func (r *TestResponse) Header(key, value string) contractshttp.ContextResponse { 584 panic("do not need to implement it") 585 } 586 587 func (r *TestResponse) Json(code int, obj any) contractshttp.Response { 588 panic("do not need to implement it") 589 } 590 591 func (r *TestResponse) Origin() contractshttp.ResponseOrigin { 592 return &TestResponseOrigin{ctx: r} 593 } 594 595 func (r *TestResponse) Redirect(code int, location string) contractshttp.Response { 596 panic("do not need to implement it") 597 } 598 599 func (r *TestResponse) String(code int, format string, values ...any) contractshttp.Response { 600 panic("do not need to implement it") 601 } 602 603 func (r *TestResponse) Success() contractshttp.ResponseSuccess { 604 panic("do not need to implement it") 605 } 606 607 func (r *TestResponse) Status(code int) contractshttp.ResponseStatus { 608 panic("do not need to implement it") 609 } 610 611 func (r *TestResponse) Writer() nethttp.ResponseWriter { 612 panic("do not need to implement it") 613 } 614 615 func (r *TestResponse) Flush() { 616 panic("do not need to implement it") 617 } 618 619 func (r *TestResponse) View() contractshttp.ResponseView { 620 panic("do not need to implement it") 621 } 622 623 type TestResponseOrigin struct { 624 ctx *TestResponse 625 } 626 627 func (r *TestResponseOrigin) Body() *bytes.Buffer { 628 return bytes.NewBuffer([]byte("body")) 629 } 630 631 func (r *TestResponseOrigin) Header() nethttp.Header { 632 return nethttp.Header{ 633 "Content-Type": []string{"text/plain; charset=utf-8"}, 634 } 635 } 636 637 func (r *TestResponseOrigin) Size() int { 638 return r.Body().Len() 639 } 640 641 func (r *TestResponseOrigin) Status() int { 642 return 200 643 }