Mencetak pesan ke layar hanya membantu saat kita sedang mengawasi terminal sendiri. Aplikasi produksi berjalan berhari-hari tanpa ada yang menatap layarnya, dan kegagalan yang muncul tengah malam akan hilang begitu layar bergulur. Yang dibutuhkan adalah jejak permanen: catatan kejadian yang bisa dibaca ulang kapan pun, siapa pun yang mencari. Aktifitas mencatat ini disebut logging, dan PHP menyediakannya mulai dari function error_log() yang sederhana sampai directive konfigurasi yang mengatur log PHP sendiri. Chapter ini membahas keduanya, lalu menutup dengan format pesan terstruktur dan library Monolog yang menjadi standar industri.
Mencatat ke Berkas dengan error_log() #
Function error_log() menerima pesan, tipe tujuan, dan lokasi tujuan. Tipe 3 adalah yang paling sering dipakai aplikasi: menambahkan pesan ke sebuah berkas tanpa pernah menghapus isinya — setiap panggilan menumpuk satu baris baru di akhir berkas:
<?php
$berkasLog = sys_get_temp_dir() . "/aplikasi-kasir.log";
@unlink($berkasLog);
error_log("Pengguna Budi berhasil login\n", 3, $berkasLog);
error_log("Stok buku tinggal 3 buah\n", 3, $berkasLog);
echo "Isi berkas log sekarang:\n";
echo file_get_contents($berkasLog);
unlink($berkasLog);Output yang Dihasilkan:
Isi berkas log sekarang:
Pengguna Budi berhasil login
Stok buku tinggal 3 buahMari kita telaah baris-baris kode di atas:
- Baris 2: berkas log diletakkan di direktori sementara sistem lewat
sys_get_temp_dir()— cukup untuk latihan tanpa mengotori folder proyek. - Baris 4:
@unlink()menghapus bekas latihan sebelumnya supaya isi log selalu dimulai dari kosong; penekan@menyembunyikan laporan ketika berkasnya memang belum ada. - Baris 6-7: dua panggilan
error_log()dengan tipe3menambahkan dua pesan berturut-turut — karakter\ndi akhir setiap pesan adalah pemisah baris yang kita tulis sendiri, karenaerror_log()tidak menambahkannya secara otomatis. - Baris 10-11: isi berkas dibaca kembali untuk membuktikan kedua pesan tersimpan, lalu berkas dibersihkan.
Perilaku menumpuk tanpa menghapus inilah yang membuat log menjadi jejak sejarah: pesan dari Senin tetap utuh ketika pesan Selasa ditulis.
Empat Tujuan Pesan yang Didukung #
error_log() mengenal empat tipe tujuan, dan memilihnya dengan tepat menentukan ke mana pesan Anda berakhir:
| Tipe | Tujuan | Catatan |
|---|---|---|
0 | System logger PHP | Masuk ke mekanisme log sistem sesuai directive error_log; perilakunya bergantung SAPI dan sistem operasi |
1 | Email ke alamat tertentu | Alamat tujuan dikirim sebagai parameter keempat; membutuhkan mail server yang terpasang dan kini jarang dipakai |
3 | Berkas yang kita tentukan | Menambahkan pesan apa adanya; pilihan paling umum dan paling mudah dikendalikan |
4 | Log SAPI | Menambahkan pesan ke log web server (misalnya error log nginx); berguna bila Anda tidak punya akses penuh ke sistem berkas |
Tipe 3 menjadi favorit karena perilakunya dapat diprediksi persis: satu panggilan, satu tambahan baris, di berkas pilihan kita sendiri. Tipe 0 dan 4 menyerahkan format dan lokasi kepada lingkungan server, sehingga hasilnya berbeda antar instalasi. Tipe 1 terdengar praktis untuk peringatan mendesak, tetapi mengirim email langsung dari dalam kode membuat pengujian sulit dan berisiko membanjiri kotak masuk saat error terjadi berulang.
Directive error_log di php.ini #
Selain function, PHP juga bisa mencatat error miliknya sendiri secara otomatis melalui dua directive yang telah kita kenal pada chapter error-reporting
. Ketika log_errors aktif, setiap error yang dilaporkan PHP — warning, fatal, atau notice — ikut tercatat ke tujuan yang ditentukan directive error_log:
log_errors = On
error_log = /var/log/aplikasi/php-error.logDua baris ini menjadikan PHP pencatat setia yang bekerja bahkan untuk kegagalan yang tidak tersentuh kode kita, misalnya ekstensi yang gagal dimuat atau skrip yang mati karena kehabisan memori. Pastikan direktori tujuannya dapat ditulis oleh pengguna yang menjalankan PHP, dan letakkan di luar folder yang bisa diakses pengunjung web agar isinya tidak bisa dibaca umum. Tanpa directive error_log, PHP menyerahkan pencatatan kepada log bawaan SAPI — pada web server biasanya berakhir di error log-nya.
Pada lingkungan produksi, kombinasi yang benar adalah display_errors = Off dan log_errors = On: pengunjung tidak melihat pesan mentah, sementara developer tetap mendapat catatan lengkap. Strategi lengkapnya dirangkum pada chapter production-error-handling
.
Format Pesan yang Mudah Ditelusuri #
Log hanya berguna bila barisnya mudah disaring dan diurutkan. Baris log yang baik memuat empat unsur: waktu kejadian, tingkat keseriusan (level), pesan inti, dan konteks tambahan berbentuk pasangan kunci-nilai. Mari kita bungkus aturan itu menjadi sebuah function tulisLog():
<?php
function tulisLog(string $level, string $pesan, array $konteks = []): void
{
$waktu = new DateTimeImmutable(
"2026-01-15 08:30:00",
new DateTimeZone("Asia/Jakarta")
);
$baris = sprintf(
"[%s] %s: %s",
$waktu->format("Y-m-d H:i:s"),
strtoupper($level),
$pesan
);
if ($konteks !== []) {
$baris .= " " . json_encode($konteks, JSON_UNESCAPED_UNICODE);
}
error_log($baris . "\n", 3, sys_get_temp_dir() . "/kasir-terstruktur.log");
}
$berkasLog = sys_get_temp_dir() . "/kasir-terstruktur.log";
@unlink($berkasLog);
tulisLog("info", "Pengguna Budi berhasil login", ["user_id" => 42, "ip" => "192.168.1.10"]);
tulisLog("warning", "Stok buku menipis", ["produk" => "B001", "sisa" => 3]);
echo file_get_contents($berkasLog);
unlink($berkasLog);Output yang Dihasilkan:
[2026-01-15 08:30:00] INFO: Pengguna Budi berhasil login {"user_id":42,"ip":"192.168.1.10"}
[2026-01-15 08:30:00] WARNING: Stok buku menipis {"produk":"B001","sisa":3}Mari kita telaah bagian-bagian utamanya:
- Baris 3-8: waktu kejadian dibekukan pada nilai tetap agar output latihan ini konsisten setiap dijalankan — pada aplikasi nyata, gunakan
new DateTimeImmutable("now", new DateTimeZone("Asia/Jakarta"))agar tercatat waktu sebenarnya. - Baris 10-15:
sprintf()merangkai waktu, level yang dihurufbesarkan, dan pesan ke dalam satu format tetap"[waktu] LEVEL: pesan". - Baris 17-19: konteks opsional dirangkai sebagai JSON lewat
json_encode()— format terstruktur yang nantinya mudah disaring mesin, danJSON_UNESCAPED_UNICODEmenjaga teks Indonesia tetap terbaca manusia. - Baris 21: baris jadi dikirim ke berkas log dengan tipe
3, diakhiri\n.
Tingkat keseriusan yang lazim dipakai industri cukup sedikit dan mudah disaring:
| Level | Makna | Contoh kejadian |
|---|---|---|
DEBUG | Rincian teknis untuk penyelidikan | Isi variabel saat menelusuri bug |
INFO | Kejadian normal yang penting | Pengguna berhasil login |
WARNING | Kondisi mencurigakan tapi tak fatal | Stok menipis, request lambat |
ERROR | Kegagalan yang mengganggu fitur | Gagal menyimpan pesanan ke database |
Membaca Log di Terminal #
Berkas log paling sering dibaca dari terminal dengan dua senjata andalan. Perintah tail -f menampilkan baris terakhir sebuah berkas lalu terus mengikuti baris baru yang masuk secara langsung:
tail -f /var/log/aplikasi/php-error.logPerintah ini adalah cara klasik mengawasi aplikasi sambil dites: setiap error baru muncul di terminal Anda hanya beberapa detik setelah terjadi. Untuk mencari kejadian masa lalu, grep menyaring baris yang memuat teks tertentu, lalu tail membatasi hasilnya ke yang terbaru:
grep "ERROR" /var/log/aplikasi/php-error.log | tail -20Monolog untuk Kebutuhan yang Serius #
Semakin besar aplikasi, kebutuhan logging juga tumbuh: berkas log perlu dirotasi agar tidak membengkak, satu pesan kadang perlu dikirim ke beberapa tujuan sekaligus, dan tiap bagian aplikasi ingin punya saluran (channel) tersendiri. Library Monolog menjawab semua itu dan menjadi standar de facto di ekosistem PHP — framework Symfony dan Laravel memakainya di balik layar:
composer require monolog/monolog<?php
require "vendor/autoload.php";
use Monolog\Level;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
$log = new Logger("aplikasi-kasir");
$log->pushHandler(new StreamHandler(__DIR__ . "/app.log", Level::Warning));
$log->warning("Stok buku menipis", ["produk" => "B001", "sisa" => 3]);
$log->info("Pengguna Budi berhasil login");Contoh di atas memerlukan pemasangan Composer sehingga tidak bisa dijalankan langsung oleh alat verifikasi buku ini, tetapi idenya lurus: sebuah Logger bernama aplikasi-kasir dibuat, lalu sebuah StreamHandler dipasang untuk menulis ke app.log dengan ambang Level::Warning. Panggilan warning() tercatat karena levelnya sama atau lebih serius dari ambang, sedangkan info() dilewati. Handler bertugas ke tujuan mana pesan mengalir — Monolog menyediakan handler untuk berkas, syslog, Slack, email, dan banyak lagi — sehingga arsitektur “level + konteks + tujuan” yang kita bangun manual di awal chapter terwujud rapi dalam bentuk siap pakai.
Ringkasan #
Logging mengubah kegagalan sesaat menjadi jejak permanen yang bisa ditelusuri mundur. Function error_log() dengan tipe 3 menambahkan pesan ke berkas pilihan kita, directive log_errors dan error_log membuat PHP mencatat kegagalannya sendiri, dan format terstruktur — waktu, level, pesan, konteks — menjadikan setiap baris mudah disaring dan diurutkan. Untuk kebutuhan besar, Monolog menyediakan rotasi, banyak tujuan, dan channel dalam satu paket mapan. Dengan catatan yang lengkap, kini kita siap menyusun strategi menyeluruh: apa yang boleh dilihat pengunjung, dan apa yang disimpan untuk developer — topik penutup section ini.