Logging in Laravel
An overview of Laravel's logging system covering log levels, channels, stacking multiple channels, and best practices for writing structured log messages.
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.
Newsletter
A weekly newsletter on React, Next.js, AI-assisted development, and engineering. No spam, unsubscribe any time.