Server yang berjalan berbulan-bulan meninggalkan jejak tertulis dari setiap kejadian pentingnya. Ketika aplikasi melapor gagal login pukul tiga pagi, atau laporan tahunan tiba-tiba lambat, pertanyaan pertama administrator bukan “parameter mana yang salah”, melainkan “apa kata server log”. PostgreSQL secara bawaan menulis semua lognya ke standard error proses server, lalu menyerahkan pengelolaan file itu kepada lingkungan sekitar — service manager, pg_ctl, atau logging collector internal. Chapter ini berfokus pada sisi pembaca: ke mana baris log mengalir, bagaimana anatomi satu baris, apa makna tingkatan pesan DEBUG sampai PANIC, dan bagaimana alur kerja investigasi saat ERROR bermunculan. Penyetelan apa yang dicatat — log_statement, log_min_duration_statement, dan kawan-kawannya — dibahas di chapter Logging Configuration
.
Log Mengalir ke stderr, Bukan Langsung ke File #
Nilai bawaan log_destination adalah stderr: setiap pesan log dikirim ke standard error proses server, dan PostgreSQL sendiri tidak peduli ke mana aliran itu berakhir. Pada server latihan kita, pg_ctl start mengarahkan stderr ke file /home/z/my-project/pgrun/pg.log, sehingga seluruh contoh log di chapter ini adalah potongan nyata dari file itu. Alternatif internalnya adalah logging_collector: proses penampung yang menangkap stderr, menuliskannya ke file dengan pola nama dan rotasi otomatis. Query berikut membaca seluruh parameter yang menentukan arsitektur tersebut:
-- Ke mana baris log ditulis
SHOW logging_collector;
SHOW log_destination;
SHOW log_directory;
SHOW log_filename;
-- Kapan file log diganti
SHOW log_rotation_age;
SHOW log_rotation_size;
-- Pola awalan setiap baris log
SHOW log_line_prefix;Output yang Dihasilkan:
logging_collector
-------------------
off
(1 row)
log_destination
-----------------
stderr
(1 row)
log_directory
---------------
log
(1 row)
log_filename
--------------------------------
postgresql-%Y-%m-%d_%H%M%S.log
(1 row)
log_rotation_age
------------------
1d
(1 row)
log_rotation_size
-------------------
10MB
(1 row)
log_line_prefix
-----------------
%m [%p]
(1 row)Mari kita bedah tujuh baris output itu satu per satu:
logging_collector = off: tidak ada proses penampung internal yang berjalan, jadi parameterlog_directory,log_filename, dan dua parameter rotasi di bawahnya sedang tidak aktif di server latihan — nilainya tetap terbaca karena merupakan konfigurasi siap pakai.log_destination = stderr: pesan log ditulis ke standard error — itulah sebabnya filepg.logtercipta bukan oleh PostgreSQL, melainkan oleh pengalihan yang dilakukanpg_ctl.log_directory = logdanlog_filename = postgresql-%Y-%m-%d_%H%M%S.log: saat collector diaktifkan, file log dikumpulkan di subfolderlogdi dalam data directory dengan nama yang memuat tanggal dan jam pembuatannya (polastrftime).log_rotation_age = 1ddanlog_rotation_size = 10MB: file log baru dibuat sekali per hari, atau lebih cepat bila file yang aktif menembus 10 MB — rotasi mencegah satu file tumbuh tak terkendali.log_line_prefix = %m [%p]: setiap baris diawali stempel waktu berzona lalu PID proses dalam kurung siku — pola ini dibedah tuntas di bagian anatomi.
Menaikkan logging_collector memerlukan restart server, itulah sebabnya server latihan tetap memakai stderr. Format terstruktur csvlog dan jsonlog juga hanya tersedia bersama collector — keduanya dibahas ringkas di akhir chapter ini dan detail penyetelannya di Logging Configuration
.
Anatomi Satu Baris Log #
Tiga baris berikut diambil apa adanya dari file log server latihan — satu kegagalan query, satu checkpoint rutin, dan satu akhir sesi:
2026-10-14 15:57:46.339 UTC [10513] ERROR: division by zero
2026-10-14 06:36:25.713 UTC [1872] LOG: checkpoint starting: time
2026-10-14 15:51:32.492 UTC [10068] LOG: disconnection: session time: 0:00:00.002 user=postgres database=pgtutor_4d4f4a46d2 host=[local]Baris pertama adalah contoh terbaik untuk membedah empat bagian tetap setiap baris log:
2026-10-14 15:57:46.339 UTC— stempel waktu berisi tanggal, jam dengan presisi milidetik, dan zona waktu; berasal dari kode%mdalamlog_line_prefix.[10513]— PID (process ID) proses backend yang menulis pesan, dari kode%p; satu koneksi klien selalu memiliki satu PID yang sama sepanjang hidupnya.ERROR:— tingkat keseriusan pesan; daftar lengkapnya ada di tabel bagian berikutnya.division by zero— isi pesan itu sendiri, ditulis apa adanya oleh server.
Kode %p inilah penghubung antara log dan view pg_stat_activity: saat insiden sedang berlangsung, PID dari baris log bisa dicocokkan dengan baris pg_stat_activity yang sama untuk melihat query, user, dan state koneksi pelakunya — teknik yang sudah kita pakai di chapter Process dan Activity Monitoring
.
Tingkatan Pesan: DEBUG sampai PANIC #
Setiap pesan log membawa satu tingkat keseriusan. Delapan tingkat itu menentukan dua hal sekaligus: siapa yang berhak melihat pesan (klien atau server log) dan seberapa parah kejadian yang dilaporkannya:
| Tingkat | Sampai klien? | Masuk log bawaan? | Contoh nyata | Arti |
|---|---|---|---|---|
DEBUG5–DEBUG1 | Ya, bila diminta | Tidak | — (harus diaktifkan) | Diagnostik internal developer |
INFO | Ya | Tidak | keluaran VACUUM (VERBOSE) | Keterangan dari perintah eksplisit |
NOTICE | Ya | Tidak | database "probe" does not exist, skipping | Informasi untuk sesi klien |
WARNING | Ya | Ya | there is already a transaction in progress | Potensi masalah, eksekusi tetap lanjut |
ERROR | Ya | Ya | division by zero | Statement gagal, sesi tetap hidup |
LOG | Tidak | Ya | checkpoint starting: time | Kejadian operasional server |
FATAL | Ya | Ya | database "uji_ch1" does not exist | Koneksi ditutup paksa |
PANIC | Ya | Ya | — (semoga tak pernah) | Semua koneksi diputus, server mati |
Tiga tingkat pertama tidak masuk server log pada konfigurasi bawaan karena log_min_messages = warning — INFO dan NOTICE berakhir di layar klien, bukan di file log. Tingkat LOG menempati posisi terbalik: pesannya hanya masuk server log dan tidak pernah dikirim ke klien — seluruh baris statement:, duration:, dan checkpoint yang kita amati di file log bertingkat LOG. Bedanya ERROR dan FATAL penting untuk diagnosis: ERROR membatalkan satu statement namun sesi klien tetap hidup dan bisa melanjutkan transaksi baru, sedangkan FATAL memutus koneksi itu sendiri — baris FATAL: database "uji_ch1" does not exist di log server latihan berasal dari klien yang mencoba membuka database yang tidak ada. PANIC adalah keadaan terburuk yang memaksa semua koneksi terputus dan server menjalani crash recovery saat dinyalakan ulang.
Peristiwa Apa Saja yang Tercatat #
Log yang sehat berisi campuran kejadian otomatis dan kejadian opsional. Query berikut membaca parameter penentunya:
-- Statement apa yang dicatat otomatis
SHOW log_statement;
SHOW log_min_duration_statement;
-- Query gagal dan peristiwa lain yang ikut tercatat
SHOW log_min_error_statement;
SHOW log_lock_waits;
SHOW log_checkpoints;
SHOW log_autovacuum_min_duration;Output yang Dihasilkan:
log_statement
---------------
none
(1 row)
log_min_duration_statement
----------------------------
-1
(1 row)
log_min_error_statement
-------------------------
error
(1 row)
log_lock_waits
----------------
off
(1 row)
log_checkpoints
-----------------
on
(1 row)
log_autovacuum_min_duration
-----------------------------
10min
(1 row)Enam parameter itu memetakan isi log harian sebagai berikut:
| Kejadian | Parameter penentu | Keadaan server latihan |
|---|---|---|
| Statement gagal | log_min_error_statement | error — statement yang memicu ERROR ikut dicatat |
| Checkpoint | log_checkpoints | on — bawaan sejak PostgreSQL 15 |
| Autovacuum | log_autovacuum_min_duration | 10min — hanya aksi yang berjalan di atas 10 menit |
| Statement tertentu | log_statement | none — tidak ada statement dicatat utuh |
| Query lambat | log_min_duration_statement | -1 — tidak ada ambang durasi |
| Koneksi dan antrean lock | log_connections, log_lock_waits | off — tidak dicatat |
Sepasang baris checkpoint berikut adalah contoh kejadian otomatis yang sudah aktif — keduanya nyata dari log server latihan:
2026-10-14 06:36:25.713 UTC [1872] LOG: checkpoint starting: time
2026-10-14 06:36:37.938 UTC [1872] LOG: checkpoint complete: wrote 125 buffers (0.8%); 0 WAL file(s) added, 0 removed, 0 recycled; write=12.225 s, sync=0.001 s, total=12.226 s; sync files=50, longest=0.001 s, average=0.001 s; distance=698 kB, estimate=698 kB; lsn=0/1602720, redo lsn=0/1602690Baris pertama memberi tahu pemicu checkpoint (time — jadwal berkala checkpoint_timeout), dan baris kedua merangkum hasilnya: 125 halaman kotor ditulis dalam 12,2 detik. Pasangan seperti ini muncul terus-menerus di server aktif dan menjadi sinyal dini di monitoring: checkpoint yang makin sering dan makin lama biasanya berarti beban tulis sedang membesar.
Statement Gagal di Log #
Kesalahan adalah warga kelas satu di server log: tidak perlu menyetel apa pun, setiap ERROR langsung tercatat. File berikut sengaja membagi bilangan dengan nol:
-- Query yang gagal akan tercatat di log server
SELECT 1/0 AS pembagi_nol_adm14b;Output yang Dihasilkan:
psql:error-terlog.sql:2: ERROR: division by zeroItulah yang dilihat klien: psql berhenti dengan error dan exit code 3. Sisi server mencatat dua baris — file yang sama dijalankan ulang dan potongan log sungguhannya ditampilkan berikut ini:
-- Query yang gagal akan tercatat di log server
SELECT 1/0 AS pembagi_nol_adm14b;Output yang Dihasilkan:
2026-10-14 15:57:46.339 UTC [10513] ERROR: division by zero
2026-10-14 15:57:46.339 UTC [10513] STATEMENT: SELECT 1/0 AS pembagi_nol_adm14b;Dua baris itu saling melengkapi: baris ERROR memuat jenis kegagalan, sedangkan baris STATEMENT memuat teks query utuh yang memicunya — perilaku bawaan log_min_error_statement = error yang kita baca di tabel sebelumnya. PID yang sama (10513) di kedua baris menegaskan keduanya berasal dari satu backend yang sama. Stempel waktu dan PID akan berbeda di mesin Anda, tetapi pasangan ERROR-STATEMENT ini adalah pola paling sering Anda cari saat investigasi.
Pesan Berlapis: DETAIL, HINT, dan CONTEXT #
Error yang kompleks tidak datang sendirian. Kegagalan berikut berasal dari uji coba konversi teks ke jsonb, tercatat aslinya di log server latihan:
2026-10-14 07:30:35.191 UTC [10297] ERROR: invalid input syntax for type json at character 8
2026-10-14 07:30:35.191 UTC [10297] DETAIL: Token "nama" is invalid.
2026-10-14 07:30:35.191 UTC [10297] CONTEXT: JSON data, line 1: {nama...
2026-10-14 07:30:35.191 UTC [10297] STATEMENT: SELECT '{nama: Budi}'::jsonb;Empat baris itu adalah bentuk paling lengkap satu insiden:
ERROR:judul kegagalan — sintaks input tidak valid untuk tipejson.DETAIL:penyebab spesifiknya — token"nama"tidak valid karena JSON menuntut tanda kutip ganda pada nama kunci.CONTEXT:posisi kegagalan dalam proses — baris 1 dari data JSON yang sedang diparse.STATEMENT:query lengkap yang mengeksekusinya.
Lapisan kelima yang juga umum adalah HINT: — saran perbaikan dari server. Contoh nyata dari log server latihan: upaya DROP TABLE pelanggan gagal dengan ERROR: cannot drop table pelanggan because other objects depend on it, lalu DETAIL menjelaskan bahwa constraint pesanan_pelanggan_id_fkey bergantung padanya, dan HINT menutup dengan saran Use DROP ... CASCADE to drop the dependent objects too. Membaca ketiga lapisan berurutan hampir selalu menghasilkan diagnosis lengkap tanpa perlu menebak.
Jejak Durasi Query #
Parameter log_min_duration_statement menambahkan baris duration untuk statement yang melewati ambang waktunya. File berikut memasang ambang 0 — artinya semua statement dicatat bersama durasinya — lalu menjalankan dua query penanda:
-- Catat semua statement sesi ini beserta durasinya
SET log_min_duration_statement = 0;
-- Statement ini dan berikutnya masuk ke log server
SELECT 'penanda-log-adm14b' AS penanda;
SELECT count(*) AS parameter_log FROM pg_settings WHERE name LIKE 'log%';
-- Matikan kembali pencatatan durasi
SET log_min_duration_statement = -1;Output yang Dihasilkan:
SET
penanda
--------------------
penanda-log-adm14b
(1 row)
parameter_log
---------------
36
(1 row)
SETDi sisi klien tidak ada yang istimewa — dua hasil query biasa. Server log berkata lain; file yang sama dijalankan ulang dan potongan log-nya ditampilkan:
-- Catat semua statement sesi ini beserta durasinya
SET log_min_duration_statement = 0;
-- Statement ini dan berikutnya masuk ke log server
SELECT 'penanda-log-adm14b' AS penanda;
SELECT count(*) AS parameter_log FROM pg_settings WHERE name LIKE 'log%';
-- Matikan kembali pencatatan durasi
SET log_min_duration_statement = -1;Output yang Dihasilkan:
2026-10-14 15:57:46.345 UTC [10515] LOG: duration: 0.110 ms statement: SET log_min_duration_statement = 0;
2026-10-14 15:57:46.345 UTC [10515] LOG: duration: 0.160 ms statement: SELECT 'penanda-log-adm14b' AS penanda;
2026-10-14 15:57:46.346 UTC [10515] LOG: duration: 1.240 ms statement: SELECT count(*) AS parameter_log FROM pg_settings WHERE name LIKE 'log%';Tiga baris duration mencatat setiap statement beserta lama eksekusinya — bahkan SET pembuka ambang ikut terekam karena penyetelannya baru berlaku setelah statement itu selesai, persis perilaku yang sudah kita amati pada log_statement di chapter Logging Configuration
. Nilai durasi seperti 1.240 ms tentu berbeda di setiap mesin dan eksekusi; yang stabil adalah formatnya: duration: <angka> ms statement: <query>. Di produksi, ambang 0 hanya dipakai untuk jendela investigasi singkat — nilai lazimnya 1000 ms agar hanya query lambat yang memenuhi log.
Rotasi, Format Terstruktur, dan Retensi #
Tiga urusan pengelolaan file menutup arsitektur logging. Pertama, rotasi: parameter log_rotation_age (1 hari) dan log_rotation_size (10 MB) yang terbaca di query pembuka memicu pembukaan file baru, dengan nama mengikuti pola log_filename — server produksi yang ramai biasanya memperbesar ukuran rotasi dan mengaktifkan log_truncate_on_rotation agar file lama tertimpa saat polanya terpakai ulang — mekanisme retensi sederhana berbasis penamaan. Kedua, format terstruktur: log_destination = csvlog atau jsonlog memecah setiap pesan menjadi kolom tetap sehingga log bisa dimuat ke tabel atau pipeline analisis tanpa parsing — keduanya mensyaratkan logging_collector = on. Ketiga, retensi: PostgreSQL tidak pernah menghapus file log lamanya sendiri; penghapusan setelah 30 atau 90 hari dijalankan lewat cron atau skrip housekeeping, topik yang bersambung dengan seluruh rutinitas pemeliharaan di chapter Database Maintenance
.
Mencari Lokasi File Log #
Karena nasib stderr ditentukan lingkungan sekitar, lokasi file log berbeda antar instalasi. Titik berangkat yang selalu benar adalah data directory:
-- Data directory adalah akar semua file milik server,
-- termasuk subfolder log/ saat logging_collector diaktifkan.
SHOW data_directory;Output yang Dihasilkan:
data_directory
---------------------------
/home/z/my-project/pgdata
(1 row)Output di atas adalah path milik server latihan; angka Anda pasti berbeda. Dari titik itu, lokasi log tiap jenis instalasi umumnya mengikuti pola berikut:
| Instalasi | Lokasi log yang lazim |
|---|---|
| Debian/Ubuntu (paket apt) | /var/log/postgresql/postgresql-17-main.log |
| RHEL dan turunannya (paket PGDG) | /var/lib/pgsql/17/data/log/ |
| Docker image resmi | stdout container — baca lewat docker logs |
pg_ctl manual (seperti server latihan) | file yang diberi ke opsi -l, misalnya pg.log |
Instalasi lewat systemd juga menampung stderr ke jurnal: journalctl -u postgresql menampilkan log yang sama. Bila file log tidak juga ditemukan, query SHOW data_directory; di atas tetap menjadi titik tolak — subfolder log di dalamnya adalah lokasi bawaan logging collector.
Alur Kerja Membaca Error #
Saat insiden terjadi, log yang panjang harus dibaca dengan urutan kerja yang menentukan, bukan dibuka dari baris pertama. Urutan yang terbukti efektif:
- Saring error terbaru —
grep 'ERROR' /home/z/my-project/pgrun/pg.log | tail -5(diuji di server latihan; log latihan yang sudah menampung lebih dari seribu baris ERROR mengembalikan lima kejadian terakhir). Waktu insiden menentukan posisi jendela pembacaan. - Baca satu insiden utuh — dari baris
ERROR, ambil PID-nya lalu baca semua baris ber-PID sama di sekitarnya: lapisanDETAIL,HINT,CONTEXT, danSTATEMENTmenyusun satu cerita. - Cocokkan waktu dan PID — bila keluhan aplikasi menyebut jam tertentu, cari baris log yang stempel waktunya berimpit (ingat selisih zona waktu), lalu hubungkan PID-nya dengan
pg_stat_activitybila koneksi masih hidup. - Cari pola, bukan satu kejadian — error yang muncul sekali dan error yang muncul seribu kali sehari ditangani dengan cara berbeda; hitung frekuensinya sebelum bertindak.
- Tindak lanjuti — perbaiki query untuk
ERRORsintaks dan tipe data, kejar statement lambat lewat barisduration, dan naikkan ke tim jaringan untukFATALautentikasi.
Log menjawab pertanyaan apa yang sudah terjadi. Pertanyaan administrasi berikutnya adalah apa yang sebaiknya dicatat — berapa ambang query lambat, statement kelas mana yang ditulis utuh, dan prefix apa yang membuat setiap baris mudah ditelusuri. Semua tombol itu ada di chapter berikutnya: Logging Configuration .
Referensi #
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
error-terlog.sql
Unduh berkas-- Query yang gagal akan tercatat di log server
SELECT 1/0 AS pembagi_nol_adm14b;
lokasi-log.sql
Unduh berkas-- Data directory adalah akar semua file milik server,
-- termasuk subfolder log/ saat logging_collector diaktifkan.
SHOW data_directory;
peristiwa-terlog.sql
Unduh berkas-- Statement apa yang dicatat otomatis
SHOW log_statement;
SHOW log_min_duration_statement;
-- Query gagal dan peristiwa lain yang ikut tercatat
SHOW log_min_error_statement;
SHOW log_lock_waits;
SHOW log_checkpoints;
SHOW log_autovacuum_min_duration;
status-logging.sql
Unduh berkas-- Ke mana baris log ditulis
SHOW logging_collector;
SHOW log_destination;
SHOW log_directory;
SHOW log_filename;
-- Kapan file log diganti
SHOW log_rotation_age;
SHOW log_rotation_size;
-- Pola awalan setiap baris log
SHOW log_line_prefix;
trace-query.sql
Unduh berkas-- Catat semua statement sesi ini beserta durasinya
SET log_min_duration_statement = 0;
-- Statement ini dan berikutnya masuk ke log server
SELECT 'penanda-log-adm14b' AS penanda;
SELECT count(*) AS parameter_log FROM pg_settings WHERE name LIKE 'log%';
-- Matikan kembali pencatatan durasi
SET log_min_duration_statement = -1;