Skip to main content
RunBook Academy

PostgreSQLXVI · Observability, Logging and AlertingObservability

Production logging configuration

Intermediate⏱ ~30 minpsql

What you'll learn

  • Configure a log_line_prefix that supports correlation
  • Choose what to log by what question it answers
  • Manage log volume and rotation deliberately
  • Know which settings require a restart

Prerequisites

Verified against PostgreSQL 18.x · PostgreSQL (comparison targets) 17.11, 16.15 · PostgreSQL (support calendar) 18, 17, 16, 15, 14 supported · pgBackRest 2.59.1 · PgBouncer 1.25.2 · Patroni 4.1.5 · Ubuntu (host baseline) 26.04 LTS · 2026-08-27

Not yet marked complete on this device.

Every other instrument in this part shows you the present. The log is the only one that shows you last Tuesday at 03:14.

The prefix

Everything starts here, because a log line without context cannot be joined to anything.

log_line_prefix = '%m [%p] %q%u@%d/%a '
EscapeIsWhy
%mTimestamp with millisecondsCorrelating with anything else
%pProcess idJoins to pg_stat_activity.pid and to the OS
%qStop here for background processesKeeps background lines clean
%uUserWho
%dDatabaseWhere
%aApplication nameWhich service — if the app sets it
Read-only / Safewhat that prefix produces
$ tail /tmp/sb.log
2026-08-27 23:13:19.892 UTC [637] postgres@postgres/psql LOG:  statement: CREATE ROLE demo_user ...

%p is the one that matters most. Lesson XVI-06 shows that pg_stat_activity.pid is the operating system pid, so a log line carrying %p can be joined to a running backend and to ps, top and /proc.

Add %h (client host) if you need to trace connections back to machines, and %x (transaction id) when investigating locking.

What to log

Always:

log_min_duration_statement = '1s'   # the slow query log
log_checkpoints = on                # on by default since 15
log_autovacuum_min_duration = '1s'  # vacuum that took real time
log_lock_waits = on                 # anything waiting past deadlock_timeout
log_temp_files = 0                  # every spill to disk; lesson XI-05
log_connections = on
log_disconnections = on

Each answers a question you will have during an incident:

  • log_min_duration_statementwhich queries were slow?
  • log_lock_waitswhat was blocking? Part IX’s whole subject.
  • log_temp_fileswas work_mem too small? Lesson XI-05 measured spilling.
  • log_checkpointswas a write stall a checkpoint? Lesson XII-04.
  • log_connections/log_disconnectionswas it a connection storm?

Destination and rotation

logging_collector = on          # RESTART REQUIRED
log_destination = 'stderr'      # or 'jsonlog', or 'csvlog', or several
log_directory = 'log'
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
log_rotation_age = '1d'
log_rotation_size = '100MB'
log_truncate_on_rotation = off

logging_collector requires a restart, and it is what makes PostgreSQL manage its own files. Without it, output goes to whatever started the postmaster — journald under systemd, docker logs in a container — which is fine if that is deliberate and surprising if it is not.

jsonlog, added in PostgreSQL 15, emits one JSON object per event. If a collector consumes your logs, it is easier to parse correctly than stderr — no ambiguity about where a multi-line statement ends.

What to take from this

  • log_line_prefix = '%m [%p] %q%u@%d/%a '. %p is what joins the log to pg_stat_activity and to the OS.
  • Check what your prefix actually is; distributions differ.
  • Log slow statements, checkpoints, slow autovacuum, lock waits, temp files, connections.
  • log_min_duration_statement is a threshold. Use sampling rather than lowering it globally.
  • logging_collector requires a restart and prevents interleaved lines.
  • The collector can become a bottleneck that backends block on. Do not log everything.

Cross-course references

  • Linux for Production Sysadmins — Part VIII (Logging and journald) covers rotation and the filesystem the logs land on, which is a disk-full cause in its own right.
  • Observability for Production Sysadmins — Part XXXI (Logging foundations) and Part XXXVI (Log shipping) cover getting these lines off the host, and Part XL (Log retention) covers how long to keep them.

Quiz

Knowledge check · 6 questions

  1. Q1. Which log_line_prefix escape makes a log line joinable to a running backend and to operating system tools?

  2. Q2. Queries on a busy cluster slow down after log_statement is set to all, with no change visible in pg_stat_statements' per-query timings. What is happening?

  3. Q3. A log occasionally contains lines from two backends spliced together. logging_collector is off. Why?

  4. Q4. Which logging settings answer a question you will actually have during an incident? Select all that apply.

  5. Q5. logging_collector can be enabled with a reload, since it only changes where log output is directed.

  6. Q6. Why is log_min_duration_statement preferable to log_statement = 'all', and what does sampling add?

Passing score: 75%. Answers are checked in this browser.