Logging
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.