การตั้งค่า PostgreSQL Logging: คู่มือครบทุกพารามิเตอร์สำคัญ

การตั้งค่า 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 ManagementELK 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ไม่บันทึก StatementProduction ทั่วไป (ใช้ log_min_duration_statement แทน)
ddlCREATE, ALTER, DROP, GRANT, REVOKEติดตามการเปลี่ยนแปลง Schema
modDDL + INSERT, UPDATE, DELETE, TRUNCATEAudit Trail สำหรับข้อมูลสำคัญ
allทุก Statement รวม SELECTDebug หรือ 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

รหัสที่ใช้บ่อย

รหัสความหมาย
%mTimestamp พร้อมมิลลิวินาที
%pProcess ID (PID)
%uUsername
%dDatabase Name
%aApplication Name
%hClient Hostname/IP
%rClient Host:Port
%lLog 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 เป็นทางเลือกที่สะดวก มีระบบจัดการพร้อมใช้งานและรองรับการเชื่อมต่อฐานข้อมูลภายนอก