Advanced Logging Patterns: Centralizing Laravel Logs for ELK Observability
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.
Why Structured Logging Matters
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."
Designing Your Logging Architecture
To achieve observability, we need a pipeline:
- Emitter: Laravel sends JSON logs via a custom Monolog formatter.
- Transport: A local Logstash agent or Filebeat reads these logs.
- Indexer: Logstash parses the JSON and pushes it to Elasticsearch.
- Visualizer: Kibana provides the dashboarding interface.
The Worked Example: Customizing Monolog
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, ], ],
Integrating with Logstash
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}" } }
Hands-on Exercise
- Implement the Formatter: Create the
ContextualLogFormattershown above and register it in yourlogging.phpfile. - Test the Output: Run a sample log command:
Log::channel('elk')->info('Payment processed', ['amount' => 500]);. - Verify: Check
storage/logs/laravel-elk.logto confirm the JSON output includes thetenant_idandrequest_idyou injected. - Logstash Setup: If you have a local Docker instance of ELK, point a Logstash input at this file and verify the logs appear in your Kibana index pattern.
Common Pitfalls
- Log Bloat: Structured logs are more verbose than plain text. Ensure you have log rotation configured (
dailydriver) or use a tool like Filebeat that handles rotation gracefully without losing data. - Sensitive Data Leakage: Never log PII (Personally Identifiable Information) in your structured logs. Use a
tapto redact sensitive keys likepasswordorcredit_card_numberbefore the logs hit the disk. - Performance Overhead: Formatting complex objects into JSON on every log call adds latency. In high-traffic systems, consider offloading the log writing to a non-blocking queue or a sidecar process.
Recap
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.
Work with me

Laravel REST API Development
Clean, secure, well-documented Laravel REST APIs — the backend engine for your app, mobile client, or SaaS. Built by an API specialist.

FilamentPHP Admin Panel & Dashboard Development
A powerful admin panel for your Laravel app — built with FilamentPHP so you can manage everything without touching the database.