Stop grepping through flat files. Learn to structure Laravel logs for searchability and integrate them with the ELK stack for production-grade observability.
Previously in this course, we discussed handling webhooks securely. While that lesson focused on maintaining integrity during external communication, today we shift our focus to how we actually see that activity inside our infrastructure. In distributed systems, relying on local log files is a recipe for disaster; this lesson teaches you to centralize and structure your logs for professional-grade observability.
In a monolithic application, you might get away with Log::info('User login'). In a distributed SaaS architecture, that string is useless. When a request traverses multiple services, you need context: trace_id, user_id, tenant_id, and execution_time.
Structured logging transforms these "human-readable" strings into machine-readable JSON objects. This allows the ELK stack (Elasticsearch, Logstash, Kibana) to index specific fields, enabling you to run queries like: "Show me all failed billing attempts for Tenant X in the last 10 minutes."
To achieve observability, we need a pipeline:
Laravel uses Monolog under the hood. We’ll inject a custom formatter to ensure every log entry carries our required metadata.
First, create a ContextualLogFormatter that forces JSON output with a consistent schema:
PHPnamespace App\Logging; use Monolog\Formatter\JsonFormatter; use Monolog\LogRecord; class ContextualLogFormatter extends JsonFormatter { public function format(LogRecord $record): string { $record->extra = [ 'tenant_id' => tenant()?->id, 'request_id' => request()->header('X-Request-ID'), 'environment' => config('app.env'), ]; return parent::format($record); } }
Next, configure your logging.php to use this formatter for your stack or a custom channel:
PHP'channels' => [ 'elk' => [ 'driver' => 'single', 'path' => storage_path('logs/laravel-elk.log'), 'level' => 'debug', 'formatter' => \App\Logging\ContextualLogFormatter::class, ], ],
Once your logs are writing JSON to a file, Logstash needs to pick them up. Create a configuration file (logstash.conf) on your application server:
CONFinput { file { path => "/var/www/html/storage/logs/laravel-elk.log" codec => "json" } } filter { # Add logic to parse specific fields or drop noisy logs } output { elasticsearch { hosts => ["https://your-elasticsearch-cluster:9200"] index => "laravel-logs-%{+YYYY.MM.dd}" } }
ContextualLogFormatter shown above and register it in your logging.php file.Log::channel('elk')->info('Payment processed', ['amount' => 500]);.storage/logs/laravel-elk.log to confirm the JSON output includes the tenant_id and request_id you injected.daily driver) or use a tool like Filebeat that handles rotation gracefully without losing data.tap to redact sensitive keys like password or credit_card_number before the logs hit the disk.We’ve moved from basic file logging to a structured approach that makes production debugging possible. By standardizing your JSON schema and routing logs through Logstash, you gain the ability to aggregate, filter, and alert on system events with surgical precision. For more on the broader landscape of telemetry, you may find our previous discussions on observability and logging or advanced error handling useful for contrast.
Up next: We will dive into Database Indexing for Joins, where we'll analyze execution plans to ensure our queries—and our logs—stay performant as the data grows.
Master production-grade logging and monitoring in Laravel. Learn to configure custom log channels, track application errors, and integrate external services.
Read moreMaster non-breaking migrations and safe rollback procedures. Learn the expand-and-contract pattern to evolve your database schema without production downtime.
Advanced Logging Patterns
Handling Large Data Exports
Security Header Configuration
Database Sharding Concepts
Real-time Data Synchronization
Database Deadlock Prevention
Managing Third-Party API Integrations