laikmosh / plog
Advanced Laravel logging system with metadata capture, tagging, and powerful filtering
Requires
- php: ^8.2
- illuminate/console: ^10.0|^11.0|^12.0
- illuminate/database: ^10.0|^11.0|^12.0
- illuminate/http: ^10.0|^11.0|^12.0
- illuminate/support: ^10.0|^11.0|^12.0
- livewire/livewire: ^3.0
Requires (Dev)
- orchestra/testbench: ^8.0|^9.0
- phpunit/phpunit: ^10.0
README
Plog is a powerful Laravel logging enhancement package that captures extensive metadata, supports tagging, and provides an interactive web interface for log exploration.
Features
- Automatic Metadata Capture: User ID, Session ID, Request ID, file/line, class/method
- Request Tracking: Track logs across HTTP requests, queued jobs, and CLI commands
- Tagging System: Organize logs with tags for easy filtering
- Interactive Web Interface: Filter, search, and explore logs with Livewire + Alpine.js
- Flexible Storage: SQLite by default, configurable to any Laravel database
- Granular Retention: Configure different retention periods for different log types
Installation
composer require laikmosh/plog
Configuration
Publish the configuration file and assets:
php artisan vendor:publish --tag=plog-config php artisan vendor:publish --tag=plog-assets
Run migrations:
php artisan migrate
Environment Variables
# Enable/disable Plog PLOG_ENABLED=true # Authorized emails (comma-separated) PLOG_AUTHORIZED_EMAILS=admin@example.com,developer@example.com # Database connection (optional, defaults to SQLite) PLOG_DB_CONNECTION=plog # Default retention period PLOG_RETENTION_DAYS=7 # Enable automatic cleanup PLOG_CLEANUP_ENABLED=true
Usage
Basic Logging
All existing Laravel log calls automatically capture metadata:
Log::info('User logged in', ['user_id' => $user->id]); Log::error('Payment failed', ['order_id' => $orderId]);
Using Tags
Add tags through the plog:tags context key — the one and only tagging syntax:
use Illuminate\Support\Facades\Log; Log::info('Order processed', [ 'order_id' => $orderId, 'plog:tags' => ['payment', 'stripe'], ]); Log::error('Connection failed', ['plog:tags' => ['database', 'error']]); Log::warning('Slow query', [ 'time' => 2.5, 'plog:tags' => ['performance', 'database'], ]); // The plog:tags key is extracted and stored as queryable tags; // it never appears in the stored context data.
This works with all Laravel log methods on any channel. Because the key is a plain string literal (never a plog class constant or method), call sites survive uninstalling plog: standard Laravel simply logs the key as ordinary context, and nothing breaks. The vendor-namespaced plog: prefix keeps it from colliding with real context keys.
The same convention carries durations: pass 'plog:response_time' => $seconds in context and plog lifts it into the entry's response_time column.
Watchers
Opt-in watchers record framework activity as regular entries — filterable by tag, correlated with the request that caused them, and carrying the app call site like any other entry. Both are configured under plog.watchers and disabled by default.
Redis (plog.watchers.redis) — records commands via Laravel's CommandExecuted/CommandFailed events, tagged redis and redis:<connection>, with the duration in response_time. Guardrails: slower_than (ms threshold; 0 records everything), ignore_commands, and wildcard ignore_key_patterns (Horizon and queue chatter is ignored out of the box). Failed commands log as warnings.
Models (plog.watchers.models) — records Eloquent lifecycle events (created, updated, deleted, restored, forceDeleted — exactly the hooks observers run on), tagged eloquent and eloquent:<action>. Updates store the changed attributes (values truncated), creates store attribute names, deletes log at info level. Configure events to narrow the list and ignore to skip model classes; plog's own models are always skipped.
Viewing Logs
Access the web interface at the path configured in plog.route.path — /logs by default, overridable via PLOG_ROUTE_PATH (requires authentication and authorization).
The interface allows you to:
- Filter by level, user, request ID, session, environment, endpoint, and tags
- Search through log messages and context
- Click any field to instantly filter by that value
- View detailed log entries with full context
- Group logs by request to trace execution flow
Database Viewer
A second section of the web UI (the Database link in the header, or /logs/db) browses and edits the host app's databases:
- Left column — every configured connection (default preselected), and the selected connection's tables from live schema introspection. Tables backed by an Eloquent model show a badge; models are auto-discovered by scanning
plog.db.model_paths(defaults toapp/Models) and correlated by table + connection. - Middle column — column/operator/value filters, free-text search across text columns, sortable headers, and incremental "load more" pagination. Model-backed tables get a collapsible intel strip: relations, casts, fillable/guarded/hidden, dispatched events, observers,
rules(), global scopes. - Right column — the selected record with type-aware editable fields (bool checkboxes, datetime pickers, JSON textareas with validation, NULL toggles), Save/Delete (soft-delete aware), and relationship chips that navigate to related records. A breadcrumb trail tracks the navigation; each crumb's ▾ lists its siblings.
Behavior worth knowing:
- Listing reads through the query builder (global scopes don't hide rows; soft-deleted rows are tinted). Writes go through the model when one exists — via
forceFill, and model events, observers and the model watcher fire. If the model definesrules(), changed columns are validated before saving. - Tables without a single-column primary key are browse-only.
- Access is gated by
viewPlogDb(defaults to theviewPlogemail allowlist — an empty allowlist means everyone). SetPLOG_DB_READONLY=trueto disable all writes server-side;PLOG_DB_ENABLED=falseremoves the section entirely.
'db' => [ 'enabled' => env('PLOG_DB_ENABLED', true), 'read_only' => env('PLOG_DB_READONLY', false), 'connections' => ['only' => [], 'exclude' => []], 'model_paths' => [], // relative to base_path(); empty ⇒ app/Models 'model_exclude' => [], // class names or glob patterns 'model_cache_ttl' => 300, // seconds; 0 disables the discovery cache 'per_page' => 50, 'list_columns' => 8, 'sibling_limit' => 25, 'hidden_tables' => [], ],
On Laravel 10 without doctrine/dbal, column types can't be introspected and the viewer degrades to read-only browsing; Laravel 11+ needs nothing extra.
Advanced Configuration
Custom Database Connection
In config/plog.php:
'database' => [ 'connection' => 'mysql', // Use your app's main database 'table' => 'plog_entries', ],
Retention Policies
Configure granular retention rules:
'retention' => [ 'default_days' => 7, 'rules' => [ ['tags' => ['payment'], 'days' => 30], ['tags' => ['authentication'], 'days' => 90], ['level' => 'error', 'days' => 14], ], ],
Authorization
Control access via email whitelist:
'authorized_emails' => [ 'admin@example.com', 'developer@example.com', ],
Or customize the gate in your AuthServiceProvider:
Gate::define('viewPlog', function ($user) { return $user->hasRole('admin'); });
Request ID Tracking
Plog automatically generates and tracks request IDs across:
- HTTP requests
- Queued jobs (preserves original request ID)
- CLI commands
Access the current request ID:
use Laikmosh\Plog\Services\RequestIdService; $requestId = app(RequestIdService::class)->getRequestId();
Captured Metadata
Each log entry captures:
- Time: Timestamp with microseconds
- Level: debug, info, notice, warning, error, critical, alert, emergency
- Message: Log message
- Context: Additional data passed to the log
- User ID: Currently authenticated user
- Session ID: Current session identifier
- Request ID: Unique request identifier
- Environment: http, cli, queue, testing
- Endpoint: Route name or URI, CLI command
- File & Line: Source code location
- Class & Method: Calling class and method
- Tags: Custom tags for organization
Performance Considerations
- Logs are written synchronously by default
- Consider using a dedicated database for high-volume applications
- Indexes are automatically created for common query patterns
- Use retention policies to manage database size
License
MIT