diff --git a/receiver/postgresqlreceiver/client.go b/receiver/postgresqlreceiver/client.go index 7bb51d5ed205..606dd9cb32de 100644 --- a/receiver/postgresqlreceiver/client.go +++ b/receiver/postgresqlreceiver/client.go @@ -55,6 +55,7 @@ type client interface { getLatestWalAgeSeconds(ctx context.Context) (int64, error) getMaxConnections(ctx context.Context) (int64, error) getIndexStats(ctx context.Context, database string) (map[indexIdentifer]indexStat, error) + getActiveConnections(ctx context.Context) (int64, error) listDatabases(ctx context.Context) ([]string, error) } @@ -488,6 +489,14 @@ func (c *postgreSQLClient) getMaxConnections(ctx context.Context) (int64, error) return maxConns, err } +func (c *postgreSQLClient) getActiveConnections(ctx context.Context) (int64, error) { + query := `SELECT COUNT(*) FROM pg_stat_activity WHERE state = 'active';` + row := c.client.QueryRowContext(ctx, query) + var activeConns int64 + err := row.Scan(&activeConns) + return activeConns, err +} + type replicationStats struct { clientAddr string pendingBytes int64 diff --git a/receiver/postgresqlreceiver/documentation.md b/receiver/postgresqlreceiver/documentation.md index 0de5881a955a..105ebcee1440 100644 --- a/receiver/postgresqlreceiver/documentation.md +++ b/receiver/postgresqlreceiver/documentation.md @@ -100,6 +100,14 @@ The number of commits. | ---- | ----------- | ---------- | ----------------------- | --------- | | 1 | Sum | Int | Cumulative | true | +### postgresql.connection.count + +The number of active connections to this database. If DBM is enabled, this metric is tagged with state, app, db and user + +| Unit | Metric Type | Value Type | +| ---- | ----------- | ---------- | +| {connection} | Gauge | Int | + ### postgresql.connection.max Configured maximum number of client connections allowed @@ -229,7 +237,7 @@ This metric requires WAL to be enabled with at least one replica. Time between flushing recent WAL locally and receiving notification that the standby server has completed an operation with it. -This metric requires WAL to be enabled with at least one replica. +This metric requires WAL to be enabled with at least one replica. | Unit | Metric Type | Value Type | diff --git a/receiver/postgresqlreceiver/internal/metadata/generated_config.go b/receiver/postgresqlreceiver/internal/metadata/generated_config.go index a0a53f803403..afaafb83fe93 100644 --- a/receiver/postgresqlreceiver/internal/metadata/generated_config.go +++ b/receiver/postgresqlreceiver/internal/metadata/generated_config.go @@ -36,6 +36,7 @@ type MetricsConfig struct { PostgresqlBgwriterMaxwritten MetricConfig `mapstructure:"postgresql.bgwriter.maxwritten"` PostgresqlBlocksRead MetricConfig `mapstructure:"postgresql.blocks_read"` PostgresqlCommits MetricConfig `mapstructure:"postgresql.commits"` + PostgresqlConnectionCount MetricConfig `mapstructure:"postgresql.connection.count"` PostgresqlConnectionMax MetricConfig `mapstructure:"postgresql.connection.max"` PostgresqlDatabaseCount MetricConfig `mapstructure:"postgresql.database.count"` PostgresqlDatabaseLocks MetricConfig `mapstructure:"postgresql.database.locks"` @@ -83,6 +84,9 @@ func DefaultMetricsConfig() MetricsConfig { PostgresqlCommits: MetricConfig{ Enabled: true, }, + PostgresqlConnectionCount: MetricConfig{ + Enabled: true, + }, PostgresqlConnectionMax: MetricConfig{ Enabled: true, }, diff --git a/receiver/postgresqlreceiver/internal/metadata/generated_config_test.go b/receiver/postgresqlreceiver/internal/metadata/generated_config_test.go index bdb699f1a761..9fc56002195c 100644 --- a/receiver/postgresqlreceiver/internal/metadata/generated_config_test.go +++ b/receiver/postgresqlreceiver/internal/metadata/generated_config_test.go @@ -33,6 +33,7 @@ func TestMetricsBuilderConfig(t *testing.T) { PostgresqlBgwriterMaxwritten: MetricConfig{Enabled: true}, PostgresqlBlocksRead: MetricConfig{Enabled: true}, PostgresqlCommits: MetricConfig{Enabled: true}, + PostgresqlConnectionCount: MetricConfig{Enabled: true}, PostgresqlConnectionMax: MetricConfig{Enabled: true}, PostgresqlDatabaseCount: MetricConfig{Enabled: true}, PostgresqlDatabaseLocks: MetricConfig{Enabled: true}, @@ -73,6 +74,7 @@ func TestMetricsBuilderConfig(t *testing.T) { PostgresqlBgwriterMaxwritten: MetricConfig{Enabled: false}, PostgresqlBlocksRead: MetricConfig{Enabled: false}, PostgresqlCommits: MetricConfig{Enabled: false}, + PostgresqlConnectionCount: MetricConfig{Enabled: false}, PostgresqlConnectionMax: MetricConfig{Enabled: false}, PostgresqlDatabaseCount: MetricConfig{Enabled: false}, PostgresqlDatabaseLocks: MetricConfig{Enabled: false}, diff --git a/receiver/postgresqlreceiver/internal/metadata/generated_metrics.go b/receiver/postgresqlreceiver/internal/metadata/generated_metrics.go index 70ffd5a4f753..c596b4691ac6 100644 --- a/receiver/postgresqlreceiver/internal/metadata/generated_metrics.go +++ b/receiver/postgresqlreceiver/internal/metadata/generated_metrics.go @@ -654,6 +654,55 @@ func newMetricPostgresqlCommits(cfg MetricConfig) metricPostgresqlCommits { return m } +type metricPostgresqlConnectionCount struct { + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + capacity int // max observed number of data points added to the metric. +} + +// init fills postgresql.connection.count metric with initial data. +func (m *metricPostgresqlConnectionCount) init() { + m.data.SetName("postgresql.connection.count") + m.data.SetDescription("The number of active connections to this database. If DBM is enabled, this metric is tagged with state, app, db and user") + m.data.SetUnit("{connection}") + m.data.SetEmptyGauge() +} + +func (m *metricPostgresqlConnectionCount) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64) { + if !m.config.Enabled { + return + } + dp := m.data.Gauge().DataPoints().AppendEmpty() + dp.SetStartTimestamp(start) + dp.SetTimestamp(ts) + dp.SetIntValue(val) +} + +// updateCapacity saves max length of data point slices that will be used for the slice capacity. +func (m *metricPostgresqlConnectionCount) updateCapacity() { + if m.data.Gauge().DataPoints().Len() > m.capacity { + m.capacity = m.data.Gauge().DataPoints().Len() + } +} + +// emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. +func (m *metricPostgresqlConnectionCount) emit(metrics pmetric.MetricSlice) { + if m.config.Enabled && m.data.Gauge().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricPostgresqlConnectionCount(cfg MetricConfig) metricPostgresqlConnectionCount { + m := metricPostgresqlConnectionCount{config: cfg} + if cfg.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + type metricPostgresqlConnectionMax struct { data pmetric.Metric // data buffer for generated metric. config MetricConfig // metric config provided by user. @@ -1643,6 +1692,7 @@ type MetricsBuilder struct { metricPostgresqlBgwriterMaxwritten metricPostgresqlBgwriterMaxwritten metricPostgresqlBlocksRead metricPostgresqlBlocksRead metricPostgresqlCommits metricPostgresqlCommits + metricPostgresqlConnectionCount metricPostgresqlConnectionCount metricPostgresqlConnectionMax metricPostgresqlConnectionMax metricPostgresqlDatabaseCount metricPostgresqlDatabaseCount metricPostgresqlDatabaseLocks metricPostgresqlDatabaseLocks @@ -1688,6 +1738,7 @@ func NewMetricsBuilder(mbc MetricsBuilderConfig, settings receiver.Settings, opt metricPostgresqlBgwriterMaxwritten: newMetricPostgresqlBgwriterMaxwritten(mbc.Metrics.PostgresqlBgwriterMaxwritten), metricPostgresqlBlocksRead: newMetricPostgresqlBlocksRead(mbc.Metrics.PostgresqlBlocksRead), metricPostgresqlCommits: newMetricPostgresqlCommits(mbc.Metrics.PostgresqlCommits), + metricPostgresqlConnectionCount: newMetricPostgresqlConnectionCount(mbc.Metrics.PostgresqlConnectionCount), metricPostgresqlConnectionMax: newMetricPostgresqlConnectionMax(mbc.Metrics.PostgresqlConnectionMax), metricPostgresqlDatabaseCount: newMetricPostgresqlDatabaseCount(mbc.Metrics.PostgresqlDatabaseCount), metricPostgresqlDatabaseLocks: newMetricPostgresqlDatabaseLocks(mbc.Metrics.PostgresqlDatabaseLocks), @@ -1803,6 +1854,7 @@ func (mb *MetricsBuilder) EmitForResource(rmo ...ResourceMetricsOption) { mb.metricPostgresqlBgwriterMaxwritten.emit(ils.Metrics()) mb.metricPostgresqlBlocksRead.emit(ils.Metrics()) mb.metricPostgresqlCommits.emit(ils.Metrics()) + mb.metricPostgresqlConnectionCount.emit(ils.Metrics()) mb.metricPostgresqlConnectionMax.emit(ils.Metrics()) mb.metricPostgresqlDatabaseCount.emit(ils.Metrics()) mb.metricPostgresqlDatabaseLocks.emit(ils.Metrics()) @@ -1893,6 +1945,11 @@ func (mb *MetricsBuilder) RecordPostgresqlCommitsDataPoint(ts pcommon.Timestamp, mb.metricPostgresqlCommits.recordDataPoint(mb.startTime, ts, val) } +// RecordPostgresqlConnectionCountDataPoint adds a data point to postgresql.connection.count metric. +func (mb *MetricsBuilder) RecordPostgresqlConnectionCountDataPoint(ts pcommon.Timestamp, val int64) { + mb.metricPostgresqlConnectionCount.recordDataPoint(mb.startTime, ts, val) +} + // RecordPostgresqlConnectionMaxDataPoint adds a data point to postgresql.connection.max metric. func (mb *MetricsBuilder) RecordPostgresqlConnectionMaxDataPoint(ts pcommon.Timestamp, val int64) { mb.metricPostgresqlConnectionMax.recordDataPoint(mb.startTime, ts, val) diff --git a/receiver/postgresqlreceiver/internal/metadata/generated_metrics_test.go b/receiver/postgresqlreceiver/internal/metadata/generated_metrics_test.go index 0ad63e4b400d..a7571ddd1fe4 100644 --- a/receiver/postgresqlreceiver/internal/metadata/generated_metrics_test.go +++ b/receiver/postgresqlreceiver/internal/metadata/generated_metrics_test.go @@ -100,6 +100,10 @@ func TestMetricsBuilder(t *testing.T) { allMetricsCount++ mb.RecordPostgresqlCommitsDataPoint(ts, 1) + defaultMetricsCount++ + allMetricsCount++ + mb.RecordPostgresqlConnectionCountDataPoint(ts, 1) + defaultMetricsCount++ allMetricsCount++ mb.RecordPostgresqlConnectionMaxDataPoint(ts, 1) @@ -322,6 +326,18 @@ func TestMetricsBuilder(t *testing.T) { assert.Equal(t, ts, dp.Timestamp()) assert.Equal(t, pmetric.NumberDataPointValueTypeInt, dp.ValueType()) assert.Equal(t, int64(1), dp.IntValue()) + case "postgresql.connection.count": + assert.False(t, validatedMetrics["postgresql.connection.count"], "Found a duplicate in the metrics slice: postgresql.connection.count") + validatedMetrics["postgresql.connection.count"] = true + assert.Equal(t, pmetric.MetricTypeGauge, ms.At(i).Type()) + assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + assert.Equal(t, "The number of active connections to this database. If DBM is enabled, this metric is tagged with state, app, db and user", ms.At(i).Description()) + assert.Equal(t, "{connection}", ms.At(i).Unit()) + dp := ms.At(i).Gauge().DataPoints().At(0) + assert.Equal(t, start, dp.StartTimestamp()) + assert.Equal(t, ts, dp.Timestamp()) + assert.Equal(t, pmetric.NumberDataPointValueTypeInt, dp.ValueType()) + assert.Equal(t, int64(1), dp.IntValue()) case "postgresql.connection.max": assert.False(t, validatedMetrics["postgresql.connection.max"], "Found a duplicate in the metrics slice: postgresql.connection.max") validatedMetrics["postgresql.connection.max"] = true diff --git a/receiver/postgresqlreceiver/internal/metadata/testdata/config.yaml b/receiver/postgresqlreceiver/internal/metadata/testdata/config.yaml index 8cf4613c3849..0b27c1df6325 100644 --- a/receiver/postgresqlreceiver/internal/metadata/testdata/config.yaml +++ b/receiver/postgresqlreceiver/internal/metadata/testdata/config.yaml @@ -17,6 +17,8 @@ all_set: enabled: true postgresql.commits: enabled: true + postgresql.connection.count: + enabled: true postgresql.connection.max: enabled: true postgresql.database.count: @@ -82,6 +84,8 @@ none_set: enabled: false postgresql.commits: enabled: false + postgresql.connection.count: + enabled: false postgresql.connection.max: enabled: false postgresql.database.count: diff --git a/receiver/postgresqlreceiver/metadata.yaml b/receiver/postgresqlreceiver/metadata.yaml index eb03b5a66f81..16840e4d4bac 100644 --- a/receiver/postgresqlreceiver/metadata.yaml +++ b/receiver/postgresqlreceiver/metadata.yaml @@ -12,8 +12,7 @@ status: resource_attributes: postgresql.database.name: description: The name of the database. - enabled: true - type: string + enabled: true type: string postgresql.schema.name: description: The schema name. enabled: true @@ -311,6 +310,15 @@ metrics: value_type: double extended_documentation: | This metric requires WAL to be enabled with at least one replica. - + tests: config: + + This metric requires WAL to be enabled with at least one replica. + postgresql.connection.count: + enabled: true + description: The number of active connections to this database. If DBM is enabled, + this metric is tagged with state, app, db and user + unit: '{connection}' + gauge: + value_type: int \ No newline at end of file diff --git a/receiver/postgresqlreceiver/scraper.go b/receiver/postgresqlreceiver/scraper.go index 91bfe6d42e2e..a9baee5610f3 100644 --- a/receiver/postgresqlreceiver/scraper.go +++ b/receiver/postgresqlreceiver/scraper.go @@ -163,6 +163,8 @@ func (p *postgreSQLScraper) scrape(ctx context.Context) (pmetric.Metrics, error) p.collectDatabaseLocks(ctx, now, listClient, &errs) + p.collectActiveConnections(ctx, now, listClient, &errs) + rb := p.mb.NewResourceBuilder() rb.SetPostgresqlDatabaseName("N/A") p.mb.EmitForResource(metadata.WithResource(rb.Emit())) @@ -348,6 +350,20 @@ func (p *postgreSQLScraper) collectMaxConnections( p.mb.RecordPostgresqlConnectionMaxDataPoint(now, mc) } +func (p *postgreSQLScraper) collectActiveConnections( + ctx context.Context, + now pcommon.Timestamp, + client client, + errs *errsMux, +) { + ac, err := client.getActiveConnections(ctx) + if err != nil { + errs.addPartial(err) + return + } + p.mb.RecordPostgresqlConnectionCountDataPoint(now, ac) +} + func (p *postgreSQLScraper) collectReplicationStats( ctx context.Context, now pcommon.Timestamp,