การตั้งค่า Logging ใน PostgreSQL เป็นหัวใจสำคัญของการดูแลฐานข้อมูลให้ทำงานได้อย่างเสถียร Log ที่ดีช่วยให้ผู้ดูแลระบบตรวจจับปัญหาได้ตั้งแต่เนิ่น ๆ วิเคราะห์ Query ที่ช้า และติดตามกิจกรรมที่ผิดปกติ ในทางกลับกัน Log ที่ตั้งค่าไม่เหมาะสมอาจสร้างภาระให้ดิสก์เต็มหรือบันทึกข้อมูลไม่เพียงพอสำหรับการแก้ปัญหา
บทความนี้จะอธิบายการตั้งค่า Logging ใน PostgreSQL อย่างละเอียด ตั้งแต่ปลายทางการบันทึก (Log Destination) รูปแบบไฟล์ ระดับข้อความ การบันทึก Query ที่ช้า ไปจนถึง Best Practices ที่ช่วยให้จัดการ Log ได้อย่างมีประสิทธิภาพ
Log Destination: เลือกว่าจะบันทึกที่ไหน
PostgreSQL รองรับการส่ง Log ไปยังหลายปลายทาง กำหนดผ่านพารามิเตอร์ log_destination ในไฟล์ postgresql.conf
# เลือกปลายทาง (เลือกได้หลายค่า คั่นด้วยคอมม่า)
log_destination = 'stderr' # ค่าเริ่มต้น — ส่งไป stderr
log_destination = 'csvlog' # บันทึกเป็น CSV (วิเคราะห์ง่าย)
log_destination = 'jsonlog' # บันทึกเป็น JSON (PostgreSQL 15+)
log_destination = 'syslog' # ส่งไป System Log (Linux syslog)
log_destination = 'stderr,csvlog' # ส่งทั้ง stderr และ CSV พร้อมกัน
| Destination | คำอธิบาย | เหมาะกับ |
|---|---|---|
| stderr | ส่งไป Standard Error แล้วให้ logging_collector จัดการ | การใช้งานทั่วไป |
| csvlog | บันทึกเป็นไฟล์ CSV สามารถ Import เข้าฐานข้อมูลวิเคราะห์ได้ | การวิเคราะห์ Log อย่างละเอียด |
| jsonlog | บันทึกเป็น JSON (PostgreSQL 15+) เหมาะกับเครื่องมือ Log Management | ELK Stack, Grafana Loki |
| syslog | ส่งไป System Log ของ OS | รวม Log กับระบบอื่นผ่าน syslog |
Logging Collector: จัดการไฟล์ Log
logging_collector เป็น Background Process ที่รับ Log จาก stderr แล้วเขียนลงไฟล์ ต้องเปิดใช้เมื่อต้องการบันทึก Log เป็นไฟล์บนเซิร์ฟเวอร์
# เปิด Logging Collector (ต้อง restart PostgreSQL)
logging_collector = on
# กำหนดโฟลเดอร์เก็บ Log
log_directory = 'log' # สัมพัทธ์กับ PGDATA หรือ absolute path
# รูปแบบชื่อไฟล์ (รองรับ strftime format)
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
# สิทธิ์ไฟล์ (0600 = เฉพาะ owner อ่านได้)
log_file_mode = 0600
Log Rotation
การหมุนเวียนไฟล์ Log ป้องกันไม่ให้ไฟล์ใหญ่เกินไปและช่วยจัดระเบียบ
# หมุนเวียนตามเวลา (นาที) — 1440 = 1 วัน
log_rotation_age = 1d
# หมุนเวียนตามขนาด (KB) — 100MB
log_rotation_size = 100MB
# เขียนทับไฟล์เก่าเมื่อชื่อซ้ำ (ระวัง!)
log_truncate_on_rotation = off
คำแนะนำคือใช้ log_rotation_age = 1d ร่วมกับ log_filename ที่มีวันที่ จากนั้นใช้ logrotate ของ Linux หรือ cron job ลบไฟล์เก่าที่ไม่ต้องการ
Log Level: ควบคุมระดับข้อความ
PostgreSQL มีหลายระดับข้อความ กำหนดผ่าน log_min_messages สำหรับ Log ทั่วไป และ client_min_messages สำหรับข้อความที่ส่งกลับไปยัง Client
| ระดับ | ความหมาย | ตัวอย่าง |
|---|---|---|
| DEBUG5-DEBUG1 | ข้อมูลละเอียดสำหรับ Developer | รายละเอียดภายใน Planner |
| INFO | ข้อมูลทั่วไปที่ผู้ใช้ร้องขอ | ผลลัพธ์ VACUUM VERBOSE |
| NOTICE | ข้อมูลที่อาจมีประโยชน์ | สร้าง Implicit Index |
| WARNING | คำเตือนที่ควรตรวจสอบ | COMMIT ภายนอก Transaction Block |
| ERROR | ข้อผิดพลาดที่ทำให้คำสั่งล้มเหลว | Syntax Error, Constraint Violation |
| LOG | ข้อมูลสำหรับผู้ดูแลระบบ | Checkpoint, Connection Authorization |
| FATAL | ข้อผิดพลาดที่ทำให้ Session สิ้นสุด | Authentication Failed |
| PANIC | ข้อผิดพลาดร้ายแรงที่ทำให้ทุก Session หยุด | ไม่สามารถเข้าถึง Data Directory |
# ระดับข้อความขั้นต่ำที่จะบันทึกใน Log
log_min_messages = warning # ค่าเริ่มต้น — บันทึกตั้งแต่ WARNING ขึ้นไป
# ระดับข้อความที่ส่งกลับไปยัง Client
client_min_messages = notice # ค่าเริ่มต้น
# สำหรับ Production ทั่วไป
log_min_messages = log # บันทึกข้อมูลมากขึ้น (รวม Checkpoint, Connection)
บันทึก Slow Query ด้วย log_min_duration_statement
หนึ่งในพารามิเตอร์สำคัญที่สุดสำหรับการวิเคราะห์ประสิทธิภาพคือ log_min_duration_statement ซึ่งบันทึก SQL Statement ที่ใช้เวลานานเกินค่าที่กำหนด
# บันทึก Statement ที่ใช้เวลานานกว่า 1 วินาที
log_min_duration_statement = 1000 # หน่วย: มิลลิวินาที (1000 = 1 วินาที)
# บันทึกทุก Statement (ระวัง! สร้าง Log จำนวนมาก)
log_min_duration_statement = 0
# ปิดการบันทึก (ค่าเริ่มต้น)
log_min_duration_statement = -1
ค่าที่แนะนำสำหรับ Production คือ 500-1000 มิลลิวินาที ซึ่งจะจับ Query ที่ช้าพอจะส่งผลกระทบต่อผู้ใช้ โดยไม่สร้าง Log มากเกินไป
log_statement: ควบคุมว่าจะบันทึก Statement ใดบ้าง
# ตัวเลือก: none, ddl, mod, all
log_statement = 'none' # ไม่บันทึก Statement เลย (ค่าเริ่มต้น)
log_statement = 'ddl' # บันทึกเฉพาะ DDL (CREATE, ALTER, DROP)
log_statement = 'mod' # บันทึก DDL + DML ที่เปลี่ยนข้อมูล (INSERT, UPDATE, DELETE)
log_statement = 'all' # บันทึกทุก Statement (รวม SELECT)
| ค่า | บันทึก | เหมาะกับ |
|---|---|---|
| none | ไม่บันทึก Statement | Production ทั่วไป (ใช้ log_min_duration_statement แทน) |
| ddl | CREATE, ALTER, DROP, GRANT, REVOKE | ติดตามการเปลี่ยนแปลง Schema |
| mod | DDL + INSERT, UPDATE, DELETE, TRUNCATE | Audit Trail สำหรับข้อมูลสำคัญ |
| all | ทุก Statement รวม SELECT | Debug หรือ Audit แบบละเอียด (ระวังดิสก์เต็ม) |
Log Line Prefix: ปรับแต่งรูปแบบ Log
log_line_prefix กำหนดข้อมูลที่จะแสดงหน้าแต่ละบรรทัด Log สำคัญมากสำหรับการวิเคราะห์และกรอง Log
# รูปแบบพื้นฐาน
log_line_prefix = '%m [%p] '
# ผลลัพธ์: 2026-04-07 10:30:15.123 ICT [12345]
# รูปแบบแนะนำสำหรับ Production
log_line_prefix = '%m [%p] %q%u@%d '
# ผลลัพธ์: 2026-04-07 10:30:15.123 ICT [12345] myuser@mydb
# รูปแบบละเอียด (รวม Application Name และ Client IP)
log_line_prefix = '%m [%p] %q%u@%d app=%a client=%h '
# ผลลัพธ์: 2026-04-07 10:30:15.123 ICT [12345] myuser@mydb app=myapp client=192.168.1.10
รหัสที่ใช้บ่อย
| รหัส | ความหมาย |
|---|---|
| %m | Timestamp พร้อมมิลลิวินาที |
| %p | Process ID (PID) |
| %u | Username |
| %d | Database Name |
| %a | Application Name |
| %h | Client Hostname/IP |
| %r | Client Host:Port |
| %l | Log Line Number (สำหรับ Session) |
| %q | หยุด prefix ถ้าเป็น Background Process (ป้องกันข้อมูลว่าง) |
บันทึก Connection และ Disconnection
# บันทึกเมื่อมีการเชื่อมต่อสำเร็จ
log_connections = on
# บันทึกเมื่อ Session สิ้นสุด (รวมระยะเวลา Session)
log_disconnections = on
เปิดทั้งสองค่าใน Production เพื่อติดตามว่าใครเชื่อมต่อเข้ามา จาก IP ใด และใช้เวลานานเท่าไหร่ ข้อมูลเหล่านี้มีประโยชน์มากเมื่อต้องวิเคราะห์ปัญหา Connection Pool หรือตรวจสอบการเข้าถึงที่ผิดปกติ
Checkpoint และ Lock Logging
# บันทึก Checkpoint Statistics
log_checkpoints = on
# ผลลัพธ์: LOG: checkpoint complete: wrote 1234 buffers (7.5%); 0 WAL file(s) added ...
# บันทึก Lock Wait ที่นานกว่า deadlock_timeout
log_lock_waits = on
deadlock_timeout = 1s
# ผลลัพธ์: LOG: process 12345 still waiting for ShareLock on transaction 67890 after 1000.123 ms
# บันทึก Temporary File ที่ใหญ่กว่าค่ากำหนด (KB)
log_temp_files = 0 # 0 = บันทึกทุก Temp File
# ผลลัพธ์: LOG: temporary file: path "base/pgsql_tmp/pgsql_tmp12345.0", size 10485760
log_checkpoints ช่วยตรวจสอบว่า Checkpoint เกิดบ่อยเกินไปหรือไม่ ถ้าเห็น Checkpoint เกิดถี่มากอาจต้องเพิ่ม max_wal_size ส่วน log_lock_waits ช่วยค้นหา Query ที่ถูกบล็อกนานเกินไป
csvlog: Log แบบมีโครงสร้าง
csvlog บันทึก Log ในรูปแบบ CSV ที่มีคอลัมน์คงที่ สามารถ Import เข้าตารางใน PostgreSQL เพื่อวิเคราะห์ด้วย SQL ได้โดยตรง
# เปิด csvlog
log_destination = 'csvlog'
logging_collector = on
# สร้างตาราง Import Log
CREATE TABLE postgres_log (
log_time TIMESTAMP(3) WITH TIME ZONE,
user_name TEXT,
database_name TEXT,
process_id INTEGER,
connection_from TEXT,
session_id TEXT,
session_line_num BIGINT,
command_tag TEXT,
session_start_time TIMESTAMP WITH TIME ZONE,
virtual_transaction_id TEXT,
transaction_id BIGINT,
error_severity TEXT,
sql_state_code TEXT,
message TEXT,
detail TEXT,
hint TEXT,
internal_query TEXT,
internal_query_pos INTEGER,
context TEXT,
query TEXT,
query_pos INTEGER,
location TEXT,
application_name TEXT,
backend_type TEXT,
leader_pid INTEGER,
query_id BIGINT
);
-- Import Log เข้าตาราง
COPY postgres_log FROM '/var/lib/postgresql/16/main/log/postgresql-2026-04-07.csv' CSV;
-- วิเคราะห์ Slow Query จาก Log
SELECT user_name, database_name, message, log_time
FROM postgres_log
WHERE error_severity = 'LOG'
AND message LIKE 'duration:%'
ORDER BY log_time DESC
LIMIT 20;
jsonlog: Log สำหรับ Log Management Tools
ตั้งแต่ PostgreSQL 15 เป็นต้นมา รองรับ jsonlog ซึ่งบันทึก Log แต่ละบรรทัดเป็น JSON Object เหมาะสำหรับส่งเข้าเครื่องมือ Log Management เช่น ELK Stack, Grafana Loki หรือ Datadog
# เปิด jsonlog (PostgreSQL 15+)
log_destination = 'jsonlog'
logging_collector = on
ตัวอย่างผลลัพธ์ jsonlog
{"timestamp":"2026-04-07 10:30:15.123 ICT","pid":12345,"user":"myuser","db":"mydb","app":"myapp","client":"192.168.1.10","session_id":"660...","line_num":5,"ps":"SELECT","session_start":"2026-04-07 10:00:00 ICT","vxid":"3/100","txid":0,"error_severity":"LOG","message":"duration: 1523.456 ms statement: SELECT * FROM orders WHERE status = 'pending'"}
auto_explain: บันทึก Query Plan อัตโนมัติ
auto_explain เป็น Extension ที่บันทึก Query Plan ของ Query ที่ช้าเข้าไปใน Log โดยอัตโนมัติ ช่วยให้ไม่ต้องรัน EXPLAIN ANALYZE ด้วยมือ
# postgresql.conf
shared_preload_libraries = 'auto_explain'
# บันทึก Query Plan ของ Statement ที่ช้ากว่า 1 วินาที
auto_explain.log_min_duration = '1s'
# รวมเวลาจริง (เหมือน EXPLAIN ANALYZE)
auto_explain.log_analyze = true
# รวมข้อมูล Buffer
auto_explain.log_buffers = true
# รวม Timing ของแต่ละ Node
auto_explain.log_timing = true
# แสดงแบบ Nested (อ่านง่าย)
auto_explain.log_nested_statements = true
# แสดงรูปแบบ JSON (วิเคราะห์ง่ายด้วยเครื่องมือ)
auto_explain.log_format = 'json'
auto_explain มีประโยชน์มากสำหรับ Production เพราะบันทึก Query Plan ของ Slow Query โดยไม่ต้องรบกวนแอปพลิเคชัน แต่การเปิด log_analyze จะทำให้มี Overhead เล็กน้อย เนื่องจากต้องวัดเวลาจริง
ตั้งค่า Logging เฉพาะ Database หรือ User
สามารถตั้งค่า Logging แยกตาม Database หรือ User ได้ด้วยคำสั่ง ALTER DATABASE หรือ ALTER ROLE เหมาะสำหรับการ Debug เฉพาะจุดโดยไม่กระทบทั้งระบบ
-- บันทึกทุก Statement ของ Database เฉพาะ
ALTER DATABASE production SET log_statement = 'all';
-- บันทึก Slow Query ที่ช้ากว่า 500ms สำหรับ User เฉพาะ
ALTER ROLE reporting SET log_min_duration_statement = 500;
-- เปิด auto_explain สำหรับ Session ปัจจุบัน
LOAD 'auto_explain';
SET auto_explain.log_min_duration = '500ms';
SET auto_explain.log_analyze = true;
-- ดูค่าที่ตั้งไว้สำหรับ Database
SELECT datname, setconfig FROM pg_db_role_setting
JOIN pg_database ON pg_database.oid = setdatabaseid
WHERE datname = 'production';
จัดการ Log ใน Production
ป้องกันดิสก์เต็ม
Log ที่ไม่ได้จัดการจะโตขึ้นเรื่อย ๆ จนดิสก์เต็ม ควรตั้ง logrotate บน Linux เพื่อจัดการไฟล์ Log อัตโนมัติ
# /etc/logrotate.d/postgresql
/var/lib/postgresql/16/main/log/*.log {
daily
rotate 14
compress
delaycompress
missingok
notifempty
create 0600 postgres postgres
sharedscripts
postrotate
/usr/bin/pg_ctlcluster 16 main reload
endscript
}
Monitoring Log ด้วย pgBadger
pgBadger เป็นเครื่องมือวิเคราะห์ Log ที่สร้างรายงาน HTML แสดงสถิติ Query, Connection, Lock และอื่น ๆ
# ติดตั้ง pgBadger
sudo apt install pgbadger
# สร้างรายงานจากไฟล์ Log
pgbadger /var/lib/postgresql/16/main/log/postgresql-2026-04-07.log -o report.html
# สร้างรายงานจาก CSV Log
pgbadger -f csv /var/lib/postgresql/16/main/log/postgresql-2026-04-07.csv -o report.html
# สร้างรายงานแบบ Incremental (สำหรับรัน cron ทุกวัน)
pgbadger --incremental /var/lib/postgresql/16/main/log/postgresql-*.log -O /var/www/pgbadger/
ส่ง Log ไปยัง Centralized Log System
สำหรับระบบขนาดใหญ่ที่มีหลายเซิร์ฟเวอร์ ควรรวม Log ไว้ที่ศูนย์กลาง
- syslog + rsyslog/syslog-ng — ส่ง Log ผ่าน syslog protocol ไปยัง Log Server กลาง
- Filebeat + ELK Stack — ใช้ Filebeat อ่านไฟล์ Log แล้วส่งไป Elasticsearch ดูผ่าน Kibana
- Promtail + Grafana Loki — ใช้ Promtail อ่าน Log แล้วส่งไป Loki ดูผ่าน Grafana
- Vector/Fluentd — Log Aggregator ที่รองรับหลาย Input/Output
ตัวอย่างการตั้งค่าสำหรับ Production
# postgresql.conf — การตั้งค่า Logging สำหรับ Production
# ปลายทาง
log_destination = 'csvlog'
logging_collector = on
log_directory = 'log'
log_filename = 'postgresql-%Y-%m-%d.log'
log_rotation_age = 1d
log_rotation_size = 0 # ปิด — ใช้ rotation ตามเวลาอย่างเดียว
log_file_mode = 0600
# ระดับข้อความ
log_min_messages = warning
log_min_error_statement = error
# Slow Query
log_min_duration_statement = 1000 # บันทึก Query ที่ช้ากว่า 1 วินาที
# Statement
log_statement = 'ddl' # บันทึกการเปลี่ยนแปลง Schema
# Connection
log_connections = on
log_disconnections = on
# Checkpoint & Lock
log_checkpoints = on
log_lock_waits = on
deadlock_timeout = 1s
# Temp Files
log_temp_files = 10240 # บันทึก Temp File ที่ใหญ่กว่า 10 MB
# Log Line Prefix (สำหรับ stderr ไม่มีผลกับ csvlog)
log_line_prefix = '%m [%p] %q%u@%d '
# Duration
log_duration = off # ใช้ log_min_duration_statement แทน
# auto_explain (ต้องเพิ่มใน shared_preload_libraries)
# shared_preload_libraries = 'auto_explain'
# auto_explain.log_min_duration = '3s'
# auto_explain.log_analyze = true
# auto_explain.log_buffers = true
ตรวจสอบและทดสอบการตั้งค่า
-- ดูค่าปัจจุบันของพารามิเตอร์ Logging
SELECT name, setting, source
FROM pg_settings
WHERE name LIKE 'log%'
ORDER BY name;
-- ทดสอบว่า Slow Query ถูกบันทึก
SET log_min_duration_statement = 0;
SELECT pg_sleep(2);
-- ตรวจใน Log ว่ามีบันทึก "duration: 2000.xxx ms"
-- Reload Config โดยไม่ Restart (สำหรับพารามิเตอร์ที่ไม่ต้อง restart)
SELECT pg_reload_conf();
-- ดูว่าพารามิเตอร์ใดต้อง Restart
SELECT name, setting, pending_restart
FROM pg_settings
WHERE pending_restart = true;
สรุป
การตั้งค่า Logging ที่เหมาะสมเป็นพื้นฐานของการดูแล PostgreSQL ใน Production พารามิเตอร์สำคัญที่ควรตั้งค่าตั้งแต่เริ่มต้นคือ logging_collector, log_min_duration_statement, log_checkpoints, log_connections และ log_lock_waits นอกจากนี้ควรจัดการ Log Rotation และเลือกใช้เครื่องมือวิเคราะห์อย่าง pgBadger หรือ Centralized Log System เพื่อให้สามารถตรวจจับปัญหาได้อย่างรวดเร็ว
แนะนำบริการ DE
การตั้งค่า Logging ใน PostgreSQL อย่างละเอียดต้องการ Root Access เพื่อแก้ไข postgresql.conf, จัดการ shared_preload_libraries และตั้ง logrotate Cloud VPS ของ DE ให้สิทธิ์ Root เต็มรูปแบบพร้อม SSD NVMe ที่รองรับ I/O สูง เหมาะสำหรับเซิร์ฟเวอร์ฐานข้อมูลที่ต้องการควบคุมทุกรายละเอียด
สำหรับผู้ที่โฮสต์เว็บแอปพลิเคชันที่เชื่อมต่อกับ PostgreSQL และไม่ต้องการจัดการเซิร์ฟเวอร์เอง Cloud Hosting ของ DE เป็นทางเลือกที่สะดวก มีระบบจัดการพร้อมใช้งานและรองรับการเชื่อมต่อฐานข้อมูลภายนอก

