PostgreSQL
Monitor PostgreSQL database performance and metrics with OpenTelemetry and Last9
Use OpenTelemetry to instrument your PostgreSQL database and send telemetry data to Last9. This integration provides comprehensive monitoring of database performance, slow queries, connection metrics, and system resource utilization.
Choose the setup that matches your environment:
- With Docker — run the OTel Collector and a Postgres exporter via Docker Compose.
- Without Docker — install the OTel Collector as a binary on the same host as PostgreSQL. No containers required.
Prerequisites
- Last9 account with integration credentials
- Access to your PostgreSQL database with appropriate permissions
- For the With Docker setup: Docker and Docker Compose installed on your system
- For the Without Docker setup: PostgreSQL 14+ installed and running on the same host as the collector, with access to
postgresql.confto enable slow query logging
Setup
Read the sample application for more details.
-
Verify Docker Installation
Check that Docker and Docker Compose are properly installed:
# Check Docker installationdocker --version# Check Docker Compose installationdocker compose version -
Configure OpenTelemetry Collector
Create an
otel-collector-config.yamlfile with the following configuration. This defines the Prometheus receiver for scraping Postgres metrics, processors for batch processing and resource detection, and the Last9 exporter configuration:receivers:prometheus:config:scrape_configs:- job_name: postgres-exporterscrape_interval: 60sstatic_configs:- targets: ["postgres-exporter:9187"]processors:batch:timeout: 10ssend_batch_size: 10000resourcedetection:detectors: [env, system, docker, ec2, azure, gcp]timeout: 2sresource:attributes:- key: db_namevalue: postgres-dbaction: upsert- key: deployment.environmentvalue: devaction: upsertexporters:otlp/last9:endpoint: "{{ .Logs.WriteURL }}"headers:"Authorization": "{{ .Logs.AuthValue }}"debug:verbosity: detailedservice:pipelines:metrics:receivers: [prometheus]processors: [resourcedetection, resource, batch]exporters: [otlp/last9]telemetry:logs:level: infoReplace the placeholder values in the
exporterssection with your actual Last9 credentials from the Last9 Integrations page. -
Configure Custom Database Queries
Create a
queries.yamlfile to define custom metrics for monitoring slow queries and database performance:slow_queries:query: |SELECTpid,datname AS database,usename AS user,application_name,client_addr,EXTRACT(EPOCH FROM (now() - query_start)) AS query_time_seconds,REGEXP_REPLACE(SUBSTRING(query, 1, 500), E'[\n\r]+', ' ', 'g') AS query,state,wait_event_type,wait_event,backend_type,pg_blocking_pids(pid) AS blocked_byFROM pg_stat_activityWHERE (now() - query_start) > interval '1 minute'AND state <> 'idle'AND query NOT ILIKE '%pg_stat%'AND query NOT ILIKE '%pg_catalog%'ORDER BY query_time_seconds DESCmetrics:- pid:usage: "LABEL"description: "Process ID"- database:usage: "LABEL"description: "Database name"- user:usage: "LABEL"description: "Username"- application_name:usage: "LABEL"description: "Application name"- client_addr:usage: "LABEL"description: "Client address"- query_time_seconds:usage: "GAUGE"description: "Query execution time in seconds"- query:usage: "LABEL"description: "Query text (first 500 chars)"- state:usage: "LABEL"description: "Query state"- wait_event_type:usage: "LABEL"description: "Type of event the process is waiting for"- wait_event:usage: "LABEL"description: "Name of the event the process is waiting for"- backend_type:usage: "LABEL"description: "Type of backend"- blocked_by:usage: "LABEL"description: "PIDs of sessions blocking this query" -
Set Up Docker Compose Configuration
Create a
docker-compose.yamlfile with your PostgreSQL database connection details:services:postgres-exporter:image: quay.io/prometheuscommunity/postgres-exporterenvironment:- DATA_SOURCE_URI=<DB_HOST>/<DB_NAME>- DATA_SOURCE_USER=<DB_USER>- DATA_SOURCE_PASS=<DB_PASSWORD>volumes:- ./queries.yaml:/queries.yamlcommand: --extend.query-path="/queries.yaml"restart: unless-stoppedports:- "9187:9187"otel-collector:image: otel/opentelemetry-collector-contrib:0.118.0volumes:- ./otel-collector-config.yaml:/etc/otel/collector/config.yamlcommand: --config=/etc/otel/collector/config.yamldepends_on:- postgres-exporterReplace the following placeholders with your actual PostgreSQL database information:
<DB_HOST> — Your PostgreSQL database host<DB_NAME> — Your PostgreSQL database name<DB_USER> — Your PostgreSQL database username<DB_PASSWORD> — Your PostgreSQL database password -
Start the Monitoring Stack
Launch the monitoring services using Docker Compose:
docker compose -f docker-compose.yaml up -dThis command starts:
- Postgres Exporter: Collects metrics from your PostgreSQL database
- OpenTelemetry Collector: Receives metrics from Postgres Exporter and forwards them to Last9
Read the sample configuration for more details.
-
Create a PostgreSQL Monitoring User
Connect to PostgreSQL and create a dedicated monitoring user:
CREATE USER otel WITH PASSWORD 'your_secure_password';GRANT pg_monitor TO otel; -
Enable Slow Query Logging
Edit
postgresql.conf(typically/etc/postgresql/<version>/main/postgresql.conf):log_min_duration_statement = 1000 # log queries slower than 1s (ms)log_line_prefix = '%t [%p] %u@%d 'logging_collector = onlog_directory = '/var/log/postgresql'log_filename = 'postgresql-%Y-%m-%d.log'Reload PostgreSQL:
sudo systemctl reload postgresql -
Install OpenTelemetry Collector
sudo apt-get update && sudo apt-get install -y wgetwget https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v0.144.0/otelcol-contrib_0.144.0_linux_amd64.debsudo dpkg -i otelcol-contrib_0.144.0_linux_amd64.debsudo apt-get update && sudo apt-get install -y wgetwget https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v0.144.0/otelcol-contrib_0.144.0_linux_arm64.debsudo dpkg -i otelcol-contrib_0.144.0_linux_arm64.debwget https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v0.144.0/otelcol-contrib_0.144.0_linux_amd64.rpmsudo rpm -ivh otelcol-contrib_0.144.0_linux_amd64.rpm -
Configure OpenTelemetry Collector
Create
/etc/otelcol-contrib/config.yaml:receivers:postgresql:endpoint: localhost:5432username: otelpassword: ${env:POSTGRESQL_PASSWORD}databases:- postgrescollection_interval: 60stls:insecure: truefilelog:# Adjust path for your OS and PostgreSQL version:# Ubuntu/Debian: /var/log/postgresql/postgresql-<version>-main.log# RHEL/CentOS: /var/lib/pgsql/<version>/data/log/postgresql-*.loginclude: [/var/log/postgresql/postgresql-*.log]include_file_path: truestart_at: endretry_on_failure:enabled: truemultiline:line_start_pattern: '^\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}'operators:- type: regex_parserif: body matches "duration:"regex: '(?P<timestamp>\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}\s\w+)\s+\[(?P<pid>\d+)\]\s+(?P<user>[^@]+)@(?P<database>\S+)\s+LOG:\s+duration:\s+(?P<duration_ms>[\d.]+)\s+ms\s+(?P<stmt_type>\w+):\s+(?P<query>.*)'on_error: sendhostmetrics:collection_interval: 60sscrapers:cpu:metrics:system.cpu.logical.count:enabled: truememory:metrics:system.memory.utilization:enabled: truesystem.memory.limit:enabled: trueload:disk:filesystem:metrics:system.filesystem.utilization:enabled: truenetwork:paging:processors:batch:timeout: 5ssend_batch_size: 10000send_batch_max_size: 10000resourcedetection/system:detectors: ["system"]system:hostname_sources: ["os"]transform/logs:flatten_data: truelog_statements:- context: logstatements:- set(observed_time, Now())- set(time_unix_nano, observed_time_unix_nano) where time_unix_nano == 0- set(resource.attributes["service.name"], "postgresql")- set(resource.attributes["deployment.environment"], "production")transform/slow_queries:log_statements:- context: logconditions:- attributes["duration_ms"] != nilstatements:- set(attributes["db.system"], "postgresql")- set(attributes["db.operation.duration_ms"], attributes["duration_ms"])- set(attributes["db.user"], attributes["user"])- set(attributes["db.namespace"], attributes["database"])- set(attributes["db.query.text"], attributes["query"])- set(attributes["db.pid"], attributes["pid"])- set(attributes["slow_query"], true)- set(severity_text, "WARN")exporters:otlp/last9:endpoint: "{{ .Logs.WriteURL }}"headers:"Authorization": "{{ .Logs.AuthValue }}"debug:verbosity: detailedservice:pipelines:logs:receivers: [filelog]processors: [batch, resourcedetection/system, transform/logs, transform/slow_queries]exporters: [otlp/last9, debug]metrics:receivers: [postgresql, hostmetrics]processors: [batch, resourcedetection/system]exporters: [otlp/last9, debug]Replace the
endpointandAuthorizationvalues with your actual Last9 credentials from the Integrations page. -
Set Environment Variables
Edit
/etc/otelcol-contrib/otelcol-contrib.envand add:POSTGRESQL_PASSWORD=your_secure_password -
Grant Log File Access
The collector needs read access to PostgreSQL log files:
sudo usermod -aG adm otelcol-contrib -
Start the Collector
sudo systemctl daemon-reloadsudo systemctl enable otelcol-contribsudo systemctl start otelcol-contrib --feature-gates transform.flatten.logs
Understanding the setup (With Docker)
Postgres Exporter
The Postgres Exporter connects to your PostgreSQL database and exposes metrics in Prometheus format. It’s configured to:
- Connect to your database using the provided credentials
- Use custom queries defined in
queries.yaml - Expose metrics on port 9187
Custom Queries
The queries.yaml file defines custom metrics to collect from PostgreSQL. The example includes a slow_queries metric that:
- Identifies queries running longer than 1 minute
- Collects detailed information about these queries including:
- Process ID and database name
- Username and application name
- Query text and execution time
- Wait events and blocking processes
OpenTelemetry Collector
The OpenTelemetry Collector is configured to:
- Scrape metrics from Postgres Exporter every 60 seconds
- Add resource attributes like database name and environment
- Process metrics in batches for efficient transmission
- Export metrics to Last9 using the OTLP protocol
Verification
-
Check container status
docker ps -
Test Postgres Exporter is exposing metrics
curl http://localhost:9187/metrics -
Review collector logs
docker logs otel-collector -
Verify metrics in Last9
Log into your Last9 account and verify metrics are being received in Grafana.
-
Check collector status
sudo systemctl status otelcol-contrib -
View collector logs
sudo journalctl -u otelcol-contrib -f -
Verify metrics in Last9
Log into your Last9 account and verify metrics are being received. Look for metrics like
postgresql.backends,postgresql.commits, andpostgresql.rows.
Troubleshooting
-
Container issues
# Check container statusdocker ps -a# View container logsdocker logs postgres-exporterdocker logs otel-collector -
Connection issues
# Check Postgres Exporter logs for connection errorsdocker logs postgres-exporterCommon connection issues include:
- Incorrect database credentials
- Network connectivity problems
- PostgreSQL not allowing connections from the exporter
-
OpenTelemetry Collector issues
# Check configurationdocker exec otel-collector cat /etc/otel/collector/config.yaml# Restart collectordocker compose restart otel-collector
-
Connection refused to PostgreSQL
# Verify PostgreSQL is runningsudo systemctl status postgresql# Test connection with monitoring userpsql -U otel -h localhost -c "SELECT 1;" -
Log files not found
# Find your log directorysudo -u postgres psql -c "SHOW log_directory;"sudo -u postgres psql -c "SHOW log_filename;" -
Collector cannot read logs
# Check file permissionsls -la /var/log/postgresql/# Ensure otelcol-contrib is in the adm groupgroups otelcol-contrib
Please get in touch with us on Discord or Email if you have any questions.