diff --git a/docs/content/docs/Basics/helpers.md b/docs/content/docs/Basics/helpers.md index e9a6b1b..275cca2 100644 --- a/docs/content/docs/Basics/helpers.md +++ b/docs/content/docs/Basics/helpers.md @@ -104,7 +104,7 @@ Valravn includes several global functions that you can use in your code. {{< /column >}} {{< column "method" >}} -[logg](#logg) +[vlog](#vlog) {{< /column >}} {{< column "method" >}} @@ -125,9 +125,43 @@ Resolve the given id to a related model. Resolve given Model to a resource class. -##### logg - -Log the given exception to a specific channel and format. +##### vlog + +Log the error in a dedicated channel with a simplified format. + +```php +class someClass { + public function someMethod(): void + { + try{ + // do something + }catch (Exception $e){ + vlog('The reason'); + } + } +} +``` +The logged content should be like this: + +```text +[2025-01-06 07:57:54] testing.DEBUG: At: [Namespace\someClass::someMethod] => "The reason" +``` + +Also, You can pass the exception to track the issue. + +```php +catch (Exception $e){ + vlog('The reason',['previous' => $e]); +} +``` + +And if you just want to log the error, you need to pass the exception only. + +```php +catch (Exception $e){ + vlog($e); +} +``` ##### slugify diff --git a/src/Exceptions/VException.php b/src/Exceptions/VException.php index 7b8dca6..5970f51 100644 --- a/src/Exceptions/VException.php +++ b/src/Exceptions/VException.php @@ -55,7 +55,7 @@ public function __construct( */ public function render(): JsonResponse { - logg(self::class, $this, ['errorCode' => $this->getErrorCode(), 'responseCode' => $this->getCode()]); + vlog($this); return new JsonResponse([ 'title' => 'Unexpected error!', diff --git a/src/Helpers/functions.php b/src/Helpers/functions.php index 588040b..df7b853 100644 --- a/src/Helpers/functions.php +++ b/src/Helpers/functions.php @@ -79,19 +79,22 @@ function resolveMorphableToResource(?Model $morphable): JsonResource } } -if (!function_exists('logg')) { +if (!function_exists('vlog')) { /** * Log the given exception to a specific channel and format. * - * @param string $location - * @param Throwable $e - * @param array $context + * @param Throwable|string $message + * @param array $context * * @return void */ - function logg(string $location, Throwable $e, array $context = []): void + function vlog(Throwable|string $message, array $context = []): void { - Log::channel('valravn')->debug($location.' => '.'message: '.$e->getMessage(), $context); + $backtrace = debug_backtrace(limit: 2)[1]; + $location = $backtrace['class'].'::'.$backtrace['function']; + $message = is_string($message) ? $message : $message->getMessage(); + + Log::channel('valravn')->debug("At: [$location] => \"$message\"", $context); } } @@ -111,7 +114,7 @@ function valravn_config(string $key): string|array if (!function_exists('slugify')) { /** - * Make a english or non-english string to a slug. + * Make an english or non-english string to a slug. * * @param string $string * @param string $separator diff --git a/src/ValravnServiceProvider.php b/src/ValravnServiceProvider.php index 38435a9..572bfc8 100644 --- a/src/ValravnServiceProvider.php +++ b/src/ValravnServiceProvider.php @@ -109,7 +109,6 @@ private function registerPublishes() */ private function registerMacros() { - // TODO: should be documented if (env('ENABLE_DB_LOG', false)) { DB::listen(function (QueryExecuted $query) { $bindings = implode(',', $query->bindings); diff --git a/tests/Feature/Commands/MigrationTests.php b/tests/Feature/Commands/MigrationTests.php index d9aeb61..f54417c 100644 --- a/tests/Feature/Commands/MigrationTests.php +++ b/tests/Feature/Commands/MigrationTests.php @@ -19,10 +19,10 @@ protected function setUp(): void protected function tearDown(): void { - parent::tearDown(); - $file = base_path("database/migrations/Blog/{$this->datePrefix}_create_posts_table.php"); File::delete($file); + + parent::tearDown(); } /** diff --git a/tests/Feature/Commands/PivotTest.php b/tests/Feature/Commands/PivotTest.php index 8f5201d..7a9f755 100644 --- a/tests/Feature/Commands/PivotTest.php +++ b/tests/Feature/Commands/PivotTest.php @@ -19,10 +19,10 @@ protected function setUp(): void protected function tearDown(): void { - parent::tearDown(); - $pivot = database_path("migrations/Blog/{$this->datePrefix}_create_category_post_table.php"); File::delete($pivot); + + parent::tearDown(); } /** diff --git a/tests/Feature/Helper/FunctionsTest.php b/tests/Feature/Helper/FunctionsTest.php index 50523a6..166955f 100644 --- a/tests/Feature/Helper/FunctionsTest.php +++ b/tests/Feature/Helper/FunctionsTest.php @@ -12,12 +12,15 @@ use Hans\Valravn\Tests\Core\Resources\User\UserResource; use Hans\Valravn\Tests\TestCase; use Illuminate\Http\Resources\Json\JsonResource; +use Illuminate\Support\Facades\File; use Illuminate\Support\Optional; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; class FunctionsTest extends TestCase { private User $user; private Post $post; + private string $date; /** * Setup the test environment. @@ -25,8 +28,24 @@ class FunctionsTest extends TestCase protected function setUp(): void { parent::setUp(); + $this->user = UserFactory::new()->create(); $this->post = PostFactory::new()->create(); + $this->date = now()->format('Y-m-d'); + config()->set('logging.channels.valravn', [ + 'driver' => 'daily', + 'path' => storage_path('logs/valravn.log'), + 'level' => 'debug', + 'days' => 1, + 'replace_placeholders' => true, + ]); + } + + protected function tearDown(): void + { + File::delete(storage_path("logs/valravn-$this->date.log")); + + parent::tearDown(); } /** @@ -152,8 +171,7 @@ public function resolveMorphableToResourceWithResourceCollectionableImplemented( */ public function slugify(): void { - // poetry meaning: if you are alive now, don't let the present pass without happiness -Omar Khayyam - // p.s.: obviously it is very complex and deep, so I can't translate it properly. + // Poetry meaning: if you are alive now, don't let the present pass without happiness -Omar Khayyam $non_english_string = 'گر یک نفست ز زندگانی گذرد / مگذار ک جز به شادمانی گذرد'; $slug = slugify($non_english_string); @@ -163,4 +181,71 @@ public function slugify(): void $slug ); } + + /** + * @test + * + * @return void + */ + public function vlogLogFile(): void + { + self::assertFileDoesNotExist(storage_path("logs/valravn-$this->date.log")); + + vlog('The reason'); + + self::assertFileExists(storage_path("logs/valravn-$this->date.log")); + } + + /** + * @test + * + * @return void + */ + public function vlogContent(): void + { + $file = storage_path("logs/valravn-$this->date.log"); + $e = new NotFoundHttpException('Failed to found your data'); + vlog('The reason: {r}', ['r' => 'something', 'previous' => $e]); + + $actual = file_get_contents($file); + $expected = << "The reason: something" + EOT; + + self::assertStringContainsString( + $expected, + $actual + ); + + $expected = <<<'EOD' + [object] (Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException(code: 0): Failed to found your data + EOD; + + self::assertStringContainsString( + $expected, + $actual + ); + } + + /** + * @test + * + * @return void + */ + public function vlogThrowableAsMessage(): void + { + $file = storage_path("logs/valravn-$this->date.log"); + $e = new NotFoundHttpException('Failed to found your data'); + vlog($e); + + $actual = file_get_contents($file); + $expected = << "Failed to found your data" + EOT; + + self::assertStringContainsString( + $expected, + $actual + ); + } }