Satu file log raksasa bukan satu-satunya cara menyimpan riwayat aplikasi. Log transaksi bisnis mungkin perlu file harian yang dirotasi otomatis, error produksi perlu terkirim ke Slack, sedangkan log server milik ops team hidup di syslog. Laravel mengakomodasi semua selera itu lewat konsep channel: setiap channel adalah satu strategi penulisan log dengan driver, path, dan level-nya sendiri. Pada chapter ini kita petakan seluruh channel bawaan, membuktikan bagaimana channel stack menggabungkan beberapa channel sekaligus, membandingkan single dengan daily, lalu meracik channel kustom dengan driver monolog.

Taksonomi Channel Bawaan #

File config/logging.php hadir dengan sebelas channel siap pakai. Tabel berikut merangkum perannya:

ChannelDriverPerilakuCocok untuk
stackstackMenggabung beberapa channel jadi satuKomposisi fleksibel
singlesingleSatu file laravel.logAplikasi kecil, lingkungan lokal
dailydailyFile per hari laravel-YYYY-MM-DD.log + retensiServer produksi biasa
monthlymonthlyFile per bulan, retensi 3 fileVolume rendah, arsip panjang
syslogsyslogLog sistem operasiBerbagi log dengan tool server
errorlogerrorlogPHP error_logIntegrasi infrastruktur lama
stderrmonologMenulis ke php://stderrContainer, Kubernetes
slackslackWebhook ke workspace SlackNotifikasi error tim
papertrailmonologSyslog UDP/TLS ke PapertrailLog hosting terkelola
nullmonologMembuang semua logTest, benchmark
emergency— (fallback)File darurat saat channel utama gagalJaring pengaman

Channel stderr dan papertrail memperlihatkan pola penting: keduanya memakai driver monolog — pintu masuk ke seluruh ekosistem handler Monolog, termasuk yang kita tulis sendiri.

Channel stack: Komposisi Berlapis #

Channel stack tidak menulis ke mana pun; tugasnya mengulang pesan ke setiap channel anggotanya. Konfigurasi default .env mengarah LOG_CHANNEL=stack dengan LOG_STACK=single, jadi praktisnya satu tujuan. Menambahkan daily cukup mengubah satu baris .env:

dotenv
LOG_CHANNEL=stack
LOG_STACK=single,daily

Perilaku berlapis ini bisa kita coba tanpa menyentuh .env memakai Log::build():

php
use Illuminate\Support\Facades\Log;

Log::channel('daily')->info('Order Budi diproses lewat channel daily.');
Log::info('Order Budi diproses lewat channel default (stack).');

$gabungan = Log::build([
    'driver' => 'stack',
    'channels' => ['single', 'daily'],
]);
$gabungan->warning('Stok buku menipis — ditulis ke dua channel.');

Output yang Dihasilkan:

text
--- isi storage/logs ---
laravel-2026-10-13.log
laravel.log

--- laravel.log ---
[2026-10-13 19:53:39] local.INFO: Order Budi diproses lewat channel default (stack).  
[2026-10-13 19:53:39] local.WARNING: Stok buku menipis — ditulis ke dua channel.  

--- laravel-2026-10-13.log ---
[2026-10-13 19:53:39] local.INFO: Order Budi diproses lewat channel daily.  
[2026-10-13 19:53:39] local.WARNING: Stok buku menipis — ditulis ke dua channel.  

Perhatikan pembagian kerjanya: Log::channel('daily') hanya menyentuh file harian, Log::info() default menyentuh channel stack, sedangkan $gabungan mengirim baris WARNING ke kedua file sekaligus. Opsi ignore_exceptions pada stack juga berguna — dengan true, kegagalan salah satu anggota (misalnya webhook Slack mati) tidak merusak penulisan channel lainnya.

Channel daily dan Retensi Otomatis #

Perbedaan praktis single dan daily adalah pertumbuhan file. Channel daily membuat file baru setiap hari dengan nama bertanggal, lalu otomatis menghapus file lama sesuai max_files — nilainya dibaca dari LOG_DAILY_DAYS, kita peroleh 14 sebagai default di sandbox kita:

php
'daily' => [
    'driver' => 'daily',
    'path' => storage_path('logs/laravel.log'),
    'max_files' => env('LOG_DAILY_DAYS', 14),
],

Di server produksi, pola daily membuat grep per tanggal menjadi mudah (laravel-2026-10-13.log langsung memberi tahu umur isinya) sekaligus membatasi ukuran disk. Untuk kebutuhan retensi lebih lama, naikkan LOG_DAILY_DAYS atau pindahkan log ke layanan terpusat.

Meracik Channel Kustom #

Saat tujuan yang kita inginkan tidak ada di daftar, driver monolog memungkinkan kita memakai handler Monolog apa pun. Pola konfigurasinya mengikuti channel papertrail bawaan:

php
'papertrail' => [
    'driver' => 'monolog',
    'level' => env('LOG_LEVEL', 'debug'),
    'handler' => env('LOG_PAPERTRAIL_HANDLER', SyslogUdpHandler::class),
    'handler_with' => [
        'host' => env('PAPERTRAIL_URL'),
        'port' => env('PAPERTRAIL_PORT'),
        'connectionString' => 'tls://'.env('PAPERTRAIL_URL').':'.env('PAPERTRAIL_PORT'),
    ],
    'processors' => [PsrLogMessageProcessor::class],
],

Kunci handler menunjuk class handler Monolog, handler_with mengisi parameter konstruktornya (host, port, koneksi TLS), dan processors menambahkan processor Monolog. Contoh di atas butuh server Papertrail sungguhan sehingga tidak kita uji di sini — tetapi pola driver => monolog + handler + handler_with adalah resep yang sama untuk handler Monolog mana pun, misalnya RotatingFileHandler atau handler SMTP.

Channel per Lingkungan #

Karena LOG_CHANNEL dibaca dari .env, tiap lingkungan bisa punya strategi berbeda tanpa menyentuh kode. File .env lokal kita biarkan sederhana:

dotenv
LOG_CHANNEL=single
LOG_LEVEL=debug

Sementara .env di server produksi memilih komposisi yang lebih serius:

dotenv
LOG_CHANNEL=stack
LOG_STACK=daily,slack
LOG_LEVEL=warning

Konfigurasi lokal memilih file tunggal berisi semua level debug; produksi memilih gabungan file harian dan notifikasi Slack, hanya menerima warning ke atas. Daftar lengkap opsi per driver terdapat di dokumentasi Logging . Satu hal yang belum kita bahas: baris-baris log di atas hanya berisi pesan dan konteks kecil. Bagaimana menautkan ribuan baris dari satu insiden yang sama? Itu pekerjaan Contextual Logging .