sql_exporter

package module
v0.0.0-...-f7adb9a Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 22, 2024 License: MIT Imports: 25 Imported by: 1

README

SQL Exporter for Prometheus

Go Go Report Card Docker Pulls Downloads Artifact HUB

Overview

SQL Exporter is a configuration driven exporter that exposes metrics gathered from DBMSs, for use by the Prometheus monitoring system. Out of the box, it provides support for the following databases and compatible interfaces:

  • MySQL
  • PostgreSQL
  • Microsoft SQL Server
  • Clickhouse
  • Snowflake
  • Vertica

In fact, any DBMS for which a Go driver is available may be monitored after rebuilding the binary with the DBMS driver included.

The collected metrics and the queries that produce them are entirely configuration defined. SQL queries are grouped into collectors -- logical groups of queries, e.g. query stats or I/O stats, mapped to the metrics they populate. Collectors may be DBMS-specific (e.g. MySQL InnoDB stats) or custom, deployment specific (e.g. pricing data freshness). This means you can quickly and easily set up custom collectors to measure data quality, whatever that might mean in your specific case.

Per the Prometheus philosophy, scrapes are synchronous (metrics are collected on every /metrics poll) but, in order to keep load at reasonable levels, minimum collection intervals may optionally be set per collector, producing cached metrics when queried more frequently than the configured interval.

Usage

Get Prometheus SQL Exporter, either as a packaged release, as a Docker image.

Use the -help flag to get help information.

$ ./sql_exporter -help
Usage of ./sql_exporter:
  -config.file string
      SQL Exporter configuration file name. (default "sql_exporter.yml")
  -web.listen-address string
      Address to listen on for web interface and telemetry. (default ":9399")
  -web.metrics-path string
      Path under which to expose metrics. (default "/metrics")
  [...]

Build

Prerequisites:

  • Go Compiler
  • GNU Make

By default we produce a binary with all the supported drivers with the following command:

make build

It's also possible to reduce the size of the binary by only including specific set of drivers like Postgres, MySQL and MSSQL. In this case we need to update drivers.go. To avoid manual manipulation there is a helper code generator available, so we can run the following commands:

make drivers-minimal
make build

The first command will regenerate drivers.go file with a minimal set of imported drivers using drivers_gen.go.

Running make drivers-all will regenerate driver set back to the current defaults.

Feel free to revisit and add more drivers as required. There's also the custom list that allows managing a separate list of drivers for special needs.

Run as a Windows service

If you run SQL Exporter from Windows, it might come in handy to register it as a service to avoid interactive sessions. It is important to define --config.file parameter to load the configuration file. The other settings can be added as well. The registration itself is performed with Powershell or CMD (make sure you run it as Administrator):

Powershell:

New-Service -name "SqlExporterSvc" `
-BinaryPathName "%SQL_EXPORTER_PATH%\sql_exporter.exe --config.file %SQL_EXPORTER_PATH%\sql_exporter.yml" `
-StartupType Automatic `
-DisplayName "Prometheus SQL Exporter"

CMD:

sc.exe create SqlExporterSvc binPath= "%SQL_EXPORTER_PATH%\sql_exporter.exe --config.file %SQL_EXPORTER_PATH%\sql_exporter.yml" start= auto

%SQL_EXPORTER_PATH% is a path to the SQL Exporter binary executable. This document assumes that configuration files are in the same location.

Configuration

SQL Exporter is deployed alongside the DB server it collects metrics from. If both the exporter and the DB server are on the same host, they will share the same failure domain: they will usually be either both up and running or both down. When the database is unreachable, /metrics responds with HTTP code 500 Internal Server Error, causing Prometheus to record up=0 for that scrape. Only metrics defined by collectors are exported on the /metrics endpoint. SQL Exporter process metrics are exported at /sql_exporter_metrics.

The configuration examples listed here only cover the core elements. For a comprehensive and comprehensively documented configuration file check out documentation/sql_exporter.yml. You will find ready to use "standard" DBMS-specific collector definitions in the examples directory. You may contribute your own collector definitions and metric additions if you think they could be more widely useful, even if they are merely different takes on already covered DBMSs.

./sql_exporter.yml

# Global settings and defaults.
global:
  # Subtracted from Prometheus' scrape_timeout to give us some headroom and prevent Prometheus from
  # timing out first.
  scrape_timeout_offset: 500ms
  # Minimum interval between collector runs: by default (0s) collectors are executed on every scrape.
  min_interval: 0s
  # Maximum number of open connections to any one target. Metric queries will run concurrently on
  # multiple connections.
  max_connections: 3
  # Maximum number of idle connections to any one target.
  max_idle_connections: 3
  # Maximum amount of time a connection may be reused to any one target. Infinite by default.
  max_connection_lifetime: 10m

# The target to monitor and the list of collectors to execute on it.
target:
  # Target name (optional). Setting this field enables extra metrics e.g. `up` and `scrape_duration` with
  # the `target` label that are always returned on a scrape.
  name: "prices_db"
  # Data source name always has a URI schema that matches the driver name. In some cases (e.g. MySQL)
  # the schema gets dropped or replaced to match the driver expected DSN format.
  data_source_name: 'sqlserver://prom_user:prom_password@dbserver1.example.com:1433'

  # Collectors (referenced by name) to execute on the target.
  # Glob patterns are supported (see <https://pkg.go.dev/path/filepath#Match> for syntax).
  collectors: [pricing_data_freshness, pricing_*]

  # In case you need to connect to a backend that only responds to a limited set of commands (e.g. pgbouncer) or
  # a data warehouse you don't want to keep online all the time (due to the extra cost), you might want to disable `ping`
  # enable_ping: true

# Collector definition files.
# Glob patterns are supported (see <https://pkg.go.dev/path/filepath#Match> for syntax).
collector_files:
  - "*.collector.yml"

NOTE: The collectors and collector_files configurations support Glob pattern matching. To match names with literal pattern terms in them, e.g. collector_*1*, these must be escaped: collector_\*1\*.

Collectors

Collectors may be defined inline, in the exporter configuration file, under collectors, or they may be defined in separate files and referenced in the exporter configuration by name, making them easy to share and reuse.

The collector definition below generates gauge metrics of the form pricing_update_time{market="US"}.

./pricing_data_freshness.collector.yml

# This collector will be referenced in the exporter configuration as `pricing_data_freshness`.
collector_name: pricing_data_freshness

# A Prometheus metric with (optional) additional labels, value and labels populated from one query.
metrics:
  - metric_name: pricing_update_time
    type: gauge
    help: 'Time when prices for a market were last updated.'
    key_labels:
      # Populated from the `market` column of each row.
      - Market
    static_labels:
      # Arbitrary key/value pair
      portfolio: income
    values: [LastUpdateTime]
    # Static metric value (optional). Useful in case we are interested in string data (key_labels) only. It's mutually
    # exclusive with `values` field.
    # static_value: 1
    # Timestamp value (optional). Should point at the existing column containing valid timestamps to return a metric
    # with an explicit timestamp.
    # timestamp_value: CreatedAt
    query: |
      SELECT Market, max(UpdateTime) AS LastUpdateTime
      FROM MarketPrices
      GROUP BY Market
Data Source Names (DSN)

To keep things simple and yet allow fully configurable database connections, SQL Exporter uses DSNs (like sqlserver://prom_user:prom_password@dbserver1.example.com:1433) to refer to database instances.

Since v0.9.0 sql_exporter relies on github.com/xo/dburl package for parsing Data Source Names (DSN). This can potentially affect your connection to certain databases like MySQL, so you might want to adjust your connection string accordingly:

mysql://user:pass@localhost/dbname - for TCP connection
mysql:/var/run/mysqld/mysqld.sock - for Unix socket connection

If your DSN contains special characters in any part of your connection string (including passwords), you might need to apply URL encoding (percent-encoding) to them. For example, p@$$w0rd#abc then becomes p%40%24%24w0rd%23abc.

For additional details please refer to xo/dburl documentation.

Using AWS Secrets Manager

If the database runs on AWS EC2 instance, this is a secure option to store the DSN without having it in the configuration file. To use this option:

  • Create a secret in key/value pairs format, specify Key data_source_name and then for Value enter the DSN value. For the secret name, enter a name for your secret, and pass that name in the configuration file as a value for aws_secret_name item under target. Secret json example:
{
  "data_source_name": "sqlserver://prom_user:prom_password@dbserver1.example.com:1433"
}
  • Configuration file example:
...
target:
  aws_secret_name: '<AWS_SECRET_NAME>'
...
  • Allow read-only access from EC2 IAM role to the secret by attaching a resource-based policy to the secret. Policy example:
{
  "Version" : "2012-10-17",
  "Statement" : [
    {
      "Effect": "Allow",
      "Principal": {"AWS": "arn:aws:iam::123456789012:role/EC2RoleToAccessSecrets"},
      "Action": "secretsmanager:GetSecretValue",
      "Resource": "*",
    }
  ]
}

Currently, AWS Secret Manager integration is only available for a single target configuration.

Multiple database connections

It is possible to run a single exporter instance against multiple database connections. In this case we need to configure jobs list instead of the target section as in the following example:

jobs:
  - job_name: db_targets
    collectors: [pricing_data_freshness, pricing_*]
    enable_ping: true # Optional, true by default. Set to `false` in case you connect to pgbouncer or a data warehouse
    static_configs:
      - targets:
          pg1: 'pg://db1@127.0.0.1:25432/postgres?sslmode=disable'
          pg2: 'postgresql://username:password@pg-host.example.com:5432/dbname?sslmode=disable'
        labels:  # Optional, arbitrary key/value pair for all targets
          cluster: cluster1

, where DSN strings are assigned to the arbitrary instance names (i.e. pg1 and pg2).

We can also define multiple jobs to run different collectors against different target sets.

Since v0.14, sql_exporter can be passed an optional list of job names to filter out metrics. The jobs[] query parameter may be used multiple times. In Prometheus configuration we can use this syntax under the scrape config:

  params:
    jobs[]:
      - db_targets1
      - db_targets2

This might be useful for scraping targets with different intervals or any other advanced use cases, when calling all jobs at once is undesired.

TLS and Basic Authentication

SQL Exporter supports TLS and Basic Authentication. This enables better control of the various HTTP endpoints.

To use TLS and/or Basic Authentication, you need to pass a configuration file using the --web.config.file parameter. The format of the file is described in the exporter-toolkit repository.

Why It Exists

SQL Exporter started off as an exporter for Microsoft SQL Server, for which no reliable exporters exist. But what is the point of a configuration driven SQL exporter, if you're going to use it along with 2 more exporters with wholly different world views and configurations, because you also have MySQL and PostgreSQL instances to monitor?

A couple of alternative database agnostic exporters are available:

However, they both do the collection at fixed intervals, independent of Prometheus scrapes. This is partly a philosophical issue, but practical issues are not all that difficult to imagine:

  • jitter;
  • duplicate data points;
  • collected but not scraped data points.

The control they provide over which labels get applied is limited, and the base label set spammy. And finally, configurations are not easily reused without copy-pasting and editing across jobs and instances.

Credits

This is a permanent fork of Database agnostic SQL exporter for Prometheus created by @free.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	SvcRegistry = prometheus.NewRegistry()
)

Functions

func OfBool

func OfBool(i bool) *bool

OfBool returns bool address.

func OpenConnection

func OpenConnection(ctx context.Context, logContext, dsn string, maxConns, maxIdleConns int, maxConnLifetime time.Duration) (*sql.DB, error)

OpenConnection parses a provided DSN, and opens a DB handle ensuring early termination if the context is closed (this is actually prevented by `database/sql` implementation), sets connection limits and returns the handle.

func PingDB

func PingDB(ctx context.Context, conn *sql.DB) error

PingDB is a wrapper around sql.DB.PingContext() that terminates as soon as the context is closed.

sql.DB does not actually pass along the context to the driver when opening a connection (which always happens if the database is down) and the driver uses an arbitrary timeout which may well be longer than ours. So we run the ping call in a goroutine and terminate immediately if the context is closed.

func TrimMissingCtx

func TrimMissingCtx(logContext string) string

Leading comma appears when previous parameter is undefined, which is a side-effect of running in single target mode. Let's trim to avoid confusions.

Types

type Collector

type Collector interface {
	// Collect is the equivalent of prometheus.Collector.Collect() but takes a context to run in and a database to run on.
	Collect(context.Context, *sql.DB, chan<- Metric)
}

Collector is a self-contained group of SQL queries and metric families to collect from a specific database. It is conceptually similar to a prometheus.Collector.

func NewCollector

func NewCollector(logContext string, cc *config.CollectorConfig, constLabels []*dto.LabelPair) (Collector, errors.WithContext)

NewCollector returns a new Collector with the given configuration and database. The metrics it creates will all have the provided const labels applied.

type Exporter

type Exporter interface {
	prometheus.Gatherer

	// WithContext returns a (single use) copy of the Exporter, which will use the provided context for Gather() calls.
	WithContext(context.Context) Exporter
	// Config returns the Exporter's underlying Config object.
	Config() *config.Config
	// UpdateTarget updates the targets field
	UpdateTarget([]Target)
	// SetJobFilters sets the jobFilters field
	SetJobFilters([]string)
}

Exporter is a prometheus.Gatherer that gathers SQL metrics from targets and merges them with the default registry.

func NewExporter

func NewExporter(configFile string) (Exporter, error)

NewExporter returns a new Exporter with the provided config.

type Job

type Job interface {
	Targets() []Target
}

Job is a collection of targets with the same collectors applied.

func NewJob

NewJob returns a new Job with the given configuration.

type Metric

type Metric interface {
	Desc() MetricDesc
	Write(out *dto.Metric) errors.WithContext
}

A Metric models a single sample value with its meta data being exported to Prometheus.

func NewInvalidMetric

func NewInvalidMetric(err errors.WithContext) Metric

NewInvalidMetric returns a metric whose Write method always returns the provided error.

func NewMetric

func NewMetric(desc MetricDesc, value float64, labelValues ...string) Metric

NewMetric returns a metric with one fixed value that cannot be changed.

NewMetric panics if the length of labelValues is not consistent with desc.labels().

func NewMetricWithTimestamp

func NewMetricWithTimestamp(t time.Time, m Metric) Metric

type MetricDesc

type MetricDesc interface {
	Name() string
	Help() string
	ValueType() prometheus.ValueType
	ConstLabels() []*dto.LabelPair
	Labels() []string
	LogContext() string
}

MetricDesc is a descriptor for a family of metrics, sharing the same name, help, labes, type.

func NewAutomaticMetricDesc

func NewAutomaticMetricDesc(
	logContext, name, help string, valueType prometheus.ValueType, constLabels []*dto.LabelPair, labels ...string,
) MetricDesc

NewAutomaticMetricDesc creates a MetricDesc for automatically generated metrics.

type MetricFamily

type MetricFamily struct {
	// contains filtered or unexported fields
}

MetricFamily implements MetricDesc for SQL metrics, with logic for populating its labels and values from sql.Rows.

func NewMetricFamily

func NewMetricFamily(logContext string, mc *config.MetricConfig, constLabels []*dto.LabelPair) (*MetricFamily, errors.WithContext)

NewMetricFamily creates a new MetricFamily with the given metric config and const labels (e.g. job and instance).

func (MetricFamily) Collect

func (mf MetricFamily) Collect(row map[string]any, ch chan<- Metric)

Collect is the equivalent of prometheus.Collector.Collect() but takes a Query output map to populate values from.

func (MetricFamily) ConstLabels

func (mf MetricFamily) ConstLabels() []*dto.LabelPair

ConstLabels implements MetricDesc.

func (MetricFamily) Help

func (mf MetricFamily) Help() string

Help implements MetricDesc.

func (MetricFamily) Labels

func (mf MetricFamily) Labels() []string

Labels implements MetricDesc.

func (MetricFamily) LogContext

func (mf MetricFamily) LogContext() string

LogContext implements MetricDesc.

func (MetricFamily) Name

func (mf MetricFamily) Name() string

Name implements MetricDesc.

func (MetricFamily) ValueType

func (mf MetricFamily) ValueType() prometheus.ValueType

ValueType implements MetricDesc.

type Query

type Query struct {
	// contains filtered or unexported fields
}

Query wraps a sql.Stmt and all the metrics populated from it. It helps extract keys and values from result rows.

func NewQuery

func NewQuery(logContext string, qc *config.QueryConfig, metricFamilies ...*MetricFamily) (*Query, errors.WithContext)

NewQuery returns a new Query that will populate the given metric families.

func (*Query) Collect

func (q *Query) Collect(ctx context.Context, conn *sql.DB, ch chan<- Metric)

Collect is the equivalent of prometheus.Collector.Collect() but takes a context to run in and a database to run on.

type Target

type Target interface {
	// Collect is the equivalent of prometheus.Collector.Collect(), but takes a context to run in.
	Collect(ctx context.Context, ch chan<- Metric)
	JobGroup() string
}

Target collects SQL metrics from a single sql.DB instance. It aggregates one or more Collectors and it looks much like a prometheus.Collector, except its Collect() method takes a Context to run in.

func NewTarget

func NewTarget(
	logContext, tname, jg, dsn string, ccs []*config.CollectorConfig, constLabels prometheus.Labels, gc *config.GlobalConfig, ep *bool) (
	Target, errors.WithContext,
)

NewTarget returns a new Target with the given target name, data source name, collectors and constant labels. An empty target name means the exporter is running in single target mode: no synthetic metrics will be exported.

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL