Laravel's default logging writes everything to one shared laravel.log file — a dedicated custom channel routes a specific category of event (payment processing, webhook payloads, a background job's activity) into its own separate file, genuinely easier to monitor and grep in isolation.
Defining a custom channel
// config/logging.php
'channels' => [
// ... existing channels
'payments' => [
'driver' => 'single',
'path' => storage_path('logs/payments.log'),
'level' => 'debug',
],
],
Writing to the custom channel
use Illuminate\Support\Facades\Log;
Log::channel('payments')->info('Payment processed', [
'order_id' => $order->id,
'amount' => $order->total,
'gateway' => 'stripe',
]);
Log::channel('payments') is what routes this specific call to payments.log instead of the default channel — every other Log::info()/Log::error() call elsewhere in the app, without specifying a channel, continues writing to the default laravel.log untouched.
Using a daily rotating channel instead of a single file
'payments' => [
'driver' => 'daily',
'path' => storage_path('logs/payments.log'),
'level' => 'debug',
'days' => 14,
],
The daily driver creates a new dated log file each day (payments-2026-05-01.log) and automatically deletes files older than the configured days — genuinely useful for a high-volume log that would otherwise grow into an unmanageably large single file with the single driver.
Setting a custom channel as one of several via the stack driver
'stack' => [
'driver' => 'stack',
'channels' => ['single', 'payments'],
],
The stack driver fans a single log call out to multiple underlying channels at once — this specific configuration means anything logged through the default stack also gets written to both files, useful when a log entry belongs in the general application log and a dedicated one simultaneously.
Logging structured context data
Log::channel('payments')->info('Payment failed', [
'order_id' => $order->id,
'error_code' => $exception->getCode(),
'gateway_response' => $response->json(),
]);
Passing an array as the second argument attaches structured context to the log entry — genuinely more useful than concatenating everything into one long message string, since many log viewers and log-aggregation tools can parse and filter on these structured context fields directly.
Creating a channel with a custom formatter (advanced)
'payments' => [
'driver' => 'monolog',
'handler' => Monolog\Handler\StreamHandler::class,
'with' => [
'stream' => storage_path('logs/payments.log'),
],
'formatter' => Monolog\Formatter\JsonFormatter::class,
],
Laravel's logging system is built directly on Monolog, and the monolog driver exposes this underlying flexibility — a JSON formatter (as shown) is genuinely useful when log entries are meant to be ingested by an external log-aggregation service that expects structured JSON lines rather than Laravel's default human-readable text format.
Why a dedicated channel matters beyond just organization
Beyond simply keeping logs organized, a dedicated channel with its own retention period, log level, and format lets each category of logged event be tuned independently — a payments channel might genuinely need a much longer retention period than a routine debug channel, and mixing them into one shared file makes that kind of independent tuning impossible.