> ## Documentation Index
> Fetch the complete documentation index at: https://otel.fyi/llms.txt
> Use this file to discover all available pages before exploring further.

# Postgresql

> OpenTelemetry receiver for Postgresql

# Postgresql Receiver

![Status](https://img.shields.io/badge/status-beta-yellow)

**Available in:** `contrib`

**Maintainers:** [@antonblock](https://github.com/antonblock), [@ishleenk17](https://github.com/ishleenk17), [@Caleb-Hurshman](https://github.com/Caleb-Hurshman), [@ebrdarSplunk](https://github.com/ebrdarSplunk), [@XSAM](https://github.com/XSAM), [@akshays-19](https://github.com/akshays-19), [@sv-splunk](https://github.com/sv-splunk), [@splunk-shanu](https://github.com/splunk-shanu)

**Source:** [opentelemetry-collector-contrib](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/postgresqlreceiver)

## Supported Telemetry

![Logs](https://img.shields.io/badge/logs-development-blue) ![Metrics](https://img.shields.io/badge/metrics-beta-green)

## Overview

## Prerequisites

See PostgreSQL documentation for [supported versions](https://www.postgresql.org/support/versioning).

The monitoring user must be granted `SELECT` on `pg_stat_database`.

> \[!NOTE]
> The feature gate `receiver.postgresql.separateSchemaAttr` addresses an inconsistency in how schema names
> are reported across different metric types. When enabled, schema names are consistently reported in a
> dedicated `postgresql.schema.name` resource attribute.
>
> **Status:** Alpha (disabled by default)
>
> **When disabled (default behavior):**
>
> * Table metrics: `postgresql.table.name = "schema_name.table_name"` (schema included)
> * Index metrics: `postgresql.table.name = "table_name"` (schema **missing**)
> * Function metrics: Schema reported separately in some cases
>
> **When enabled (recommended for consistency):**
>
> * All metrics consistently use:
>   * `postgresql.schema.name = "schema_name"`
>   * `postgresql.table.name = "table_name"`
>
> This ensures reliable correlation of metrics when tables with identical names exist across different schemas.
> To enable:
>
> ```bash theme={null}
> otelcol-contrib --feature-gates=receiver.postgresql.separateSchemaAttr
> ```
>
> **Note:** This gate is mutually exclusive with `receiver.postgresql.useOTelSemconv`. Both cannot be
> enabled at the same time.
>
> See [https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/29559](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/29559) for more details.

## Configuration

The following setting is required to create a database connection:

* `username`

Exactly one of the following credential settings is also required:

* `password`: A static PostgreSQL password.
* `db_auth`: The component ID of a database authentication provider extension. The provider supplies the password, and can optionally override `username`, whenever the receiver opens a connection. `db_auth` and `password` are mutually exclusive.

For example, a provider extension must be declared, referenced from the receiver,
and enabled in `service.extensions`:

```yaml theme={null}
extensions:
  aws_iam_db_auth:
    region: us-east-2

receivers:
  postgresql:
    endpoint: my-database.example.com:5432
    username: monitor
    db_auth: aws_iam_db_auth

service:
  extensions: [aws_iam_db_auth]
  pipelines:
    metrics:
      receivers: [postgresql]
```

The selected provider extension must be included in the Collector distribution.
Provider-wide settings, such as the region above, belong to the extension;
`endpoint` and `username` are passed to it by the receiver for each credential
request.

The following settings are optional:

* `endpoint` (default = `localhost:5432`): The endpoint of the PostgreSQL server. Whether using TCP or Unix sockets, this value should be `host:port`. If `transport` is set to `unix`, the endpoint will internally be translated from `host:port` to `/host.s.PGSQL.port`

* `transport` (default = `tcp`): The transport protocol being used to connect to PostgreSQL. Available options are `tcp` and `unix`.

* `databases` (default = `[]`): The list of databases for which the receiver will attempt to collect statistics. If an empty list is provided, the receiver will attempt to collect statistics for all non-template databases. This list applies to metrics only; the query sample and top query collectors ignore it and are filtered solely by `exclude_databases`.

* `exclude_databases` (default = `[]`): List of databases excluded from statistics, query samples, and top queries. Excluded databases are filtered out of every collection query and the receiver opens no per-database connection to them. Exception: the receiver always connects to the configured `connect_database` (default `postgres`) for discovery and server-level queries, even if it is listed here.

* `connect_database` (default = `postgres`): The database the receiver connects to for discovery and server-level queries, including `pg_stat_statements`. Independent of `databases` — `pg_stat_statements` is tracked cluster-wide, so any database with the extension installed works as the connection target, regardless of which databases are being monitored. Use this if `pg_stat_statements` lives outside `postgres`, or if you connect through a dedicated monitoring-only database:
  ```yaml theme={null}
  connect_database: "monitoring"   # extension lives here
  databases:
    - "mydb"                       # database being monitored
  ```

> \[!NOTE]
> Managed PostgreSQL services create internal databases that no customer credential can connect to. The receiver discovers them like any other database and logs a connection error on every scrape. If you use one of these services, add its internal databases to `exclude_databases`:
>
> | Service | Databases to exclude |
> | - | - |
> | Amazon RDS / Aurora PostgreSQL | `rdsadmin` |
> | Azure Database for PostgreSQL | `azure_maintenance` |
> | Google Cloud SQL | `cloudsqladmin` |
> | Google AlloyDB | `alloydbadmin`, `alloydbmetadata` |
>
> Example: `exclude_databases: [rdsadmin]`

The following settings are also optional and nested under `tls` to help configure client transport security

* `insecure` (default = `false`): Whether to enable client transport security for the PostgreSQL connection.

* `insecure_skip_verify` (default = `true`): Whether to validate server name and certificate if client transport security is enabled.

* `cert_file` (default = `$HOME/.postgresql/postgresql.crt`): A certificate used for client authentication, if necessary.

* `key_file` (default = `$HOME/.postgresql/postgresql.key`): An SSL key used for client authentication, if necessary.

* `ca_file` (default = ""): A set of certificate authorities used to validate the database server's SSL certificate.

* `collection_interval` (default = `10s`): This receiver collects metrics on an interval. This value must be a string readable by Golang's [time.ParseDuration](https://pkg.go.dev/time#ParseDuration). Valid time units are `ns`, `us` (or `µs`), `ms`, `s`, `m`, `h`.

* `initial_delay` (default = `1s`): defines how long this receiver waits before starting.

### Query Sample Collection

We provide functionality to collect the query sample from PostgreSQL. It will get historical query
from `pg_stat_activity`. To enable it, you will need the following configuration

```
...
    events:
      db.server.query_sample:
        enabled: true
...
```

By default, query sample collection is disabled, also note, to use it, you will need
to grant the user you are using `pg_monitor`. Take the example from `testdata/integration/init.sql`

```sql theme={null}
GRANT pg_monitor TO otelu;
```

To correlate query samples with traces, set the PostgreSQL [`application_name`](https://www.postgresql.org/docs/current/runtime-config-logging.html#GUC-APPLICATION-NAME)
for the client connection to a valid [W3C `traceparent`](https://www.w3.org/TR/trace-context/#traceparent-header) value before running the query:

```sql theme={null}
SET application_name = '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01';
```

When query sample collection observes a valid `traceparent` in `application_name`, the receiver sets the trace ID and span ID on the emitted
`db.server.query_sample` log record. This enables correlation between the query sample and the originating trace.

The following options are available:

* `max_rows_per_query`: (optional, default=1000) The max number of rows would return from the query
  against `pg_stat_activity`.

### Top Query Collection

We provide functionality to collect the most executed queries from PostgreSQL. It will get data from `pg_stat_statements` and report incremental value of `total_exec_time`, `total_plan_time`, `calls`, `rows`, `shared_blks_dirtied`, `shared_blks_hit`, `shared_blks_read`, `shared_blks_written`, `temp_blks_read`, `temp_blks_written`. To enable it, you will need the following configuration

```
...
    events:
      db.server.top_query:
        enabled: true
...
```

Along with those attributes, we will also report the query plan we gathered if it is possible.

By default, top query collection is disabled, also note, to use it, you will need
to create the extension in the database the receiver connects to (`connect_database`,
default `postgres`) — `pg_stat_statements` is tracked cluster-wide, so it does not need to be
created in every database. Take the example from `testdata/integration/02-create-extension.sh`

```sql theme={null}
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
```

> \[!NOTE]
> This is different from [vector metrics](#vector-metrics) below, which query per-database
> clients — `connect_database` has no effect there, and `pg_stat_statements` must be installed
> in every scanned database for vector metrics to work.

The following options are available:

* `max_rows_per_query`: (optional, default=1000) The max number of rows would return from the query
  against `pg_stat_statements`.
* `top_n_query`: (optional, default=200) The maximum number of active queries to report (to the next consumer) in a single run.
* `max_explain_each_interval`: (optional, default=1000). The maximum number of explain query to be sent in each scrape interval. The top query
  collection would not get the query plan directly. Instead, we need to mimic the query in the database and get the query plan from database
  separately. This could lead some resources usage and limit this will reduce the impact on your database.
* `query_plan_cache_size`: (optional, default=1000). The query plan cache size. Once we got explain for one query, we will store it in the cache.
  This defines the cache's size for query plan.
* `query_plan_cache_ttl`: (optional, default=1h). How long before the query plan cache got expired. Example values: `1m`, `1h`.
* `collection_interval`: (optional, default=60s). This receiver can collect top\_query metrics on an interval. If not provided then the global collection\_interval takes effect. This value must be a string readable by Golang's [time.ParseDuration](https://pkg.go.dev/time#ParseDuration). Valid time units are `ns`, `us` (or `µs`), `ms`, `s`, `m`, `h`.

#### `db.server.query_plan`

By default, `db.server.top_query` carries the query's execution plan in its `postgresql.query_plan`
attribute. Enabling `db.server.query_plan` isolates the plan on a record of its own, where it can be
filtered, routed or dropped independently of the query statistics, and where an oversized plan does
not take those statistics with it when a batcher splits by size.

`db.server.top_query` is then emitted **without** its `postgresql.query_plan` attribute, and the plan
itself is reported on `db.server.query_plan`, joined back to its query via `postgresql.queryid`,
`db.namespace` and `postgresql.rolname` — `queryid` alone can repeat across databases and across
roles in the same database. A query with no plan available yet (not yet explained, or the `EXPLAIN`
failed) produces no `db.server.query_plan` record. Leaving `db.server.query_plan` disabled preserves
the previous behavior exactly.

Known limitations of the join key:

* It doesn't distinguish `toplevel`, so a top-level statement and a nested one it calls can still
  collide (only with `pg_stat_statements.track = all`, not the default).
* `postgresql.rolname` is empty once the role that ran the statement is dropped, so two rows from
  different dropped roles can collide too (mainly with ephemeral roles or credential rotation).

`db.server.query_plan` is sourced from the same collection as `db.server.top_query` and only splits
the plan out of it, so it needs no grants of its own, and enabling it without `db.server.top_query`
is a configuration error.

```yaml theme={null}
receivers:
  postgresql:
    events:
      db.server.top_query:
        enabled: true
      db.server.query_plan:                      # reports the execution plan on its own event, off db.server.top_query
        enabled: true
```

### Vector Metrics

The receiver can report [pgvector](https://github.com/pgvector/pgvector) similarity-search and insert activity
through a set of opt-in metrics.

Prerequisites:

* PostgreSQL 13 or later.
* The [pgvector](https://github.com/pgvector/pgvector) extension installed in each scanned database. The `l1`,
  `hamming`, and `jaccard` distance functions additionally require pgvector 0.7.0 or later.
* The `pg_stat_statements` extension (version 1.8+) installed and enabled in each scanned database (see below).

#### Search metrics

Three metrics report similarity-search activity

* `postgresql.vector.search.calls`: the cumulative number of vector search executions.
* `postgresql.vector.search.duration`: the cumulative execution time (in seconds) of vector searches.
* `postgresql.vector.search.rows_returned`: the cumulative number of rows returned by vector searches.

> \[!NOTE]
> The distance function is inferred from the query text alone, so the receiver cannot tell which
> operator implementation is actually invoked. If the `pg_trgm` extension is also installed, its
> text-similarity `<->` operator is indistinguishable from pgvector's L2 `<->` operator, so queries
> such as `ORDER BY text_col <-> 'abc'` may be counted under the `l2` distance function.

#### Insert metrics

These metrics report write activity against pgvector tables

* `postgresql.vector.insert.rows`: the cumulative number of vectors inserted.
* `postgresql.vector.insert.duration`: the cumulative execution time (in seconds) of those inserts.

All of these metrics are disabled by default. Enable the ones you need via:

```yaml theme={null}
receivers:
  postgresql:
    metrics:
      postgresql.vector.search.calls:
        enabled: true
      postgresql.vector.search.duration:
        enabled: true
      postgresql.vector.search.rows_returned:
        enabled: true
      postgresql.vector.insert.rows:
        enabled: true
      postgresql.vector.insert.duration:
        enabled: true
```

The `pg_stat_statements` extension must be created in every database you want these metrics collected from:

```sql theme={null}
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
```

### Example Configuration

```yaml theme={null}
receivers:
  postgresql:
    endpoint: localhost:5432
    transport: tcp
    username: otel
    password: ${env:POSTGRESQL_PASSWORD}
    databases:
      - otel
    collection_interval: 10s
    tls:
      insecure: false
      insecure_skip_verify: false
      ca_file: /home/otel/authorities.crt
      cert_file: /home/otel/mypostgrescert.crt
      key_file: /home/otel/mypostgreskey.key
    events:
      db.server.query_sample:
        enabled: true
      db.server.top_query:
        enabled: true
    query_sample_collection:
      max_rows_per_query: 100
    top_query_collection:
      max_rows_per_query: 100
      top_n_query: 100
      collection_interval: 60s
```

The full list of settings exposed for this receiver are documented in [config.go](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/postgresqlreceiver/config.go) with detailed sample configurations in [testdata/config.yaml](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/postgresqlreceiver/testdata/config.yaml). TLS config is documented further under the [opentelemetry collector's configtls package](https://github.com/open-telemetry/opentelemetry-collector/blob/main/config/configtls/README.md).

## Connection pool feature

The feature gate `receiver.postgresql.connectionPool` allows to enable the creation & reuse of a pool per database for the connections instead of creating & closing on each scrape.
This is generally a useful optimization but also alleviates the volume of generated audit logs when the PostgreSQL instance is configured with `log_connections=on` and/or `log_disconnections=on`.

When this feature gate is enabled, the following optional settings are available nested under `connection_pool` to help configure the connection pools:

* `max_idle_time`: The maximum amount of time a connection may be idle before being closed.
* `max_lifetime`: The maximum amount of time a connection may be reused.
* `max_idle`: The maximum number of connections in the idle connection pool.
* `max_open`: The maximum number of open connections to the database.

Those settings and their defaults are further documented in the [`sql/database`](https://pkg.go.dev/database/sql#DB) package.

The connection pool composes with a `db_auth` block (e.g. AWS IAM). The
credential is re-resolved on every new connection the pool opens, so a short-lived
token (an RDS IAM token lives \~15 minutes) is re-minted as the pool grows or
replaces connections, and a connection is never opened with an expired token.
Connections already established stay valid for their lifetime — IAM authenticates
only at connection open, not per query — so tune `max_lifetime` to bound how long
a connection lives before it must reconnect with a fresh token.

### Example Configuration

```yaml theme={null}
receivers:
  postgresql:
    endpoint: localhost:5432
    transport: tcp
    username: otel
    password: ${env:POSTGRESQL_PASSWORD}
    connection_pool:
      max_idle_time: 10m
      max_lifetime: 0
      max_idle: 2
      max_open: 5
```

## OpenTelemetry semantic conventions feature gate

The feature gate `receiver.postgresql.useOTelSemconv` (alpha, disabled by default) controls the resource model used by this receiver:

* **Gate disabled (default):** Legacy per-entity resource model. Each database, table, and index emits metrics under a separate resource with `postgresql.database.name`, `postgresql.table.name`, `postgresql.index.name`, and `postgresql.schema.name` as resource attributes. `service.instance.id` is in `host:port` format.
* **Gate enabled:** Single resource per server. All metrics are emitted under one resource with `service.instance.id` (UUID v5) as a resource attribute, aligning with OpenTelemetry semantic conventions. Metric-level attributes `db.namespace`, `db.collection.name`, and `postgresql.index.name` are present on applicable metrics.

`server.address` and `server.port` are emitted in both models and are not affected by this gate.

This gate is mutually exclusive with `receiver.postgresql.separateSchemaAttr` — both cannot be enabled simultaneously.

### Server address resolution

`server.address` and `server.port` describe the monitored server. When `endpoint` is a loopback address
(`localhost`, `127.0.0.1`, or `::1`), the server is only reachable because it is co-located with the
collector, so `server.address` reports the name of the machine running the collector rather than the
loopback address, which every monitored host would otherwise report identically. This is the same host
already used to derive `service.instance.id`, and both are resolved when the receiver starts, so the
two attributes always agree and a host name change is picked up on restart. Non-loopback endpoints
are reported as configured, and with `transport: unix` the socket path is reported instead.

## Metrics

Details about the metrics produced by this receiver can be found in [metadata.yaml](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/postgresqlreceiver/metadata.yaml)

> \[!NOTE]
> The optional `postgresql.query.execution.time` metric requires the `pg_stat_statements` extension to be
> installed and enabled.

## Metrics

| Metric Name | Description | Unit | Type | Attributes |
| - | - | - | - | - |
| ✅ `postgresql.backends` | The number of backend processes associated with each database. Counts backends across all connection states (active, idle, idle-in-transaction) and all backend types, including non-client backends such as autovacuum and parallel workers. | 1 | UpDownCounter | db.namespace |
| ✅ `postgresql.bgwriter.buffers.allocated` | Number of buffers allocated. | \{buffers} | Counter | |
| ✅ `postgresql.bgwriter.buffers.writes` | Number of buffers written. | \{buffers} | Counter | bg\_buffer\_source |
| ✅ `postgresql.bgwriter.checkpoint.count` | The number of checkpoints performed. | \{checkpoints} | Counter | bg\_checkpoint\_type |
| ✅ `postgresql.bgwriter.duration` | Total time spent writing and syncing files to disk by checkpoints. | ms | Counter | bg\_duration\_type |
| ✅ `postgresql.bgwriter.maxwritten` | Number of times the background writer stopped a cleaning scan because it had written too many buffers. | 1 | Counter | |
| ❌ `postgresql.blks_hit` | Number of times disk blocks were found already in the buffer cache. | \{blks\_hit} | Counter | db.namespace |
| ❌ `postgresql.blks_read` | Number of disk blocks read in this database. | \{blks\_read} | Counter | db.namespace |
| ✅ `postgresql.blocks_read` | The number of blocks read. | 1 | Counter | source, db.namespace, db.collection.name |
| ✅ `postgresql.commits` | The number of commits. | 1 | Counter | db.namespace |
| ✅ `postgresql.connection.max` | Configured maximum number of client connections allowed | \{connections} | Gauge | |
| ✅ `postgresql.database.count` | Number of user databases. | \{databases} | UpDownCounter | |
| ❌ `postgresql.database.locks` | The number of database locks, including those held by the receiver's own connections. | \{lock} | Gauge | relation, mode, lock\_type, db.namespace |
| ✅ `postgresql.db_size` | The database disk usage. | By | UpDownCounter | db.namespace |
| ❌ `postgresql.deadlocks` | The number of deadlocks. | \{deadlock} | Counter | db.namespace |
| ❌ `postgresql.function.calls` | The number of calls made to a function. Requires `track_functions=pl\|all` in Postgres config. | \{call} | Counter | function, db.namespace |
| ✅ `postgresql.index.scans` | The number of index scans on a table. | \{scans} | Counter | db.namespace, db.collection.name, postgresql.index.name |
| ✅ `postgresql.index.size` | The size of the index on disk. | By | Gauge | db.namespace, db.collection.name, postgresql.index.name |
| ✅ `postgresql.operations` | The number of db row operations. | 1 | Counter | operation, db.namespace, db.collection.name |
| ❌ `postgresql.query.conflicts` | Number of queries canceled due to conflicts with recovery on this database. Conflicts only occur on standby servers; this metric will be zero on primary servers. | \{query} | Counter | postgresql.conflict.type, db.namespace |
| ❌ `postgresql.query.execution.time` | The total execution time of SQL statements currently tracked by pg\_stat\_statements for the database. | s | Counter | db.namespace |
| ✅ `postgresql.replication.data_delay` | The amount of data delayed in replication. | By | Gauge | replication\_client |
| ✅ `postgresql.rollbacks` | The number of rollbacks. | 1 | Counter | db.namespace |
| ✅ `postgresql.rows` | The number of rows in the database. | 1 | UpDownCounter | state, db.namespace, db.collection.name |
| ❌ `postgresql.sequential_scans` | The number of sequential scans. | \{sequential\_scan} | Counter | db.namespace, db.collection.name |
| ✅ `postgresql.table.count` | Number of user tables in a database. | \{table} | UpDownCounter | db.namespace |
| ✅ `postgresql.table.size` | Total disk space used by a table, including its indexes and TOAST data. | By | UpDownCounter | db.namespace, db.collection.name |
| ✅ `postgresql.table.vacuum.count` | Number of times a table has manually been vacuumed. | \{vacuum} | Counter | db.namespace, db.collection.name |
| ❌ `postgresql.temp.io` | Total amount of data written to temporary files by queries. | By | Counter | db.namespace |
| ❌ `postgresql.temp_files` | The number of temp files. | \{temp\_file} | Counter | db.namespace |
| ❌ `postgresql.tup_deleted` | Number of rows deleted by queries in the database. | \{tup\_deleted} | Counter | db.namespace |
| ❌ `postgresql.tup_fetched` | Number of rows fetched by queries in the database. | \{tup\_fetched} | Counter | db.namespace |
| ❌ `postgresql.tup_inserted` | Number of rows inserted by queries in the database. | \{tup\_inserted} | Counter | db.namespace |
| ❌ `postgresql.tup_returned` | Number of rows returned by queries in the database. | \{tup\_returned} | Counter | db.namespace |
| ❌ `postgresql.tup_updated` | Number of rows updated by queries in the database. | \{tup\_updated} | Counter | db.namespace |
| ❌ `postgresql.vector.insert.duration` | The cumulative execution time of statements that insert vectors into pgvector tables. | s | Counter | db.namespace |
| ❌ `postgresql.vector.insert.rows` | The number of vectors inserted into pgvector tables. | \{vectors} | Counter | db.namespace |
| ❌ `postgresql.vector.search.calls` | The number of vector similarity search operations executed, grouped by the distance function used. | \{search} | Counter | postgresql.distance.function.name, db.namespace |
| ❌ `postgresql.vector.search.duration` | The cumulative execution time of vector similarity searches, grouped by the distance function used. | s | Counter | postgresql.distance.function.name, db.namespace |
| ❌ `postgresql.vector.search.rows_returned` | The cumulative number of rows returned by vector similarity searches, grouped by the distance function used. | \{rows} | Counter | postgresql.distance.function.name, db.namespace |
| ✅ `postgresql.wal.age` | Age of the oldest WAL file. | s | Gauge | |
| ❌ `postgresql.wal.delay` | Time between flushing recent WAL locally and receiving notification that the standby server has completed an operation with it. | s | Gauge | wal\_operation\_lag, replication\_client |
| ✅ `postgresql.wal.lag` | Time between flushing recent WAL locally and receiving notification that the standby server has completed an operation with it. | s | Gauge | wal\_operation\_lag, replication\_client |

## Attributes

| Attribute Name | Description | Type | Values |
| - | - | - | - |
| `source` | The source of a buffer write. | string | `backend`, `backend_fsync`, `checkpoints`, `bgwriter` |
| `type` | The type of checkpoint state. | string | `requested`, `scheduled` |
| `type` | The type of time spent during the checkpoint. | string | `sync`, `write` |
| `db.collection.name` | The name of the table within the database. | string | |
| `db.namespace` | The database namespace, following the `{database}\|{schema}` format defined by OpenTelemetry semantic conventions for PostgreSQL. | string | |
| `db.query.text` | The text of the database query being executed. | string | |
| `db.system.name` | The database management system (DBMS) product as identified by the client instrumentation. | string | `postgresql` |
| `function` | The name of the function. | string | |
| `lock_type` | Type of the lockable object. | string | |
| `mode` | Name of the lock mode held or desired by the process. | string | |
| `network.peer.address` | IP address of the client connected to this backend. | string | |
| `network.peer.port` | TCP port number that the client is using for communication with this backend. | int | |
| `operation` | The database operation. | string | `ins`, `upd`, `del`, `hot_upd` |
| `postgresql.application_name` | Name of the application that is connected to this backend. | string | |
| `postgresql.backend.connection.start` | UTC timestamp (RFC3339) when this backend was started. | string | |
| `postgresql.blocking.lock.mode` | The lock mode being requested by the blocked session (e.g. RowExclusiveLock, AccessExclusiveLock). Empty string when not blocked. | string | |
| `postgresql.blocking.lock.relation` | The name of the relation (table) being waited on. Empty string when not blocked or when lock is not on a relation. | string | |
| `postgresql.blocking.lock.type` | The type of lock resource being waited on (e.g. relation, transactionid, tuple). Empty string when not blocked. | string | |
| `postgresql.blocking.pids` | Array of PIDs of sessions blocking this session (from pg\_blocking\_pids). Empty array when not blocked. | string | |
| `postgresql.blocking.start_time` | UTC timestamp (RFC3339) when the current lock wait began, derived from pg\_locks.waitstart. Empty string when not blocked. | string | |
| `postgresql.blocking.transaction.start_time` | UTC timestamp (RFC3339) when the current transaction started. Empty string when no active transaction. | string | |
| `postgresql.blocking.wait_duration` | Whole seconds this session has been waiting for a lock, measured from pg\_locks.waitstart. 0 when not blocked. | int | |
| `postgresql.calls` | Number of times the statement was executed, reported in delta value. | int | |
| `postgresql.client_hostname` | Host name of the connected client, as reported by a reverse DNS lookup of client\_addr. | string | |
| `postgresql.conflict.type` | The type of recovery conflict that caused a query to be canceled on a standby server. | string | `tablespace`, `lock`, `snapshot`, `bufferpin`, `deadlock` |
| `postgresql.distance.function.name` | The vector distance (similarity) function used by the query, one of the pgvector distance functions. | string | `cosine`, `l2`, `inner_product`, `l1`, `hamming`, `jaccard` |
| `postgresql.index.name` | The name of the index on a table. | string | |
| `postgresql.pid` | Process ID of this backend. | int | |
| `postgresql.query_id` | Identifier of this backend's most recent query. If state is active this field shows the identifier of the currently executing query. In all other states, it shows the identifier of last query that was executed. | string | |
| `postgresql.query_plan` | The execution plan used by PostgreSQL for the query. | string | |
| `postgresql.query_start` | Time when the currently active query was started, or if state is not active, when the last query was started. | string | |
| `postgresql.queryid` | Hash code to identify identical normalized queries. | string | |
| `postgresql.rolname` | The name of the PostgreSQL role that executed the query. | string | |
| `postgresql.rows` | Total number of rows retrieved or affected by the statement, reported in delta value. | int | |
| `postgresql.shared_blks_dirtied` | Total number of shared blocks dirtied by the statement, reported in delta value. | int | |
| `postgresql.shared_blks_hit` | Total number of shared block cache hits by the statement, reported in delta value. | int | |
| `postgresql.shared_blks_read` | Total number of shared blocks read by the statement, reported in delta value. | int | |
| `postgresql.shared_blks_written` | Total number of shared blocks written by the statement, reported in delta value. | int | |
| `postgresql.state` | Current overall state of this backend | string | |
| `postgresql.temp_blks_read` | Total number of temp blocks read by the statement, reported in delta value. | int | |
| `postgresql.temp_blks_written` | Total number of temp blocks written by the statement, reported in delta value. | int | |
| `postgresql.total_exec_time` | Total time spent executing the statement, in delta seconds. | double | |
| `postgresql.total_plan_time` | Total time spent planning the statement, in delta seconds. | double | |
| `postgresql.wait_event` | Wait event name if backend is currently waiting, otherwise NULL. | string | |
| `postgresql.wait_event_type` | The type of event for which the backend is waiting, if any; otherwise NULL. | string | |
| `relation` | The name of the relation (table, index, view, etc.) targeted by the lock, or empty if the target is not a relation or part of a relation. | string | |
| `replication_client` | The IP address of the client connected to this backend. If this field is "unix", it indicates either that the client is connected via a Unix socket. | string | |
| `source` | The block read source type. | string | `heap_read`, `heap_hit`, `idx_read`, `idx_hit`, `toast_read`, `toast_hit`, `tidx_read`, `tidx_hit` |
| `state` | The tuple (row) state. | string | `dead`, `live` |
| `user.name` | Name of the user logged into this backend. | string | |
| `operation` | The operation which is responsible for the lag. | string | `flush`, `replay`, `write` |

## Resource Attributes

| Attribute Name | Description | Type | Enabled |
| - | - | - | - |
| `db.system.version` | The database version of the instance. Examples include "13.18" and "17.2". | string | ❌ |
| `postgresql.database.name` | The name of the database. | string | ✅ |
| `postgresql.index.name` | The name of the index on a table. | string | ✅ |
| `postgresql.schema.name` | The schema name. | string | ✅ |
| `postgresql.table.name` | The table name. | string | ✅ |
| `server.address` | The address of the PostgreSQL server. A loopback endpoint is reported as the name of the machine running the collector, because the server is then co-located with it. With transport `unix` the socket path is reported instead. | string | ✅ |
| `server.port` | The port number of the PostgreSQL server. | int | ✅ |
| `service.instance.id` | A unique identifier of the PostgreSQL instance. | string | ✅ |
| `service.name` | Logical name of the service. When enabled, defaults to unknown\_service:postgresql. | string | ❌ |
| `service.namespace` | Logical namespace for the service (for example team or environment). When enabled, defaults to an empty string until set via configuration. | string | ❌ |

## Configuration

### Example Configuration

```yaml theme={null}
postgresql/minimal:
  endpoint: localhost:5432
  username: otel
  password: ${env:POSTGRESQL_PASSWORD}
  top_query_collection:
    top_n_query: 1234
    query_plan_cache_ttl: 123s
postgresql/pool:
  endpoint: localhost:5432
  transport: tcp
  username: otel
  password: ${env:POSTGRESQL_PASSWORD}
  connection_pool:
    max_idle_time: 30s
    max_idle: 5
postgresql/all:
  endpoint: localhost:5432
  transport: tcp
  username: otel
  password: ${env:POSTGRESQL_PASSWORD}
  databases:
    - otel
  exclude_databases:
    - template0
  connect_database: monitoring
  collection_interval: 10s
  tls:
    insecure: false
    insecure_skip_verify: false
    ca_file: /home/otel/authorities.crt
    cert_file: /home/otel/mypostgrescert.crt
    key_file: /home/otel/mypostgreskey.key
  connection_pool:
    max_idle_time: 30s
    max_lifetime: 1m
    max_idle: 5
    max_open: 10
```

***

*Last generated: 2026-10-05*
