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:
| Channel | Driver | Perilaku | Cocok untuk |
|---|---|---|---|
stack | stack | Menggabung beberapa channel jadi satu | Komposisi fleksibel |
single | single | Satu file laravel.log | Aplikasi kecil, lingkungan lokal |
daily | daily | File per hari laravel-YYYY-MM-DD.log + retensi | Server produksi biasa |
monthly | monthly | File per bulan, retensi 3 file | Volume rendah, arsip panjang |
syslog | syslog | Log sistem operasi | Berbagi log dengan tool server |
errorlog | errorlog | PHP error_log | Integrasi infrastruktur lama |
stderr | monolog | Menulis ke php://stderr | Container, Kubernetes |
slack | slack | Webhook ke workspace Slack | Notifikasi error tim |
papertrail | monolog | Syslog UDP/TLS ke Papertrail | Log hosting terkelola |
null | monolog | Membuang semua log | Test, benchmark |
emergency | — (fallback) | File darurat saat channel utama gagal | Jaring 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:
LOG_CHANNEL=stack
LOG_STACK=single,dailyPerilaku berlapis ini bisa kita coba tanpa menyentuh .env memakai Log::build():
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:
--- 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:
'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:
'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:
LOG_CHANNEL=single
LOG_LEVEL=debugSementara .env di server produksi memilih komposisi yang lebih serius:
LOG_CHANNEL=stack
LOG_STACK=daily,slack
LOG_LEVEL=warningKonfigurasi 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
.