3 min read
Logging
Laravel wraps the popular Monolog library behind a clean, unified interface. You get multiple log channels out of the box — file, single, daily, syslog, errorlog, and others — and you can stack or aggregate them however you like.
Log Levels
Laravel supports the standard PSR-3 log levels, from least to most severe:
debug— detailed diagnostic informationinfo— general operational eventswarning— something unexpected happened, but the application can continueerror— a runtime error that prevents a specific operationcritical— a serious error; the application may be unable to continuealert— action must be taken immediatelyemergency— the system is unusable
Basic Usage
The Log facade gives you immediate access to the default channel:
use Illuminate\Support\Facades\Log; Log::info('User logged in', ['user_id' => $user->id]); Log::warning('Disk space running low', ['percent_used' => 87]); Log::error('Payment failed', ['order_id' => $order->id, 'reason' => $e->getMessage()]);
The second argument is a context array. It gets serialised alongside the message and makes logs searchable and debuggable.
Configuring Channels
Log channels are defined in config/logging.php. Laravel ships with several defaults:
// config/logging.php return [ 'default' => env('LOG_CHANNEL', 'stack'), 'channels' => [ 'stack' => [ 'driver' => 'stack', 'channels' => ['single', 'slack'], ], 'single' => [ 'driver' => 'single', 'path' => storage_path('logs/laravel.log'), 'level' => 'debug', ], 'daily' => [ 'driver' => 'daily', 'path' => storage_path('logs/laravel.log'), 'level' => 'info', 'days' => 14, ], 'slack' => [ 'driver' => 'slack', 'url' => env('SLACK_WEBHOOK_URL'), 'level' => 'critical', ], ], ];
The stack channel fans a single log call out to multiple channels. This is the recommended default for production — write to a file and alert on Slack for critical errors, for example.
Selecting a Channel at Runtime
You can target a specific channel without changing the default:
Log::channel('daily')->info('This goes only to the daily file'); Log::channel('slack')->critical('Alerting the team via Slack');
Contextual Logging
Attach context that applies to every subsequent log call within a block using withContext:
Log::withContext(['request_id' => $requestId, 'user_id' => auth()->id()]); // All log calls in this request now include request_id and user_id Log::info('Processing order'); Log::error('Something went wrong');
For even more granular control, create a logger instance scoped to a specific channel and context:
$logger = Log::channel('daily'); $logger->info('Order created', ['order_id' => $order->id]);
Writing a Custom Log Channel
If none of the built-in drivers suit your needs, create a custom channel using a handler class:
// config/logging.php 'datadog' => [ 'driver' => 'custom', 'via' => App\Logging\DatadogLogger::class, ],
namespace App\Logging; use Monolog\Logger; class DatadogLogger { public function __invoke(array $config): Logger { $logger = new Logger('datadog'); $logger->pushHandler(new \App\Logging\DatadogHandler()); return $logger; } }
Tips
- Always pass context as the second argument rather than embedding variables in the message string. It makes logs structured and machine-parseable.
- Set the log level for each channel appropriately. You rarely want
debuglevel in production — it generates enormous amounts of output. - In CI and containerised deployments, the
errorlogdriver writes to PHP's error output, which your orchestrator (Docker, Kubernetes) can capture and forward. - Rotate logs in production. The
dailydriver handles this automatically, keeping only the number of days you configure.