1
0
mirror of https://github.com/prometheus-community/postgres_exporter.git synced 2026-10-23 12:08:57 +03:00

main: adopt exporter-toolkit bootstrap (#1428)

This commit is contained in:
Arthur Silva Sens
2026-10-22 05:05:32 -03:00
committed by GitHub
parent 322d6e60ec
commit 02d6977a2f
4 changed files with 112 additions and 67 deletions

View File

@@ -32,8 +32,11 @@ the `-file` flags at a mounted secret.
* [CHANGE] Wrap non-negative 64-bit counters at `2^53` to preserve single-unit precision by @gnanirahulnutakki in https://github.com/prometheus-community/postgres_exporter/pull/1351
* [CHANGE] stat_replication: add `pid` label to disambiguate replication connections that otherwise share identical labels by @sysadmind in https://github.com/prometheus-community/postgres_exporter/pull/1353
* [CHANGE] stat_activity_autovacuum: add `datname` and `schemaname` labels to disambiguate autovacuum workers that otherwise share a `relname` by @spicysharkco in https://github.com/prometheus-community/postgres_exporter/pull/1419
* [CHANGE] Deprecate `PG_EXPORTER_WEB_TELEMETRY_PATH` for removal in v0.22.0; use `--web.telemetry-path` instead, by @ArthurSens in https://github.com/prometheus-community/postgres_exporter/pull/1428. Until removal, the environment variable continues to set the metrics path when the flag is unset and logs a startup warning. Other `PG_EXPORTER_*` variables retain their existing behavior.
* [BUGFIX] Enforce concurrent scrape limits through `--web.max-requests` (default `40`), independently for the metrics and `/probe` endpoints, by @ArthurSens in https://github.com/prometheus-community/postgres_exporter/pull/1428. Previously, both endpoints allowed unlimited concurrent scrapes. After upgrading, requests exceeding the limit receive HTTP 503; multi-target deployments share one limit across all `/probe` targets. Set `--web.max-requests=0` to preserve the previous unlimited behavior.
* [BUGFIX] Fix `long_running_transactions` to count only transactions older than a configurable threshold, by @ArthurSens in https://github.com/prometheus-community/postgres_exporter/pull/1379, based on the contribution by @moreinhardt in https://github.com/prometheus-community/postgres_exporter/pull/1210
* [BUGFIX] Fix the `wal` collector failing on every scrape on Amazon Aurora PostgreSQL versions that don't support `pg_ls_waldir()` by @megative in https://github.com/prometheus-community/postgres_exporter/pull/1312
* [FEATURE] Add `--web.disable-exporter-metrics` to exclude Go, process, and `postgres_exporter_build_info` metrics while retaining PostgreSQL scrape-status and configuration-reload metrics, by @ArthurSens in https://github.com/prometheus-community/postgres_exporter/pull/1428, based on @nicolastakashi's work in https://github.com/prometheus-community/postgres_exporter/pull/1368.
* [FEATURE] Export `pg_settings.pending_restart` as `pg_settings_pending_restart{name}` from the `settings` collector by @MarcWort in https://github.com/prometheus-community/postgres_exporter/pull/1396
* [FEATURE] Add `aurora_replica_status` collector for Amazon Aurora PostgreSQL by @megative in https://github.com/prometheus-community/postgres_exporter/pull/1311
* [FEATURE] Add `aurora_global_db_status` collector for Amazon Aurora PostgreSQL by @megative in https://github.com/prometheus-community/postgres_exporter/pull/1410

View File

@@ -218,6 +218,12 @@ This will build the docker image as `prometheuscommunity/postgres_exporter:${bra
* `web.telemetry-path`
Path under which to expose metrics. Default is `/metrics`.
* `web.max-requests`
Maximum number of parallel scrape requests per endpoint (`/metrics` and `/probe` have independent limits). Excess requests receive HTTP 503. Use `0` to disable. Default is `40`.
* `web.disable-exporter-metrics`
Exclude Go, process, and `postgres_exporter_build_info` metrics. PostgreSQL scrape-status and configuration-reload metrics remain available. Default is `false`.
* `disable-default-metrics`
Use only metrics supplied from `queries.yaml` via `--extend.query-path`. Default is `false`.
@@ -313,8 +319,8 @@ warning at startup when one of them is set.
and will avoid exhausting the pool of connections of the database.
Value of `0` or less than `1ms` is considered invalid and will report an error.
* `PG_EXPORTER_WEB_TELEMETRY_PATH`
Path under which to expose metrics. Default is `/metrics`.
* `PG_EXPORTER_WEB_TELEMETRY_PATH` (DEPRECATED)
Path under which to expose metrics when `--web.telemetry-path` is unset. Default is `/metrics`. This environment variable will be removed in postgres_exporter v0.22.0. Use `--web.telemetry-path` instead; an explicit flag takes precedence.
* `PG_EXPORTER_DISABLE_DEFAULT_METRICS`
Use only metrics supplied from `queries.yaml`. Value can be `true` or `false`. Default is `false`.

View File

@@ -14,6 +14,7 @@
package main
import (
"errors"
"fmt"
"net/http"
_ "net/http/pprof"
@@ -34,16 +35,12 @@ import (
versioncollector "github.com/prometheus/client_golang/prometheus/collectors/version"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/prometheus/common/promslog"
"github.com/prometheus/common/promslog/flag"
"github.com/prometheus/common/version"
"github.com/prometheus/exporter-toolkit/bootstrap"
"github.com/prometheus/exporter-toolkit/web"
"github.com/prometheus/exporter-toolkit/web/kingpinflag"
)
var (
configFile = kingpin.Flag("config.file", "Postgres exporter configuration file.").Default("postgres_exporter.yml").String()
webConfig = kingpinflag.AddFlags(kingpin.CommandLine, ":9187")
metricsPath = kingpin.Flag("web.telemetry-path", "Path under which to expose metrics.").Default("/metrics").Envar("PG_EXPORTER_WEB_TELEMETRY_PATH").String()
disableDefaultMetrics = kingpin.Flag("disable-default-metrics", "Do not include default metrics.").Default("false").Envar("PG_EXPORTER_DISABLE_DEFAULT_METRICS").Bool()
autoDiscoverDatabases = kingpin.Flag("auto-discover-databases", "Whether to discover the databases on a server dynamically. (DEPRECATED)").Default("false").Envar("PG_EXPORTER_AUTO_DISCOVER_DATABASES").Bool()
autoDiscoverDatabasesMaxConcurrency = kingpin.Flag("auto-discover-databases.max-concurrency", "Maximum number of discovered databases to scrape concurrently in a single scrape.").Default(fmt.Sprintf("%d", config.DefaultAutoDiscoverDatabasesMaxConcurrency)).Int()
@@ -73,6 +70,15 @@ var (
// The name of the exporter.
const exporterName = "postgres_exporter"
// webTelemetryPathEnvar is deprecated and will be removed in v0.22.0.
// Use --web.telemetry-path instead.
const webTelemetryPathEnvar = "PG_EXPORTER_WEB_TELEMETRY_PATH"
// Bootstrap parses flags and builds the handler before starting the listener.
// Returning errDumpMaps from the handler factory stops startup before database
// initialization, so main can dump the maps and exit successfully.
var errDumpMaps = errors.New("dumpmaps requested")
type collectorFlagSet map[string]*bool
type pgStatStatementsFlags struct {
@@ -131,23 +137,67 @@ func newPGStatStatementsFlags() pgStatStatementsFlags {
}
func main() {
kingpin.Version(version.Print(exporterName))
promslogConfig := &promslog.Config{}
flag.AddFlags(kingpin.CommandLine, promslogConfig)
kingpin.HelpFlag.Short('h')
kingpin.Parse()
logger = promslog.New(promslogConfig)
server := &exporterServer{}
runner := bootstrap.New(bootstrap.Config{
App: kingpin.CommandLine,
Name: exporterName,
Description: "Prometheus PostgreSQL server Exporter",
DefaultAddress: ":9187",
LandingConfig: web.LandingConfig{
Name: "Postgres Exporter",
},
MetricsHandlerFactory: server.newMetricsHandler,
})
// This flag previously belonged to postgres_exporter and read this env var.
// Bootstrap registers it without an env binding. Restore that binding until
// v0.22.0, preserving explicit CLI flags' precedence.
kingpin.CommandLine.GetFlag("web.telemetry-path").Envar(webTelemetryPathEnvar)
if *onlyDumpMaps {
err := runner.Run()
server.close()
if errors.Is(err, errDumpMaps) {
exporter.DumpMaps()
return
}
if err != nil {
if runner.Logger != nil {
runner.Logger.Error("Error running exporter", "err", err)
} else {
fmt.Fprintln(os.Stderr, err)
}
os.Exit(1)
}
}
type exporterServer struct {
runtime *collector.Runtime
stopReload func()
}
func (s *exporterServer) close() {
if s.stopReload != nil {
s.stopReload()
}
if s.runtime != nil {
if err := s.runtime.Close(); err != nil {
logger.Error("Failed to close runtime", "err", err)
}
}
}
// newMetricsHandler wires up the exporter and registers postgres_exporter's
// endpoints once the toolkit has parsed flags.
func (s *exporterServer) newMetricsHandler(b *bootstrap.Bootstrap) (http.Handler, error) {
logger = b.Logger
if *onlyDumpMaps {
return nil, errDumpMaps
}
registry := prometheus.NewRegistry()
authHandler, err := config.NewHandler(registry)
if err != nil {
logger.Error("Failed to create config handler", "err", err)
os.Exit(1)
return nil, fmt.Errorf("failed to create config handler: %w", err)
}
if err := authHandler.ReloadAuthConfig(*configFile, logger); err != nil {
@@ -157,6 +207,10 @@ func main() {
warnDeprecatedDataSourceEnvVars()
if os.Getenv(webTelemetryPathEnvar) != "" {
logger.Warn(webTelemetryPathEnvar + " is DEPRECATED, use --web.telemetry-path instead")
}
dsns, err := getDataSources(dataSourceOpts{
URI: *datasourceURI,
URIFile: *datasourceURIFile,
@@ -164,14 +218,12 @@ func main() {
PassFile: *datasourcePassFile,
})
if err != nil {
logger.Error("Failed reading data sources", "err", err.Error())
os.Exit(1)
return nil, fmt.Errorf("failed reading data sources: %w", err)
}
cfg, err := buildConfig(dsns)
if err != nil {
logger.Error("Failed building config", "err", err)
os.Exit(1)
return nil, fmt.Errorf("failed building config: %w", err)
}
logger.Info("Excluded databases", "databases", fmt.Sprintf("%v", cfg.ExcludeDatabases))
@@ -189,65 +241,47 @@ func main() {
validatedConfig, err := cfg.Validate()
if err != nil {
logger.Error("Invalid config", "err", err)
os.Exit(1)
return nil, fmt.Errorf("invalid config: %w", err)
}
pgRuntime, err := collector.NewRuntime(validatedConfig, logger)
runtime, err := collector.NewRuntime(validatedConfig, logger)
if err != nil {
logger.Error("Failed to create runtime", "err", err)
os.Exit(1)
return nil, fmt.Errorf("failed to create runtime: %w", err)
}
defer func() {
if err := pgRuntime.Close(); err != nil {
logger.Error("Failed to close runtime", "err", err)
}
}()
s.runtime = runtime
registry.MustRegister(
prometheuscollectors.NewGoCollector(),
prometheuscollectors.NewProcessCollector(prometheuscollectors.ProcessCollectorOpts{}),
versioncollector.NewCollector(exporterName),
)
for _, collector := range pgRuntime.Collectors() {
if !b.DisableExporterMetrics {
registry.MustRegister(
versioncollector.NewCollector(exporterName),
prometheuscollectors.NewGoCollector(),
prometheuscollectors.NewProcessCollector(prometheuscollectors.ProcessCollectorOpts{}),
)
}
for _, collector := range runtime.Collectors() {
registry.MustRegister(collector)
}
http.Handle(*metricsPath, promhttp.HandlerFor(registry, promhttp.HandlerOpts{}))
b.Handle("/probe", b.MaxRequestsHandler(handleProbe(logger, authHandler, cfg)))
b.Handle("/debug/", http.DefaultServeMux)
if *metricsPath != "/" && *metricsPath != "" {
landingConfig := web.LandingConfig{
Name: "Postgres Exporter",
Description: "Prometheus PostgreSQL server Exporter",
Version: version.Info(),
Links: []web.LandingLinks{
{
Address: *metricsPath,
Text: "Metrics",
},
},
}
landingPage, err := web.NewLandingPage(landingConfig)
if err != nil {
logger.Error("error creating landing page", "err", err)
os.Exit(1)
}
http.Handle("/", landingPage)
}
metricsHandler := promhttp.HandlerFor(registry, promhttp.HandlerOpts{})
http.HandleFunc("/probe", handleProbe(logger, authHandler, cfg))
http.HandleFunc("/-/reload", handleReload(authHandler, *configFile))
b.HandleFunc("/-/reload", handleReload(authHandler, *configFile))
reloadSignals := make(chan os.Signal, 1)
reloadDone := make(chan struct{})
signal.Notify(reloadSignals, syscall.SIGHUP)
defer signal.Stop(reloadSignals)
go handleReloadSignal(reloadSignals, authHandler, *configFile, logger)
srv := &http.Server{}
if err := web.ListenAndServe(srv, webConfig, logger); err != nil {
logger.Error("Error running HTTP server", "err", err)
os.Exit(1)
go func() {
defer close(reloadDone)
handleReloadSignal(reloadSignals, authHandler, *configFile, logger)
}()
s.stopReload = func() {
signal.Stop(reloadSignals)
close(reloadSignals)
<-reloadDone
}
return metricsHandler, nil
}
func buildConfig(dsns []string) (config.Config, error) {

View File

@@ -104,7 +104,6 @@ Flags specific to this exporter:
| Flag | Purpose |
|---|---|
| `--web.telemetry-path` | Metrics path. Default `/metrics`. |
| `--config.file` | Path to the [config file](#config-file). Default `postgres_exporter.yml`. |
| `--collection-timeout` | Per-scrape timeout. Default `1m`. See [Connecting](connecting.md#connection-timeout). |
| `--wrap-large-counters` | Wrap 64-bit counters at 2^53 to avoid floating point rounding. Default `true`. |
@@ -115,6 +114,9 @@ The exporter's HTTP server is provided by [exporter-toolkit](https://github.com/
| Flag | Purpose |
|---|---|
| `--web.telemetry-path` | Metrics path. Default `/metrics`. |
| `--web.max-requests` | Maximum concurrent scrapes per endpoint, independently for the metrics path and `/probe`. Default `40`; excess requests receive HTTP 503. Set `0` for no limit. Reload and debug endpoints are not limited. |
| `--web.disable-exporter-metrics` | Exclude Go, process, and `postgres_exporter_build_info` metrics. PostgreSQL scrape-status and config-reload metrics remain available. Default `false`. |
| `--web.listen-address` | Address to serve on. Default `:9187`. Repeatable, to listen on several addresses (e.g. `--web.listen-address=:9187 --web.listen-address=[::1]:9187`). |
| `--web.config.file` | Enables TLS and/or basic auth on the exporter's own HTTP server. File format: [web-configuration.md](https://github.com/prometheus/exporter-toolkit/blob/master/docs/web-configuration.md). See also [Secrets](secrets.md#exporter-http-endpoint). |
| `--web.systemd-socket` | Use systemd socket activation instead of binding a port. Linux only — the flag doesn't exist on other platforms. |
@@ -126,7 +128,7 @@ Logging flags come from the shared [`prometheus/common`](https://github.com/prom
| `--log.level` | Logging verbosity: `debug`, `info` (default), `warn`, or `error`. |
| `--log.format` | Log output format: `logfmt` (default) or `json`. |
Most flags also have an environment-variable equivalent (e.g. `PG_EXPORTER_WEB_TELEMETRY_PATH`, `PG_EXPORTER_COLLECTION_TIMEOUT`); a CLI flag, if set, always wins over the corresponding environment variable. The full list is in the [main README](../README.md#flags).
`PG_EXPORTER_WEB_TELEMETRY_PATH` is deprecated; use `--web.telemetry-path` instead. It continues to work during deprecation and will be removed in postgres_exporter v0.22.0. Many flags also have an environment-variable equivalent (e.g. `PG_EXPORTER_COLLECTION_TIMEOUT`); a CLI flag, if set, always wins over the corresponding environment variable. The full list is in the [main README](../README.md#flags).
## Config file