Render dan report adalah dua urusan yang berbeda. Render menjawab “apa yang dilihat pengguna” — halaman 404, JSON 422 — sedangkan report menjawab “apa yang kita, pengembang, ketahui dari insiden tersebut”: baris log, notifikasi Slack, atau tiket di Sentry. Chapter Exception Handling
sudah menunjukkan sisi render; sekarang kita bedah sisi report: daftar exception yang tidak dilaporkan, callback reportable() dengan konteks tambahan, dan helper report() untuk melaporkan secara manual dari kode mana pun. Semua output di bawah diambil dari log sungguhan di sandbox.
Daftar dontReport Bawaan #
Tidak semua exception pantas memenuhi log. Pengguna yang salah ketik URL memicu ModelNotFoundException beratus-ratus kali sehari, dan itu bukan insiden — hanya lalu lintas normal internet. Karena itu handler bawaan Laravel diam-diam melewati daftar berikut saat melaporkan (dirangkum dari properti $internalDontReport di Illuminate\Foundation\Exceptions\Handler):
AuthenticationException,AuthorizationExceptionHttpException,HttpResponseExceptionModelNotFoundException,RecordNotFoundException,RecordsNotFoundExceptionBackedEnumCaseNotFoundException,OriginMismatchExceptionRequestExceptionInterface,TokenMismatchException,ValidationException
Kita buktikan dengan feature test: request ke route yang melempar ModelNotFoundException menghasilkan 404, lalu kita periksa keberadaan file log.
public function test_404_tidak_direport(): void
{
$this->get('/post/7');
fwrite(STDERR, 'ada_log_setelah_404: '
. var_export(File::exists(storage_path('logs/laravel.log')), true) . "\n");
$this->assertFalse(File::exists(storage_path('logs/laravel.log')));
}Output yang Dihasilkan:
ada_log_setelah_404: falseFile log bahkan tidak pernah dibuat — 404 benar-benar sunyi. Sebaliknya, RuntimeException dari kode aplikasi kita ditulis ke log lengkap dengan class, pesan, file, baris, dan stack trace-nya. Daftar ini bisa kita perluas sendiri: $exceptions->dontReport(...) di withExceptions() menyembunyikan exception tertentu dari pelaporan, sementara $exceptions->dontReportDuplicates() membuat exception yang sama tidak dilaporkan berulang-ulang dalam satu proses.
reportable() dengan Konteks Khusus #
Kadang satu jenis exception membutuhkan perlakuan khusus. Contoh klasiknya: StokHabisException — bukan insiden teknis yang butuh stack trace, melainkan peristiwa bisnis yang ingin dipantau tim gudang. Kita daftarkan callback-nya di bootstrap/app.php:
use App\Exceptions\StokHabisException;
use Illuminate\Support\Facades\Log;
$exceptions->reportable(function (StokHabisException $e) {
Log::warning('Stok habis saat memproses order.', $e->konteks());
});Class exception-nya sendiri sederhana — method konteks() menyediakan informasi yang relevan:
namespace App\Exceptions;
use Exception;
class StokHabisException extends Exception
{
public function konteks(): array
{
return ['produk' => 'Buku "Belajar Laravel"', 'stok' => 0];
}
}Route /order/buat melempar exception tersebut, dan feature test memeriksa hasilnya di storage/logs/laravel.log:
public function test_stok_habis_direport_dengan_konteks(): void
{
$response = $this->get('/order/buat');
fwrite(STDERR, 'status: ' . $response->status() . "\n");
fwrite(STDERR, "log:\n" . trim(File::get(storage_path('logs/laravel.log'))) . "\n");
$this->assertSame(500, $response->status());
}Output yang Dihasilkan:
status: 500
log:
[2026-10-13 19:50:03] testing.WARNING: Stok habis saat memproses order. {"produk":"Buku \"Belajar Laravel\"","stok":0}
[2026-10-13 19:50:03] testing.ERROR: Stok buku "Belajar Laravel" habis. {"exception":"[object] (App\\Exceptions\\StokHabisException(code: 0): Stok buku \"Belajar Laravel\" habis. at /home/z/my-project/work/sandboxes/w10-c/routes/web.php:56)
[stacktrace]
#0 ...
"}Perhatikan dua baris yang muncul: baris WARNING dari callback kita dengan konteks produk dan stok, lalu baris ERROR bawaan berisi stack trace. Perilaku ini penting dipahami — callback reportable() berjalan tambahan di samping pelaporan bawaan, bukan menggantikannya. Pada Laravel 13, menghentikan pelaporan bawaan dilakukan dengan menautkan ->stop():
$exceptions->reportable(function (StokHabisException $e) {
Log::warning('Stok habis saat memproses order.', $e->konteks());
})->stop();Output yang Dihasilkan (request yang sama diulang):
status: 500
log:
[2026-10-13 19:50:38] testing.WARNING: Stok habis saat memproses order. {"produk":"Buku \"Belajar Laravel\"","stok":0}Baris ERROR bawaan lenyap — hanya laporan bisnis yang rapi yang tersisa. Callback juga bisa mengembalikan false untuk efek serupa.
Helper report() dari Mana Pun #
Untuk exception yang tertangkap try/catch tetapi tetap ingin tercatat, Laravel menyediakan helper global report(). Panggilan helper ini menjalankan pipeline reporting yang sama persis — tanpa melempar ulang exception ke pengguna:
public function test_report_helper(): void
{
report(new \RuntimeException('Koneksi ke layanan kurir terputus.'));
$log = trim((string) File::get(storage_path('logs/laravel.log')));
fwrite(STDERR, "log:\n" . strtok($log, "\n") . "\n");
$this->assertStringContainsString('ERROR', $log);
}Output yang Dihasilkan:
log:
[2026-10-13 19:50:03] testing.ERROR: Koneksi ke layanan kurir terputus. {"exception":"[object] (RuntimeException(code: 0): Koneksi ke layanan kurir terputus. at ..."}Helper yang sama berguna untuk pola re-reporting: menangkap exception, mencatatnya, lalu melempar exception lain yang lebih bermakna bagi domain aplikasi.
Setelah laporan tertulis di storage/logs/laravel.log, pertanyaan berikutnya adalah bagaimana log tersebut tersusun — channel apa, format apa, siapa yang menulisnya. Chapter Logging Configuration
membedah config/logging.php dari baris pertama. Referensi API pelaporan lengkap tersedia di dokumentasi Errors
.