Menjadi ahli debugging: Monolog dengan channel & structured logging, Symfony Profiler dan Web Debug Toolbar untuk inspeksi request, var-dumper untuk inspeksi variabel, serta teknik men-debug error dan performa di environment production.

Aplikasi pasti error — pertanyaannya seberapa cepat kalian menemukannya. Episode 15 membekali kalian dengan tiga senjata debugging: logging (jejak permanen), profiler (mikroskop per request), dan var-dumper (inspeksi nilai). Kemampuan men-debug dengan efisien adalah skill yang paling sering diuji di dunia kerja nyata.
Mengapa topik ini berada di tengah series, sebelum deployment? Karena mulai episode 16 (multi-environment) sampai 26 (production readiness), semua praktik kita bergantung pada kemampuan membaca apa yang terjadi di aplikasi. Kalian tidak bisa mengelola environment atau scaling tanpa bisa melihat log dan profile.
Symfony memakai Monolog untuk logging. Konsep penting: channel — alur log yang terpisah per domain. Konfigurasi di config/packages/monolog.yaml:
monolog:
channels: ['payment']
handlers:
main:
type: stream
path: '%kernel.logs_dir%/%kernel.environment%.log'
level: debug
payment:
type: rotating_file
path: '%kernel.logs_dir%/payment.log'
level: info
max_files: 14Menulis log di kode:
use Psr\Log\LoggerInterface;
public function __construct(
private readonly LoggerInterface $paymentLogger,
) {
}
public function charge(float $amount): void
{
$this->paymentLogger->info('Pembayaran diproses', [
'amount' => $amount,
'currency' => 'IDR',
'request_id' => $this->requestId,
]);
}Perhatikan dua hal: context (data terstruktur, bukan string gabung) dan channel ($paymentLogger otomatis ter-inject karena tipe variabel $paymentLogger cocok nama channel payment). Log yang bagus adalah log yang bisa difilter dan dicari.
Log string gabungan ("Charge 150000 IDR") sulit dicari dan diparsing. Structured logging memisahkan pesan dan konteks:
# Log lama (string)
[2026-08-16 10:00:00] payment.INFO: Pembayaran 150000 IDR untuk user 42
# Structured (JSON)
{"channel":"payment","level":"INFO","message":"Pembayaran diproses","context":{"amount":150000,"currency":"IDR","user_id":42}}Untuk aggregator log modern, set Monolog keluar sebagai JSON:
handlers:
main:
type: stream
path: 'php://stderr'
level: info
formatter: monolog.formatter.jsonOutput JSON siap dikirim ke sistem observability (Elasticsearch/Loki) — di-query dengan bahasa filter, bukan grep manual. Ini fondasi yang dipakai di episode 24 saat kita menambang log untuk scaling.
Di environment dev, Web Debug Toolbar muncul di bawah halaman — tetapi kekuatan sebenarnya adalah Profiler (klik ikonnya). Profiler menyimpan seluruh detail request:
| Panel | Yang diperiksa |
|---|---|
| Request | Header, parameter, session |
| Performance | Timeline render, waktu per event |
| Doctrine | Semua query + durasinya |
| Twig | Template yang dirender & waktu |
| Routing | Route yang cocok |
| Logs | Semua log request tersebut |
Pola debugging yang paling efektif: buka halaman, klik profiler, lihat panel Doctrine — query N+1 (banyak query identik) terlihat jelas. Ini jalan pintas menemukan masalah yang butuh waktu berjam-jam tanpa profiler.
dump() dan dd() adalah cara cepat memeriksa nilai:
public function show(Article $article): Response
{
dump($article->getAuthor()); // tampilkan nilai, lanjutkan eksekusi
dd($article->getTags()); // tampilkan dan hentikan (die)
// ...
}Di dev, output muncul di Web Debug Toolbar; di CLI, memakai format yang rapi. Kapan memakai yang mana:
| Perintah | Perilaku | Cocok untuk |
|---|---|---|
dump() | Cetak & lanjut | Menebak alur data |
dd() | Cetak & hentikan | Investigasi titik mati |
Warning
Jangan pernah membiarkan dump()/dd() tertinggal di kode production. Konvensi: sebelum commit, pastikan tidak ada dd( tersisa — beberapa tim memakai linter atau CI check. dump() di APP_ENV=prod tidak menampilkan apa pun, tapi tetap membuang waktu eksekusi.
Di produksi, profiler tidak aktif — jadi strateginya berubah:
APP_ENV=prod + log level info — log jadi satu-satunya sumber utama.var/log/prod.log — caranya: filter per request dengan request id yang sudah kita log di context.bin/console di server — jalankan command yang sama dengan env prod untuk melihat error langsung.Untuk error yang hanya muncul sekali-sekali, context yang lengkap (request id, user id, payload) adalah pembeda antara "tahu bahwa error" dan "bisa reproduce error".
php bin/console lint:twig templates # syntax template
php bin/console lint:yaml config # syntax yaml
php bin/console lint:container # validasi container
php bin/console debug:container # inspect service
php bin/console debug:router # inspect route
php bin/console doctrine:query:sql "SELECT 1" # cek query di DBLint commands menangkap kesalahan sebelum masuk runtime — biasakan menjalankannya sebelum commit, terutama saat mengubah file YAML atau Twig.
error untuk kegagalan, warning untuk kondisi tidak ideal, info untuk jejak normal. Jangan menumpuk semua di error.LoggerInterface $paymentLogger, tapi pastikan dideklarasikan di monolog.channels jika butuh handler khusus.Pada episode 15 ini, kalian telah menjadi detektif aplikasi.
Inti yang harus dibawa pulang:
dump() vs dd(); jangan pernah biarkan tertinggal di production.Di episode 16 selanjutnya kita mengelola perbedaan lingkungan: Config Management & Environments — .env & Dotenv, env vars, konfigurasi per-environment, dan Symfony Secrets Vault untuk rahasia production. Sampai jumpa di episode 16!