Add database logging
This commit is contained in:
parent
4453c882e6
commit
6e1affeda3
18 changed files with 290 additions and 24 deletions
17
app/Events/ExceptionLogged.php
Normal file
17
app/Events/ExceptionLogged.php
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Events;
|
||||||
|
|
||||||
|
use App\Models\Log;
|
||||||
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class ExceptionLogged
|
||||||
|
{
|
||||||
|
use Dispatchable, SerializesModels;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
public Log $log
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
||||||
21
app/Events/ExceptionOccurred.php
Normal file
21
app/Events/ExceptionOccurred.php
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Events;
|
||||||
|
|
||||||
|
use App\LogLevelEnum;
|
||||||
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
class ExceptionOccurred
|
||||||
|
{
|
||||||
|
use Dispatchable, SerializesModels;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
public Throwable $exception,
|
||||||
|
public LogLevelEnum $level,
|
||||||
|
public string $message,
|
||||||
|
public array $context = []
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -12,6 +12,6 @@ public function __invoke(Request $request): View
|
||||||
{
|
{
|
||||||
$articles = Article::all()->sortByDesc('created_at');
|
$articles = Article::all()->sortByDesc('created_at');
|
||||||
|
|
||||||
return view('articles.index', compact('articles'));
|
return view('pages.articles.index', compact('articles'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
17
app/Http/Controllers/LogsController.php
Normal file
17
app/Http/Controllers/LogsController.php
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Models\Log;
|
||||||
|
use Illuminate\Contracts\View\View;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class LogsController extends Controller
|
||||||
|
{
|
||||||
|
public function __invoke(Request $request): View
|
||||||
|
{
|
||||||
|
$logs = Log::all()->sortByDesc('created_at');
|
||||||
|
|
||||||
|
return view('pages.logs.index', compact('logs'));
|
||||||
|
}
|
||||||
|
}
|
||||||
31
app/Listeners/LogExceptionToDatabase.php
Normal file
31
app/Listeners/LogExceptionToDatabase.php
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Listeners;
|
||||||
|
|
||||||
|
use App\Events\ExceptionLogged;
|
||||||
|
use App\Events\ExceptionOccurred;
|
||||||
|
use App\Models\Log;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
|
||||||
|
class LogExceptionToDatabase implements ShouldQueue
|
||||||
|
{
|
||||||
|
use InteractsWithQueue;
|
||||||
|
|
||||||
|
public function handle(ExceptionOccurred $event): void
|
||||||
|
{
|
||||||
|
$log = Log::create([
|
||||||
|
'level' => $event->level,
|
||||||
|
'message' => $event->message,
|
||||||
|
'context' => [
|
||||||
|
'exception_class' => get_class($event->exception),
|
||||||
|
'file' => $event->exception->getFile(),
|
||||||
|
'line' => $event->exception->getLine(),
|
||||||
|
'trace' => $event->exception->getTraceAsString(),
|
||||||
|
...$event->context
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
|
||||||
|
ExceptionLogged::dispatch($log);
|
||||||
|
}
|
||||||
|
}
|
||||||
23
app/LogLevelEnum.php
Normal file
23
app/LogLevelEnum.php
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App;
|
||||||
|
|
||||||
|
enum LogLevelEnum: string
|
||||||
|
{
|
||||||
|
case DEBUG = 'debug';
|
||||||
|
case INFO = 'info';
|
||||||
|
case WARNING = 'warning';
|
||||||
|
case ERROR = 'error';
|
||||||
|
case CRITICAL = 'critical';
|
||||||
|
|
||||||
|
public static function toArray(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
self::DEBUG->value,
|
||||||
|
self::INFO->value,
|
||||||
|
self::WARNING->value,
|
||||||
|
self::ERROR->value,
|
||||||
|
self::CRITICAL->value,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
24
app/Models/Log.php
Normal file
24
app/Models/Log.php
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\LogLevelEnum;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class Log extends Model
|
||||||
|
{
|
||||||
|
protected $table = 'logs';
|
||||||
|
|
||||||
|
protected $fillable = [
|
||||||
|
'level',
|
||||||
|
'message',
|
||||||
|
'context',
|
||||||
|
];
|
||||||
|
|
||||||
|
protected $casts = [
|
||||||
|
'level' => LogLevelEnum::class,
|
||||||
|
'context' => 'array',
|
||||||
|
'created_at' => 'datetime',
|
||||||
|
'updated_at' => 'datetime',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
@ -4,10 +4,15 @@
|
||||||
|
|
||||||
use App\Events\ArticleFetched;
|
use App\Events\ArticleFetched;
|
||||||
use App\Events\ArticleReadyToPublish;
|
use App\Events\ArticleReadyToPublish;
|
||||||
|
use App\Events\ExceptionOccurred;
|
||||||
use App\Listeners\CheckArticleKeywords;
|
use App\Listeners\CheckArticleKeywords;
|
||||||
|
use App\Listeners\LogExceptionToDatabase;
|
||||||
use App\Listeners\PublishArticle;
|
use App\Listeners\PublishArticle;
|
||||||
|
use App\LogLevelEnum;
|
||||||
|
use Illuminate\Contracts\Debug\ExceptionHandler;
|
||||||
use Illuminate\Support\Facades\Event;
|
use Illuminate\Support\Facades\Event;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
class AppServiceProvider extends ServiceProvider
|
class AppServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
|
|
@ -27,5 +32,27 @@ public function boot(): void
|
||||||
ArticleReadyToPublish::class,
|
ArticleReadyToPublish::class,
|
||||||
PublishArticle::class,
|
PublishArticle::class,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Event::listen(
|
||||||
|
ExceptionOccurred::class,
|
||||||
|
LogExceptionToDatabase::class,
|
||||||
|
);
|
||||||
|
|
||||||
|
app()->make(ExceptionHandler::class)
|
||||||
|
->reportable(function (Throwable $e) {
|
||||||
|
$level = $this->mapExceptionToLogLevel($e);
|
||||||
|
|
||||||
|
ExceptionOccurred::dispatch($e, $level, $e->getMessage(), []);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private function mapExceptionToLogLevel(Throwable $exception): LogLevelEnum
|
||||||
|
{
|
||||||
|
return match (true) {
|
||||||
|
$exception instanceof \Error => LogLevelEnum::CRITICAL,
|
||||||
|
$exception instanceof \RuntimeException => LogLevelEnum::ERROR,
|
||||||
|
$exception instanceof \InvalidArgumentException => LogLevelEnum::WARNING,
|
||||||
|
default => LogLevelEnum::ERROR,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,10 @@ private static function fetchArticles(): Collection
|
||||||
|
|
||||||
return collect($responses)
|
return collect($responses)
|
||||||
->map(function ($response, $index) use ($urls) {
|
->map(function ($response, $index) use ($urls) {
|
||||||
|
if (!isset($urls[$index])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
$url = $urls[$index];
|
$url = $urls[$index];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -51,7 +55,7 @@ private static function fetchArticles(): Collection
|
||||||
})
|
})
|
||||||
->filter(fn($article) => !empty($article));
|
->filter(fn($article) => !empty($article));
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
logger('article_fetcher')->error("Failed to fetch VRT homepage", ['error' => $e->getMessage()]);
|
logger()->error("Failed to fetch VRT homepage", ['error' => $e->getMessage()]);
|
||||||
return new Collection([]);
|
return new Collection([]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
11
app/Services/Log/LogSaver.php
Normal file
11
app/Services/Log/LogSaver.php
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Services\Log;
|
||||||
|
|
||||||
|
class LogSaver
|
||||||
|
{
|
||||||
|
public function log()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -23,5 +23,19 @@
|
||||||
]);
|
]);
|
||||||
})
|
})
|
||||||
->withExceptions(function (Exceptions $exceptions) {
|
->withExceptions(function (Exceptions $exceptions) {
|
||||||
//
|
$exceptions->reportable(function (Throwable $e) {
|
||||||
|
$level = match (true) {
|
||||||
|
$e instanceof Error => App\LogLevelEnum::CRITICAL,
|
||||||
|
$e instanceof RuntimeException => App\LogLevelEnum::ERROR,
|
||||||
|
$e instanceof InvalidArgumentException => App\LogLevelEnum::WARNING,
|
||||||
|
default => App\LogLevelEnum::ERROR,
|
||||||
|
};
|
||||||
|
|
||||||
|
App\Events\ExceptionOccurred::dispatch(
|
||||||
|
$e,
|
||||||
|
$level,
|
||||||
|
$e->getMessage(),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
});
|
||||||
})->create();
|
})->create();
|
||||||
|
|
|
||||||
25
database/migrations/2025_06_29_154705_create_logs_table.php
Normal file
25
database/migrations/2025_06_29_154705_create_logs_table.php
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use App\LogLevelEnum;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('logs', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->enum('level', LogLevelEnum::toArray());
|
||||||
|
$table->string('message');
|
||||||
|
$table->json('context')->nullable();
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('logs');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
<h1>Articles</h1>
|
|
||||||
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>ID</th>
|
|
||||||
<th>URL</th>
|
|
||||||
<th>Created At</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
@foreach($articles as $article)
|
|
||||||
<tr>
|
|
||||||
<td>{{ $article->id }}</td>
|
|
||||||
<td>{{ $article->url }}</td>
|
|
||||||
<td>{{ $article->created_at->format('Y-m-d H:i') }}</td>
|
|
||||||
</tr>
|
|
||||||
@endforeach
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
10
resources/views/layouts/app.blade.php
Normal file
10
resources/views/layouts/app.blade.php
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Lemmy Poster</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
@include('partials.navbar')
|
||||||
|
|
||||||
|
@yield('content')
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
24
resources/views/pages/articles/index.blade.php
Normal file
24
resources/views/pages/articles/index.blade.php
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
@extends('layouts.app')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<h1>Articles</h1>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>URL</th>
|
||||||
|
<th>Created At</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach($articles as $article)
|
||||||
|
<tr>
|
||||||
|
<td>{{ $article->id }}</td>
|
||||||
|
<td>{{ $article->url }}</td>
|
||||||
|
<td>{{ $article->created_at->format('Y-m-d H:i') }}</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
@endsection
|
||||||
26
resources/views/pages/logs/index.blade.php
Normal file
26
resources/views/pages/logs/index.blade.php
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
@extends('layouts.app')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<h1>Logs</h1>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>Level</th>
|
||||||
|
<th>Message</th>
|
||||||
|
<th>Created At</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach($logs as $log)
|
||||||
|
<tr>
|
||||||
|
<td>{{ $log->id }}</td>
|
||||||
|
<td>{{ ucfirst($log->level->value) }}</td>
|
||||||
|
<td>{{ $log->message }}</td>
|
||||||
|
<td>{{ $log->created_at->format('Y-m-d H:i') }}</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
@endsection
|
||||||
10
resources/views/partials/navbar.blade.php
Normal file
10
resources/views/partials/navbar.blade.php
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href="/articles">Articles</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/logs">Logs</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
use App\Http\Controllers\ArticlesController;
|
use App\Http\Controllers\ArticlesController;
|
||||||
|
use App\Http\Controllers\LogsController;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
use Inertia\Inertia;
|
use Inertia\Inertia;
|
||||||
|
|
||||||
|
|
@ -18,3 +19,4 @@
|
||||||
require __DIR__.'/auth.php';
|
require __DIR__.'/auth.php';
|
||||||
|
|
||||||
Route::get('/articles', ArticlesController::class)->name('articles');
|
Route::get('/articles', ArticlesController::class)->name('articles');
|
||||||
|
Route::get('/logs', LogsController::class)->name('logs');
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue