Path: blob/main/pkg/integrations/postgres_exporter/postgres_exporter.go
5302 views
// Package postgres_exporter embeds https://github.com/prometheus/postgres_exporter1package postgres_exporter //nolint:golint23import (4"fmt"5"os"6"strings"78config_util "github.com/prometheus/common/config"910"github.com/go-kit/log"11"github.com/grafana/agent/pkg/integrations"12integrations_v2 "github.com/grafana/agent/pkg/integrations/v2"13"github.com/grafana/agent/pkg/integrations/v2/metricsutils"14"github.com/lib/pq"15"github.com/prometheus-community/postgres_exporter/exporter"16)1718// Config controls the postgres_exporter integration.19type Config struct {20// DataSourceNames to use to connect to Postgres.21DataSourceNames []config_util.Secret `yaml:"data_source_names,omitempty"`2223DisableSettingsMetrics bool `yaml:"disable_settings_metrics,omitempty"`24AutodiscoverDatabases bool `yaml:"autodiscover_databases,omitempty"`25ExcludeDatabases []string `yaml:"exclude_databases,omitempty"`26IncludeDatabases []string `yaml:"include_databases,omitempty"`27DisableDefaultMetrics bool `yaml:"disable_default_metrics,omitempty"`28QueryPath string `yaml:"query_path,omitempty"`29}3031// Name returns the name of the integration this config is for.32func (c *Config) Name() string {33return "postgres_exporter"34}3536// NewIntegration converts this config into an instance of a configuration.37func (c *Config) NewIntegration(l log.Logger) (integrations.Integration, error) {38return New(l, c)39}4041// InstanceKey returns a simplified DSN of the first postgresql DSN, or an error if42// not exactly one DSN is provided.43func (c *Config) InstanceKey(_ string) (string, error) {44dsn, err := c.getDataSourceNames()45if err != nil {46return "", err47}48if len(dsn) != 1 {49return "", fmt.Errorf("can't automatically determine a value for `instance` with %d DSN. either use 1 DSN or manually assign a value for `instance` in the integration config", len(dsn))50}5152s, err := parsePostgresURL(dsn[0])53if err != nil {54return "", fmt.Errorf("cannot parse DSN: %w", err)55}5657// Assign default values to s.58//59// PostgreSQL hostspecs can contain multiple host pairs. We'll assign a host60// and port by default, but otherwise just use the hostname.61if _, ok := s["host"]; !ok {62s["host"] = "localhost"63s["port"] = "5432"64}6566hostport := s["host"]67if p, ok := s["port"]; ok {68hostport += fmt.Sprintf(":%s", p)69}70return fmt.Sprintf("postgresql://%s/%s", hostport, s["dbname"]), nil71}7273func parsePostgresURL(url string) (map[string]string, error) {74raw, err := pq.ParseURL(url)75if err != nil {76return nil, err77}7879res := map[string]string{}8081unescaper := strings.NewReplacer(`\'`, `'`, `\\`, `\`)8283for _, keypair := range strings.Split(raw, " ") {84parts := strings.SplitN(keypair, "=", 2)85if len(parts) != 2 {86panic(fmt.Sprintf("unexpected keypair %s from pq", keypair))87}8889key := parts[0]90value := parts[1]9192// Undo all the transformations ParseURL did: remove wrapping93// quotes and then unescape the escaped characters.94value = strings.TrimPrefix(value, "'")95value = strings.TrimSuffix(value, "'")96value = unescaper.Replace(value)9798res[key] = value99}100101return res, nil102}103104// getDataSourceNames loads data source names from the config or from the105// environment, if set.106func (c *Config) getDataSourceNames() ([]string, error) {107dsn := c.DataSourceNames108var stringDsn []string109if len(dsn) == 0 {110envDsn, present := os.LookupEnv("POSTGRES_EXPORTER_DATA_SOURCE_NAME")111if !present {112return nil, fmt.Errorf("cannot create postgres_exporter; neither postgres_exporter.data_source_name or $POSTGRES_EXPORTER_DATA_SOURCE_NAME is set")113}114stringDsn = append(stringDsn, strings.Split(envDsn, ",")...)115} else {116for _, d := range dsn {117stringDsn = append(stringDsn, string(d))118}119}120return stringDsn, nil121}122123func init() {124integrations.RegisterIntegration(&Config{})125integrations_v2.RegisterLegacy(&Config{}, integrations_v2.TypeMultiplex, metricsutils.NewNamedShim("postgres"))126}127128// New creates a new postgres_exporter integration. The integration scrapes129// metrics from a postgres process.130func New(log log.Logger, c *Config) (integrations.Integration, error) {131dsn, err := c.getDataSourceNames()132if err != nil {133return nil, err134}135136e := exporter.NewExporter(137dsn,138log,139exporter.DisableDefaultMetrics(c.DisableDefaultMetrics),140exporter.WithUserQueriesPath(c.QueryPath),141exporter.DisableSettingsMetrics(c.DisableSettingsMetrics),142exporter.AutoDiscoverDatabases(c.AutodiscoverDatabases),143exporter.ExcludeDatabases(strings.Join(c.ExcludeDatabases, ",")),144exporter.IncludeDatabases(strings.Join(c.IncludeDatabases, ",")),145exporter.MetricPrefix("pg"),146)147148return integrations.NewCollectorIntegration(c.Name(), integrations.WithCollectors(e)), nil149}150151152