Skip to content

Commit 71d4800

Browse files
authored
Add arbitrary labels on a per-database basis (#287)
* Adding arbitrary per-database labels as per #275 * Adjusting per-DB label assignments: * Ensuring the list of allConstLabels is part of the exporter now, and it's passed into the Database each time its needed in there. * Adjusting the format of the label/value from a list of name/value pairs to a map of strings.
1 parent 773ecb8 commit 71d4800

File tree

6 files changed

+92
-21
lines changed

6 files changed

+92
-21
lines changed

README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -721,6 +721,12 @@ databases:
721721
## Oracle Database Connection pool minimum size
722722
# poolMinConnections: 15
723723
724+
## Arbitrary labels to add to each metric scraped from this database
725+
# labels:
726+
# label_name1: label_value1
727+
# label_name2: label_value2
728+
729+
724730
metrics:
725731
## How often to scrape metrics. If not provided, metrics will be scraped on request.
726732
# scrapeInterval: 15s
@@ -791,6 +797,16 @@ databases:
791797
# poolMaxConnections: 15
792798
## Oracle Database Connection pool minimum size
793799
# poolMinConnections: 15
800+
801+
### Arbitrary labels to add to each metric scraped from this database
802+
## Any labels configured for one database will be added to metrics from
803+
## every database, because the same metric names must always have the same
804+
## full labelset. If the label isn't set for a particular database, then it
805+
## will just be set to an empty string.
806+
# labels:
807+
# label_name1: label_value1
808+
# label_name2: label_value2
809+
794810
db2:
795811
## Database username
796812
username: ${DB2_USERNAME}
@@ -828,6 +844,15 @@ databases:
828844
## Oracle Database Connection pool minimum size
829845
# poolMinConnections: 15
830846
847+
### Arbitrary labels to add to each metric scraped from this database
848+
## Any labels configured for one database will be added to metrics from
849+
## every database, because the same metric names must always have the same
850+
## full labelset. If the label isn't set for a particular database, then it
851+
## will just be set to an empty string.
852+
# labels:
853+
# label_name1: label_value1
854+
# label_name2: label_value2
855+
831856
metrics:
832857
## How often to scrape metrics. If not provided, metrics will be scraped on request.
833858
# scrapeInterval: 15s

collector/collector.go

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"io"
1515
"log/slog"
1616
"os"
17+
"slices"
1718
"strconv"
1819
"strings"
1920
"sync"
@@ -50,6 +51,20 @@ func maskDsn(dsn string) string {
5051
func NewExporter(logger *slog.Logger, m *MetricsConfiguration) *Exporter {
5152
var databases []*Database
5253
wg := &sync.WaitGroup{}
54+
55+
var allConstLabels []string
56+
// All the metrics of the same name need to have the same set of labels
57+
// If a label is set for a particular database, it must be included also
58+
// in the same metrics collected from other databases. It will just be
59+
// set to a blank value.
60+
for _, dbconfig := range m.Databases {
61+
for label, _ := range dbconfig.Labels {
62+
if (!slices.Contains(allConstLabels, label)) {
63+
allConstLabels = append(allConstLabels, label)
64+
}
65+
}
66+
}
67+
5368
for dbname, dbconfig := range m.Databases {
5469
logger.Info("Initializing database", "database", dbname)
5570
database := NewDatabase(logger, dbname, dbconfig)
@@ -91,12 +106,25 @@ func NewExporter(logger *slog.Logger, m *MetricsConfiguration) *Exporter {
91106
MetricsConfiguration: m,
92107
databases: databases,
93108
lastScraped: map[string]*time.Time{},
109+
allConstLabels: allConstLabels,
94110
}
95111
e.metricsToScrape = e.DefaultMetrics()
96112

97113
return e
98114
}
99115

116+
func (e *Exporter) constLabels() map[string]string {
117+
// All the metrics of the same name need to have the same labels
118+
// If a label is set for a particular database, it must be included also
119+
// in the same metrics collected from other databases. It will just be
120+
// set to a blank value.
121+
labels := map[string]string{}
122+
for _, label := range e.allConstLabels {
123+
labels[label] = ""
124+
}
125+
return labels
126+
}
127+
100128
// Describe describes all the metrics exported by the Oracle DB exporter.
101129
func (e *Exporter) Describe(ch chan<- *prometheus.Desc) {
102130
// We cannot know in advance what metrics the exporter will generate
@@ -148,8 +176,8 @@ func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
148176
ch <- e.error
149177
e.scrapeErrors.Collect(ch)
150178
for _, db := range e.databases {
151-
ch <- db.DBTypeMetric()
152-
ch <- db.UpMetric()
179+
ch <- db.DBTypeMetric(e.constLabels())
180+
ch <- db.UpMetric(e.constLabels())
153181
}
154182
}
155183

@@ -203,7 +231,7 @@ func (e *Exporter) scheduledScrape(tick *time.Time) {
203231
metricCh <- e.error
204232
e.scrapeErrors.Collect(metricCh)
205233
for _, db := range e.databases {
206-
metricCh <- db.UpMetric()
234+
metricCh <- db.UpMetric(e.constLabels())
207235
}
208236
close(metricCh)
209237
wg.Wait()
@@ -399,7 +427,8 @@ func (e *Exporter) scrapeGenericValues(d *Database, ch chan<- prometheus.Metric,
399427
metricsDesc map[string]string, metricsType map[string]string, metricsBuckets map[string]map[string]string,
400428
fieldToAppend string, ignoreZeroResult bool, request string, queryTimeout time.Duration) error {
401429
metricsCount := 0
402-
constLabels := d.constLabels()
430+
constLabels := d.constLabels(e.constLabels())
431+
e.logger.Debug("labels", constLabels)
403432
genericParser := func(row map[string]string) error {
404433
// Construct labels value
405434
labelsValues := []string{}

collector/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ type DatabaseConfig struct {
2727
URL string `yaml:"url"`
2828
ConnectConfig `yaml:",inline"`
2929
Vault *VaultConfig `yaml:"vault,omitempty"`
30+
Labels map[string]string `yaml:"labels,omitempty"`
3031
}
3132

3233
type ConnectConfig struct {

collector/database.go

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,25 @@ import (
1515
"time"
1616
)
1717

18-
func (d *Database) UpMetric() prometheus.Metric {
18+
func (d *Database) UpMetric(exporterLabels map[string]string) prometheus.Metric {
1919
desc := prometheus.NewDesc(
2020
prometheus.BuildFQName(namespace, "", "up"),
2121
"Whether the Oracle database server is up.",
2222
nil,
23-
d.constLabels(),
23+
d.constLabels(exporterLabels),
2424
)
2525
return prometheus.MustNewConstMetric(desc,
2626
prometheus.GaugeValue,
2727
d.Up,
2828
)
2929
}
3030

31-
func (d *Database) DBTypeMetric() prometheus.Metric {
31+
func (d *Database) DBTypeMetric(exporterLabels map[string]string) prometheus.Metric {
3232
desc := prometheus.NewDesc(
3333
prometheus.BuildFQName(namespace, "", "dbtype"),
3434
"Type of database the exporter is connected to (0=non-CDB, 1=CDB, >1=PDB).",
3535
nil,
36-
d.constLabels(),
36+
d.constLabels(exporterLabels),
3737
)
3838
return prometheus.MustNewConstMetric(desc,
3939
prometheus.GaugeValue,
@@ -65,20 +65,24 @@ func (d *Database) ping(logger *slog.Logger) error {
6565
return nil
6666
}
6767

68-
func (d *Database) constLabels() map[string]string {
69-
return map[string]string{
70-
"database": d.Name,
68+
func (d *Database) constLabels(labels map[string]string) map[string]string {
69+
labels["database"] = d.Name
70+
71+
// configured per-database labels added to constLabels
72+
for label, value := range d.Config.Labels {
73+
labels[label] = value
7174
}
75+
return labels
7276
}
7377

7478
func NewDatabase(logger *slog.Logger, dbname string, dbconfig DatabaseConfig) *Database {
7579
db, dbtype := connect(logger, dbname, dbconfig)
7680
return &Database{
77-
Name: dbname,
78-
Up: 0,
79-
Session: db,
80-
Type: dbtype,
81-
Config: dbconfig,
81+
Name: dbname,
82+
Up: 0,
83+
Session: db,
84+
Type: dbtype,
85+
Config: dbconfig,
8286
}
8387
}
8488

collector/types.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,15 @@ type Exporter struct {
2424
databases []*Database
2525
logger *slog.Logger
2626
lastScraped map[string]*time.Time
27+
allConstLabels []string
2728
}
2829

2930
type Database struct {
30-
Name string
31-
Up float64
32-
Session *sql.DB
33-
Type float64
34-
Config DatabaseConfig
31+
Name string
32+
Up float64
33+
Session *sql.DB
34+
Type float64
35+
Config DatabaseConfig
3536
}
3637

3738
type Config struct {

example-config-multi-database.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ databases:
4545
# poolMaxConnections: 15
4646
## Oracle Database Connection pool minimum size
4747
# poolMinConnections: 15
48+
49+
## Arbitrary labels to add to each metric scraped from this database
50+
# labels:
51+
# label_name1: label_value1
52+
# label_name2: label_value2
53+
4854
db2:
4955
## Database username
5056
username: ${DB2_USERNAME}
@@ -82,6 +88,11 @@ databases:
8288
## Oracle Database Connection pool minimum size
8389
# poolMinConnections: 15
8490

91+
## Arbitrary labels to add to each metric scraped from this database
92+
# labels:
93+
# label_name1: label_value1
94+
# label_name2: label_value2
95+
8596
metrics:
8697
## How often to scrape metrics. If not provided, metrics will be scraped on request.
8798
# scrapeInterval: 15s

0 commit comments

Comments
 (0)