paulund

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 information
  • info — general operational events
  • warning — something unexpected happened, but the application can continue
  • error — a runtime error that prevents a specific operation
  • critical — a serious error; the application may be unable to continue
  • alert — action must be taken immediately
  • emergency — 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 debug level in production — it generates enormous amounts of output.
  • In CI and containerised deployments, the errorlog driver writes to PHP's error output, which your orchestrator (Docker, Kubernetes) can capture and forward.
  • Rotate logs in production. The daily driver handles this automatically, keeping only the number of days you configure.