{"id":49583057,"url":"https://github.com/becomeliminal/pgxporter","last_synced_at":"2026-05-03T21:05:36.073Z","repository":{"id":65634300,"uuid":"596161810","full_name":"becomeliminal/pgxporter","owner":"becomeliminal","description":"Prometheus exporter for PostgreSQL server-side metrics. ","archived":false,"fork":false,"pushed_at":"2026-04-18T16:31:47.000Z","size":105,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-04-18T18:31:56.315Z","etag":null,"topics":["go","golang","pgx","postgres","postgres-exporter","postgresql","prometheus","prometheus-exporter"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/becomeliminal.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-02-01T15:44:47.000Z","updated_at":"2026-04-18T16:31:42.000Z","dependencies_parsed_at":"2024-06-21T01:08:02.649Z","dependency_job_id":"3372b080-28f9-41df-8e88-ce8a60e09839","html_url":"https://github.com/becomeliminal/pgxporter","commit_stats":{"total_commits":51,"total_committers":1,"mean_commits":51.0,"dds":0.0,"last_synced_commit":"d3c18622786f728e0faa314bec509cae31dd8972"},"previous_names":["becomeliminal/pgxporter"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/becomeliminal/pgxporter","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/becomeliminal%2Fpgxporter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/becomeliminal%2Fpgxporter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/becomeliminal%2Fpgxporter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/becomeliminal%2Fpgxporter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/becomeliminal","download_url":"https://codeload.github.com/becomeliminal/pgxporter/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/becomeliminal%2Fpgxporter/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32584660,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-03T06:36:36.687Z","status":"ssl_error","status_checked_at":"2026-05-03T06:36:09.306Z","response_time":103,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["go","golang","pgx","postgres","postgres-exporter","postgresql","prometheus","prometheus-exporter"],"created_at":"2026-05-03T21:05:31.771Z","updated_at":"2026-05-03T21:05:36.062Z","avatar_url":"https://github.com/becomeliminal.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# pgxporter\n\n[![CI](https://github.com/becomeliminal/pgxporter/actions/workflows/ci.yaml/badge.svg)](https://github.com/becomeliminal/pgxporter/actions/workflows/ci.yaml)\n[![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)\n\nA pgx-native Prometheus exporter library for PostgreSQL 13–18. Intended as an alternative to [`prometheus-community/postgres_exporter`](https://github.com/prometheus-community/postgres_exporter), which is still built on [`lib/pq`](https://github.com/lib/pq) — a driver that's been in maintenance mode since 2021.\n\nWe leverage [`pgx/v5`](https://github.com/jackc/pgx), which is low-level, fast, actively maintained, and exposes PostgreSQL-specific features that `database/sql` doesn't — binary protocol, per-connection type registry, and the `BeforeConnect` hook that makes first-class cloud IAM auth possible.\n\nThis is a **library** — you compose it into your own `main.go` rather than running a pre-built binary. See [Quickstart](#quickstart) below.\n\n## Why pgxporter\n\n- **pgx v5 foundation.** Binary protocol, prepared-statement cache, persistent `pgxpool` per database, parallel scrapes via `errgroup`. `postgres_exporter` opens a new `sql.DB` (with `MaxOpenConns=1`) every scrape and runs collectors serially.\n- **Cloud IAM auth, first-class.** AWS RDS, GCP CloudSQL, Azure Database — short-lived tokens minted via pgx's [`BeforeConnect`](https://pkg.go.dev/github.com/jackc/pgx/v5/pgxpool#Config) hook. No DSN-rewriting wrappers.\n- **Declarative collector extension.** Add metrics in YAML without writing Go — the supported replacement for `postgres_exporter`'s deprecated `queries.yaml`. Custom Go collectors are still available as an escape hatch.\n- **Drop-in dashboard compatibility.** `Opts.MetricPrefix = MetricPrefixPg` flips metric names from `pg_stat_database_*` to `pg_database_*`, so community `postgres_exporter` Grafana dashboards work unchanged.\n- **PG 13–18 tested.** Real-Postgres integration matrix in CI across PG 13.22, 14.19, 15.14, 16.10, 17.6, 18.0.\n\n## Feature matrix\n\nHonest comparison against `postgres_exporter` v0.19.x.\n\n| | pgxporter | postgres_exporter |\n| --- | --- | --- |\n| **Driver** | pgx/v5 (actively maintained) | lib/pq (maintenance mode) |\n| **Connection pooling** | `pgxpool` persistent per DB | `sql.DB` with `MaxOpenConns=1`, reopened per scrape |\n| **Parallel scrapes** | `errgroup` fan-out | serial |\n| **Scrape wall time** (HTTP, warm, 1 DB, PG 17.6)¹ | **8.7 ms** | 20.2 ms (**2.3× slower**) |\n| **Series per scrape**¹ | **2,610** | 1,967 (−25%) |\n| **Scrape context propagation** | full | full |\n| **Password auth** | ✅ | ✅ |\n| **Cloud IAM (RDS/CloudSQL/Azure)** | ✅ via `BeforeConnect` | ❌ DSN-rewriting hack |\n| **TLS client certs** | ✅ via pgx DSN | ✅ |\n| **Cluster pg_stat_* collectors** | ✅ (24 total, see below) | ✅ (~22) |\n| **`pg_stat_io` (PG 16+)** | ✅ | ❌ |\n| **`pg_stat_slru` (PG 13+)** | ✅ | ❌ |\n| **`pg_stat_progress_*` (all 6)** | ✅ | partial (vacuum + analyze) |\n| **Declarative YAML collectors** | ✅ `ExtendFromYAMLFile` | ❌ (deprecated `queries.yaml`) |\n| **Per-collector enable/disable** | ✅ | ✅ |\n| **Dashboard-compat metric prefix** | ✅ `MetricPrefixPg` | N/A (native) |\n| **TLS listener + basic auth** | via `exporter-toolkit` | via `exporter-toolkit` |\n| **Graceful shutdown** | ✅ `Exporter.Shutdown(ctx)` | ✅ |\n| **Self-observability metrics** | ✅ scrape duration / errors / cardinality per-collector | partial |\n| **PG versions tested** | 13–18 | 9.4–18 |\n| **License** | Apache-2.0 | Apache-2.0 |\n| **Community Grafana dashboards** | reuse postgres_exporter's via `MetricPrefixPg` | native |\n| **Official Docker image** | not yet | Docker Hub |\n\nWhere `postgres_exporter` wins today: PG 9.4–13 long-tail support (we require PG 13+), the mature community dashboard ecosystem, and first-party container images. Coming tickets close the image/dashboards gap.\n\n¹ Head-to-head benchmark against postgres_exporter v0.19.1 — both run as subprocesses against identical fresh PG 17.6 instances with 50 tables × 100 rows seeded, 10-scrape warmup, `defaults vs defaults` collector sets, client-observed wall time. Full methodology and raw numbers: [BENCHMARKS.md](BENCHMARKS.md#head-to-head-vs-postgres_exporter).\n\n## Requirements\n\n- Go 1.25+ (pgx 5.9.x requires 1.25)\n- PostgreSQL 13, 14, 15, 16, 17, or 18\n\n## Quickstart\n\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"log\"\n\t\"net/http\"\n\n\t\"github.com/prometheus/client_golang/prometheus/promhttp\"\n\n\t\"github.com/becomeliminal/pgxporter/exporter\"\n\t\"github.com/becomeliminal/pgxporter/exporter/db\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\n\texp, err := exporter.New(ctx, exporter.Opts{\n\t\tDBOpts: []db.Opts{{\n\t\t\tHost:            \"localhost\",\n\t\t\tPort:            5432,\n\t\t\tUser:            \"postgres\",\n\t\t\tPassword:        \"postgres\",\n\t\t\tDatabase:        \"postgres\",\n\t\t\tApplicationName: \"pgxporter\",\n\t\t}},\n\t})\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\texp.Register()\n\n\thttp.Handle(\"/metrics\", promhttp.Handler())\n\tlog.Fatal(http.ListenAndServe(\":9187\", nil))\n}\n```\n\n```\n$ curl -s localhost:9187/metrics | head\n# HELP pg_stat_activity_backends Number of backends by state and wait event\n# TYPE pg_stat_activity_backends gauge\npg_stat_activity_backends{...}\n...\n```\n\n## Collectors\n\nAll default-on except `settings`, `statements`, and `subscription`. Disable one via `exporter.Opts.DisabledCollectors: []string{\"locks\"}`; whitelist an explicit set via `Opts.EnabledCollectors`.\n\n| Name | PostgreSQL view | Default |\n| --- | --- | --- |\n| `activity` | `pg_stat_activity` | ✅ |\n| `archiver` | `pg_stat_archiver` | ✅ |\n| `bgwriter` | `pg_stat_bgwriter` | ✅ |\n| `checkpointer` | `pg_stat_checkpointer` (PG 17+) | ✅ |\n| `database` | `pg_stat_database` | ✅ |\n| `database_size` | `pg_database_size()` | ✅ |\n| `io` | `pg_stat_io` (PG 16+) | ✅ |\n| `io_user_indexes` | `pg_statio_user_indexes` | ✅ |\n| `io_user_tables` | `pg_statio_user_tables` | ✅ |\n| `locks` | `pg_locks` (with blocking-chain detection) | ✅ |\n| `progress_analyze` | `pg_stat_progress_analyze` | ✅ |\n| `progress_basebackup` | `pg_stat_progress_basebackup` | ✅ |\n| `progress_cluster` | `pg_stat_progress_cluster` | ✅ |\n| `progress_copy` | `pg_stat_progress_copy` | ✅ |\n| `progress_create_index` | `pg_stat_progress_create_index` | ✅ |\n| `progress_vacuum` | `pg_stat_progress_vacuum` | ✅ |\n| `replication` | `pg_stat_replication` (primary) | ✅ |\n| `replication_slots` | `pg_replication_slots` | ✅ |\n| `settings` | `pg_settings` (numeric-typed subset) | ⬜ off |\n| `slru` | `pg_stat_slru` (PG 13+) | ✅ |\n| `ssl` | `pg_stat_ssl` (aggregate) | ✅ |\n| `statements` | `pg_stat_statements` | ⬜ off |\n| `subscription` | `pg_stat_subscription` (+ stats PG 15+) | ⬜ off |\n| `user_indexes` | `pg_stat_user_indexes` | ✅ |\n| `user_tables` | `pg_stat_user_tables` | ✅ |\n| `wal` | `pg_stat_wal` (PG 14+) | ✅ |\n| `wal_receiver` | `pg_stat_wal_receiver` (standby) | ✅ |\n\n`statements` is off by default because `pg_stat_statements` queries are expensive on busy clusters and the metric cardinality is high. Opt in explicitly.\n\n## Configuration\n\n`exporter.Opts` highlights (see [`pkg.go.dev`](https://pkg.go.dev/github.com/becomeliminal/pgxporter/exporter) for the full reference):\n\n| Field | Purpose |\n| --- | --- |\n| `DBOpts` | One `db.Opts` per PostgreSQL server (required, ≥1) |\n| `Name` | Const label `database=\u003cName\u003e` on self-metrics when multiple exporters register in one Prometheus instance |\n| `CollectionTimeout` | Per-scrape deadline. `0` → 1 min default; `-1` → no timeout |\n| `MetricPrefix` | `collectors.MetricPrefixPg` for postgres_exporter-compat names |\n| `EnabledCollectors` | Whitelist (empty = use defaults) |\n| `DisabledCollectors` | Subtracts from the resolved set (wins if in both) |\n\n`db.Opts` covers connection basics (`Host`/`Port`/`User`/`Password`/`Database`/`ApplicationName`), statement/lock/transaction timeouts, pgxpool tuning (`PoolMaxConns`/`PoolMaxConnLifetime`/etc.), pgx statement-cache mode, and the optional `AuthProvider` hook. ~20 fields total; see [`pkg.go.dev/.../exporter/db`](https://pkg.go.dev/github.com/becomeliminal/pgxporter/exporter/db).\n\n## Custom collectors\n\n### YAML (the `queries.yaml` replacement)\n\n```yaml\n- subsystem: custom_rates\n  sql: |\n    SELECT current_database() AS database, key, count(*) AS c\n    FROM business.rate_events\n    GROUP BY key\n  labels: [key]\n  metrics:\n    - name: events_total\n      help: Events by rate key\n      type: counter\n      column: c\n\n- subsystem: bloat\n  min_pg_version: \"14.0\"\n  sql: SELECT current_database() AS database, schemaname, bytes FROM monitoring.bloat\n  labels: [schemaname]\n  metrics:\n    - name: bytes\n      help: Table bloat in bytes\n      column: bytes\n```\n\nLoad:\n\n```go\nif err := exp.ExtendFromYAMLFile(\"/etc/pgxporter/custom.yaml\"); err != nil {\n\tlog.Fatal(err)\n}\n```\n\n`min_pg_version` accepts `\"14.0\"`, `[14, 0]`, or bare `\"14\"`. Omit `type` to emit a gauge. Every SQL must project a `database` label column so metrics are correctly labelled across multi-DB exporters.\n\n### Go interface\n\nFor logic that outgrows YAML — custom type conversions, cross-row aggregation, conditional emission — implement the Go interface directly:\n\n```go\ntype Collector interface {\n\tDescribe(ch chan\u003c- *prometheus.Desc)\n\tScrape(ctx context.Context, ch chan\u003c- prometheus.Metric) error\n}\n```\n\nRegister:\n\n```go\nexp := exporter.MustNew(ctx, opts).WithCustomCollectors(myCollector{}, anotherCollector{})\n```\n\nImplementations **must** thread `ctx` into every DB call — a pathological query that doesn't honour cancellation can hang the whole scrape cycle.\n\n## Cloud IAM auth (RDS / CloudSQL / Azure)\n\nSet `db.Opts.AuthProvider` and pgx will mint a fresh token for every new pool connection via its `BeforeConnect` hook. Tokens are rotated implicitly whenever pgxpool opens a new connection, so set `PoolMaxConnLifetime` shorter than the token's validity window.\n\nAWS RDS example:\n\n```go\nimport (\n\t\"time\"\n\n\t\"github.com/becomeliminal/pgxporter/exporter/db\"\n\t\"github.com/becomeliminal/pgxporter/exporter/db/auth/awsrds\"\n)\n\nprovider, err := awsrds.NewDefault(ctx, \"us-east-1\")\nif err != nil {\n\tlog.Fatal(err)\n}\n\ndbOpts := db.Opts{\n\tHost:                \"mydb.cluster-xyz.us-east-1.rds.amazonaws.com\",\n\tPort:                5432,\n\tUser:                \"app_iam_user\",\n\tDatabase:            \"appdb\",\n\tApplicationName:     \"pgxporter\",\n\tAuthProvider:        provider,\n\tPoolMaxConnLifetime: 14 * time.Minute, // RDS IAM tokens are valid 15m; rotate at 14m\n}\n```\n\n`awsrds.NewDefault` chains the standard AWS credential sources (env vars → shared config → IRSA → IMDS). Token minting is pure local SigV4 signing — no network round-trip per connection.\n\nGCP CloudSQL and Azure Database providers follow the same pattern (implement `db.AuthProvider`):\n\n```go\ntype AuthProvider interface {\n\tPassword(ctx context.Context, host string, port int, user string) (string, error)\n}\n```\n\n## postgres_exporter dashboard compatibility\n\n`postgres_exporter` community Grafana dashboards assume names like `pg_database_xact_commit_total`. pgxporter defaults to `pg_stat_database_xact_commit_total`. Flip with `Opts.MetricPrefix`:\n\n```go\nimport \"github.com/becomeliminal/pgxporter/exporter/collectors\"\n\nexp, err := exporter.New(ctx, exporter.Opts{\n\tMetricPrefix: collectors.MetricPrefixPg,\n\tDBOpts:       []db.Opts{{...}},\n})\n```\n\nOr set it before constructing any exporter (applies library-wide):\n\n```go\ncollectors.SetMetricPrefix(collectors.MetricPrefixPg)\n```\n\nWith this flag the community postgres_exporter dashboard set works unmodified.\n\nFor a full switch-over guide — flag mapping, env-var mapping, `queries.yaml` → `CollectorSpec`, and the recommended parallel-run pattern — see [docs/migrating-from-postgres_exporter.md](docs/migrating-from-postgres_exporter.md).\n\n## Serving metrics over TLS / with basic auth\n\npgxporter is a Prometheus `Collector` implementation — it stays framework-agnostic and doesn't bundle an HTTP server, so use any `net/http` listener you already run. For production setups matching `postgres_exporter`'s ergonomics, pair with [`prometheus/exporter-toolkit`](https://github.com/prometheus/exporter-toolkit), which provides a `--web.config.file` flag covering TLS, basic auth, and HTTP/2 out of the box:\n\n```go\nimport (\n\t\"context\"\n\t\"net/http\"\n\n\t\"github.com/prometheus/client_golang/prometheus/promhttp\"\n\t\"github.com/prometheus/exporter-toolkit/web\"\n\twebflag \"github.com/prometheus/exporter-toolkit/web/kingpinflag\"\n\n\t\"github.com/becomeliminal/pgxporter/exporter\"\n\t\"github.com/becomeliminal/pgxporter/exporter/db\"\n)\n\nfunc main() {\n\twebConfig := webflag.AddFlags(kingpin.CommandLine, \":9187\")\n\tkingpin.Parse()\n\n\texp := exporter.MustNew(context.Background(), exporter.Opts{DBOpts: []db.Opts{opts.DB}})\n\texp.Register()\n\n\thttp.Handle(\"/metrics\", promhttp.Handler())\n\tsrv := \u0026http.Server{}\n\tif err := web.ListenAndServe(srv, webConfig, slog.Default()); err != nil {\n\t\tlog.Fatal(err)\n\t}\n}\n```\n\n`--web.config.file` YAML (per [exporter-toolkit docs](https://github.com/prometheus/exporter-toolkit/blob/master/docs/web-configuration.md)):\n\n```yaml\ntls_server_config:\n  cert_file: /etc/pgxporter/tls.crt\n  key_file: /etc/pgxporter/tls.key\nbasic_auth_users:\n  prometheus: $2b$12$...  # bcrypt\n```\n\n## Graceful shutdown\n\n```go\nctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGTERM, syscall.SIGINT)\ndefer cancel()\n\n// ... start exporter + HTTP listener ...\n\n\u003c-ctx.Done()\n\nshutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 30*time.Second)\ndefer shutdownCancel()\n\nif err := srv.Shutdown(shutdownCtx); err != nil {\n\tlog.Printf(\"http shutdown: %v\", err)\n}\nif err := exp.Shutdown(shutdownCtx); err != nil {\n\tlog.Printf(\"exporter shutdown: %v\", err)\n}\n```\n\n`Exporter.Shutdown` takes the scrape mutex so in-flight `Collect` calls finish before pools close. The per-scrape deadline from `Opts.CollectionTimeout` bounds the wait.\n\n## Example deployments\n\n### Single target\n\n```go\nimport (\n\t\"github.com/becomeliminal/pgxporter/exporter\"\n\t\"github.com/becomeliminal/pgxporter/exporter/db\"\n)\n\nvar opts struct {\n\tDB db.Opts `group:\"Postgres\"`\n}\n\nfunc main() {\n\tflags.MustParse(\u0026opts)\n\texp := exporter.MustNew(context.Background(), exporter.Opts{DBOpts: []db.Opts{opts.DB}})\n\texp.Register()\n\t// ...\n}\n```\n\nKubernetes:\n\n```yaml\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  # ...\nspec:\n  # ...\n  template:\n    spec:\n      containers:\n        - name: main\n          image: \u003cGo-Binary-Docker-Image\u003e\n          args:\n            - --application_name=pgxporter\n          resources:\n            requests:\n              memory: 20Mi\n              cpu: 5m\n            limits:\n              memory: 40Mi\n              cpu: 50m\n          ports:\n            - containerPort: 9187\n              name: prometheus\n          envFrom:\n            - secretRef:\n                name: your-db-secret\n```\n\n### Multi-target\n\nScrape N databases from one exporter process — the pattern used in production at Liminal across 18 Postgres instances.\n\n```go\nimport (\n\t\"github.com/becomeliminal/pgxporter/exporter\"\n\t\"github.com/becomeliminal/pgxporter/exporter/db\"\n)\n\nvar opts struct {\n\tFirstDB  db.Opts `namespace:\"first\" env-namespace:\"FIRST\"`\n\tSecondDB db.Opts `namespace:\"second\" env-namespace:\"SECOND\"`\n}\n\nfunc main() {\n\tflags.MustParse(\u0026opts)\n\texp := exporter.MustNew(context.Background(), exporter.Opts{\n\t\tDBOpts: []db.Opts{opts.FirstDB, opts.SecondDB},\n\t})\n\texp.Register()\n\t// ...\n}\n```\n\nKubernetes:\n\n```yaml\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  # ...\nspec:\n  # ...\n  template:\n    spec:\n      containers:\n        - name: main\n          image: \u003cGo-Binary-Docker-Image\u003e\n          args:\n            - --first.application_name=pgxporter\n            - --second.application_name=pgxporter\n          ports:\n            - containerPort: 9187\n              name: prometheus\n          envFrom:\n            - secretRef:\n                name: your-first-db-secret\n              prefix: FIRST_\n            - secretRef:\n                name: your-second-db-secret\n              prefix: SECOND_\n```\n\n## Exporter self-metrics\n\nEmitted at the default `pg_stat` prefix (flip with `Opts.MetricPrefix`):\n\n- `pg_stat_up` (gauge) — `1` if the last scrape succeeded, `0` on error.\n- `pg_stat_exporter_scrapes_total` (counter) — cumulative scrape count.\n- `pg_stat_scrape_duration_seconds{collector}` (histogram) — per-collector wall time.\n- `pg_stat_scrape_errors_total{collector}` (counter) — per-collector error count. Pre-initialised to 0 for every resolved collector so `rate()` returns a clean zero on healthy instances.\n- `pg_stat_metric_cardinality{collector}` (gauge) — metric count emitted by a collector on its last scrape. Early-warning signal for cardinality explosions.\n\nAll self-metrics carry a `database=\u003cOpts.Name\u003e` const label when `Opts.Name` is set.\n\n## License\n\nApache-2.0 — see [LICENSE](LICENSE) and [NOTICE](NOTICE). Relicensed from AGPL-3.0 in April 2026 to align with the Prometheus exporter ecosystem (`postgres_exporter`, `client_golang`, `exporter-toolkit` are all Apache-2.0) and unblock corporate adoption blocked by AGPL §13 network-distribution obligations.\n\n## Dashboards\n\nSee [dashboards/](dashboards/) for `pgxporter-health.json` (exporter self-metrics) plus the compatibility guide for reusing community postgres_exporter dashboards via `MetricPrefixPg`.\n\n## Benchmarks\n\nSee [BENCHMARKS.md](BENCHMARKS.md) for head-to-head scrape-duration numbers.\n\n## Changelog\n\nSee [CHANGELOG.md](CHANGELOG.md).\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbecomeliminal%2Fpgxporter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbecomeliminal%2Fpgxporter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbecomeliminal%2Fpgxporter/lists"}