laravel-applogger maintained by fastnetksa
laravel-applogger
A configurable database and file log management package for Laravel 11/12 applications.
Captures application logs to a database table via a custom Monolog channel, and provides a full-featured admin UI to browse, filter, inspect, and clean up both database logs and Laravel log files.
Features
- Custom Monolog
databasechannel — logs flow automatically toapplication_logstable - Config-driven log types (
exception,payment,auth, etc.) - Keyword-based auto-detection of log type from message content
- Tenant context fields — attach arbitrary columns (e.g.
company_id,business_id) via invokable resolver classes - Dynamic Eloquent relationships — show related model data in log detail view without touching package code
- Sensitive key redaction — passwords, tokens, API keys never stored in plain text
- Admin UI with DataTables, filtering, log detail modal
- File log management — list, view, download, delete Laravel log files
- Health status API — JSON endpoint with 24-hour error/warning counts
- Fully publishable: config, migration, views
Requirements
| Dependency | Version |
|---|---|
| PHP | ^8.2 |
| Laravel | ^11.0 | ^12.0 |
| yajra/laravel-datatables-oracle | ^11.0 | ^12.0 |
| monolog/monolog | ^3.0 |
The admin UI also requires Bootstrap 5, Font Awesome, DataTables, and SweetAlert2 to be available in the host application's layout.
Installation
composer require fastnetksa/laravel-applogger
The ServiceProvider is auto-discovered via Laravel's package discovery — no manual registration needed.
Setup
1. Publish config
php artisan vendor:publish --tag=applogger-config
This creates config/applogger.php. Edit it to match your application.
2. Publish & customise migration
php artisan vendor:publish --tag=applogger-migrations
Open the published migration and add your tenant columns before running migrate:
// Inside Schema::create('application_logs', ...) — add after user_id:
$table->foreignId('company_id')->nullable()->constrained()->nullOnDelete();
$table->foreignId('business_id')->nullable()->constrained()->nullOnDelete();
3. Run migration
php artisan migrate
Monolog Channel Setup
Add a database channel to config/logging.php:
'channels' => [
// ... existing channels ...
'database' => [
'driver' => 'custom',
'via' => \FastnetKSA\AppLogger\Logging\CreateDatabaseLogger::class,
'level' => 'debug',
],
],
Then set your default or stack channel to include database:
// Option 1 — add to your stack
'stack' => [
'driver' => 'stack',
'channels' => ['single', 'database'],
],
// Option 2 — set as default
'default' => env('LOG_CHANNEL', 'database'),
All log calls (Log::error(...), Log::info(...), etc.) will now be persisted to the database automatically.
Layout Configuration
Set layout.value in config/applogger.php to the Blade view path of your application's main layout:
'layout' => [
'value' => 'layouts.admin', // → resources/views/layouts/admin.blade.php
],
The layout must contain @yield('content') where page content should appear.
If your layout also uses {{ $slot }}
When a layout dual-supports both component usage (<x-your-layout>) and extends usage (@extends), the $slot variable is only defined in the component context. To prevent an "Undefined variable $slot" error when the package uses @extends, change:
{{-- Before --}}
{{ $slot }}
{{-- After --}}
{{ $slot ?? '' }}
Route Configuration
Option A — Package-managed routes (default)
The package registers its own routes when routes.enabled = true:
'routes' => [
'enabled' => true,
'prefix' => 'admin/system/logs',
'name' => 'applogger.',
'middleware' => ['web', 'auth'],
],
Routes available:
| Method | URI | Name | Action |
|---|---|---|---|
| GET | {prefix}/ |
{name}index |
Log list & dashboard |
| GET | {prefix}/{id} |
{name}show |
Log detail (JSON) |
| POST | {prefix}/cleanup |
{name}cleanup |
Delete old logs |
| GET | {prefix}/health/status |
{name}health |
Health check (JSON) |
| GET | {prefix}/files/list |
{name}files |
File log list |
| GET | {prefix}/files/view |
{name}files.view |
View file content (JSON) |
| GET | {prefix}/files/download |
{name}files.download |
Download log file |
| POST | {prefix}/files/delete |
{name}files.delete |
Delete a log file |
| POST | {prefix}/files/clear-all |
{name}files.clear-all |
Clear all log files |
Option B — Host-app-managed routes
If the host app has its own route group (e.g. with auth guards, permission middleware, locale prefix), disable the package routes and define them manually:
// config/applogger.php
'routes' => [
'enabled' => false,
],
// routes/admin.php
use FastnetKSA\AppLogger\Http\Controllers\ApplicationLogsController;
Route::prefix('system/logs')->name('admin.system.logs.')->group(function () {
Route::get('/', [ApplicationLogsController::class, 'index'])->name('index');
Route::get('/{log}', [ApplicationLogsController::class, 'show'])->name('show');
Route::post('/cleanup', [ApplicationLogsController::class, 'cleanup'])->name('cleanup');
Route::get('/health/status', [ApplicationLogsController::class, 'health'])->name('health');
Route::get('/files/list', [ApplicationLogsController::class, 'fileLogs'])->name('files');
Route::get('/files/view', [ApplicationLogsController::class, 'viewFile'])->name('files.view');
Route::get('/files/download', [ApplicationLogsController::class, 'downloadFile'])->name('files.download');
Route::post('/files/delete', [ApplicationLogsController::class, 'deleteFile'])->name('files.delete');
Route::post('/files/clear-all', [ApplicationLogsController::class, 'clearAllFiles'])->name('files.clear-all');
});
Tenant Context Fields
Attach extra columns (e.g. company_id) to every log record automatically.
1. Ensure the column exists in your migration
$table->foreignId('company_id')->nullable()->constrained()->nullOnDelete();
2. Create an invokable resolver class
// app/Logging/Resolvers/CompanyIdResolver.php
namespace App\Logging\Resolvers;
class CompanyIdResolver
{
public function __invoke(): ?int
{
return auth()->user()?->company_id;
}
}
Important: Use an invokable class (not a closure). Closures cannot be serialised by
php artisan config:cache.
3. Register in config
// config/applogger.php
'context_fields' => [
[
'column' => 'company_id',
'resolver' => \App\Logging\Resolvers\CompanyIdResolver::class,
],
[
'column' => 'business_id',
'resolver' => \App\Logging\Resolvers\BusinessIdResolver::class,
],
],
Dynamic Relationships
Display related model data in the log detail modal without modifying package code.
// config/applogger.php
'relationships' => [
'company' => [
'model' => \App\Models\Company::class,
'foreign' => 'company_id', // FK column on application_logs
'display' => 'name', // attribute to show in the detail view
],
'business' => [
'model' => \App\Models\Business::class,
'foreign' => 'business_id',
'display' => 'name',
],
],
The ServiceProvider registers these as runtime belongsTo relationships on ApplicationLog using resolveRelationUsing(). The controller loads them with with([...]) and renders the display attribute in the detail modal.
Log Types
Define the type vocabulary for your application:
// config/applogger.php
'types' => [
'exception' => 'Exception',
'auth' => 'Authentication',
'system' => 'System',
'database' => 'Database',
'invoice' => 'Invoice',
'payment' => 'Payment',
],
Keyword auto-detection
When a log entry has no explicit type in its context, the handler scans the message for keywords:
'type_keywords' => [
'payment' => 'payment',
'invoice' => 'invoice',
'auth' => 'auth',
'login' => 'auth',
'database' => 'database',
'query' => 'database',
],
'default_type' => 'system', // fallback when no keyword matches
First match wins (top-to-bottom). To set an explicit type, pass it in log context:
Log::error('Payment gateway timeout', ['type' => 'payment']);
Static Logging Helpers
ApplicationLog provides static helpers for direct programmatic logging (bypassing Monolog):
use FastnetKSA\AppLogger\Models\ApplicationLog;
ApplicationLog::logError('payment', 'Stripe charge failed', ['order_id' => 42], $exception);
ApplicationLog::logWarning('invoice', 'PDF generation slow', ['duration_ms' => 3200]);
ApplicationLog::logInfo('auth', 'User logged in', ['user_id' => 1]);
ApplicationLog::logDebug('system', 'Cache miss for key settings.theme');
All helpers call logEntry() internally, which resolves tenant context fields automatically.
Sensitive Key Redaction
Context keys listed in sensitive_keys are replaced with [REDACTED] before being stored:
'sensitive_keys' => [
'password',
'token',
'secret',
'api_key',
'credit_card',
],
View Customisation
Publish views to override them individually:
php artisan vendor:publish --tag=applogger-views
Files are copied to resources/views/vendor/applogger/logs/. Laravel automatically uses these instead of the package originals. You can override one file without touching the other.
Health Status API
GET {prefix}/health/status
Returns JSON:
{
"status": "healthy",
"total_logs_24h": 142,
"errors_24h": 3,
"warnings_24h": 11,
"last_error": { ... }
}
Status values: healthy / warning / critical — determined by thresholds:
'health' => [
'critical_errors' => 100, // errors/24h to trigger critical
'warning_errors' => 10, // errors/24h to trigger warning
],
Eloquent Query Scopes
use FastnetKSA\AppLogger\Models\ApplicationLog;
ApplicationLog::errors()->get(); // level = error
ApplicationLog::warnings()->get(); // level = warning
ApplicationLog::level('info')->get(); // any level
ApplicationLog::type('payment')->get(); // by type
ApplicationLog::dateRange('2026-01-01', '2026-01-31')->get(); // date range
Security Notes
- File path traversal protection:
viewFile,downloadFile, anddeleteFilevalidate that the resolved real path starts withstorage_path('logs'). Requests with../style paths are rejected with 404. laravel.logis protected from deletion bydeleteFile.clearAllFilestruncates it to empty rather than deleting.- Sensitive context keys are redacted at write time — they are never stored even in the
contextJSON column.
Package Structure
laravel-applogger/
├── composer.json
├── config/
│ └── applogger.php ← default config (publish to override)
├── database/
│ └── migrations/
│ └── ..._create_application_logs_table.php
├── resources/
│ └── views/
│ └── logs/
│ ├── index.blade.php ← database log list + dashboard
│ └── files.blade.php ← file log manager
├── routes/
│ └── applogger.php
└── src/
├── AppLoggerServiceProvider.php
├── Http/
│ └── Controllers/
│ └── ApplicationLogsController.php
├── Logging/
│ ├── CreateDatabaseLogger.php ← Monolog factory
│ └── DatabaseLogHandler.php ← Monolog handler → DB
└── Models/
└── ApplicationLog.php
Publishable Tags
| Tag | What it publishes |
|---|---|
applogger-config |
config/applogger.php |
applogger-migrations |
database/migrations/..._create_application_logs_table.php |
applogger-views |
resources/views/vendor/applogger/logs/ |
License
MIT