@@ -315,6 +315,16 @@ var builtinMetricMaps = map[string]intermediateMetricMap{
315
315
true ,
316
316
0 ,
317
317
},
318
+ "pg_replication_slots" : {
319
+ map [string ]ColumnMapping {
320
+ "slot_name" : {LABEL , "Name of the replication slot" , nil , nil },
321
+ "database" : {LABEL , "Name of the database" , nil , nil },
322
+ "active" : {GAUGE , "Flag indicating if the slot is active" , nil , nil },
323
+ "pg_wal_lsn_diff" : {GAUGE , "Replication lag in bytes" , nil , nil },
324
+ },
325
+ true ,
326
+ 0 ,
327
+ },
318
328
"pg_stat_archiver" : {
319
329
map [string ]ColumnMapping {
320
330
"archived_count" : {COUNTER , "Number of WAL files that have been successfully archived" , nil , nil },
@@ -407,6 +417,16 @@ var queryOverrides = map[string][]OverrideQuery{
407
417
},
408
418
},
409
419
420
+ "pg_replication_slots" : {
421
+ {
422
+ semver .MustParseRange (">=9.4.0" ),
423
+ `
424
+ SELECT slot_name, database, active, pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)
425
+ FROM pg_replication_slots
426
+ ` ,
427
+ },
428
+ },
429
+
410
430
"pg_stat_archiver" : {
411
431
{
412
432
semver .MustParseRange (">=0.0.0" ),
@@ -1561,50 +1581,51 @@ func (e *Exporter) scrapeDSN(ch chan<- prometheus.Metric, dsn string) error {
1561
1581
// DATA_SOURCE_NAME always wins so we do not break older versions
1562
1582
// reading secrets from files wins over secrets in environment variables
1563
1583
// DATA_SOURCE_NAME > DATA_SOURCE_{USER|PASS}_FILE > DATA_SOURCE_{USER|PASS}
1564
- func getDataSources () []string {
1584
+ func getDataSources () ( []string , error ) {
1565
1585
var dsn = os .Getenv ("DATA_SOURCE_NAME" )
1566
- if len (dsn ) == 0 {
1567
- var user string
1568
- var pass string
1569
- var uri string
1586
+ if len (dsn ) != 0 {
1587
+ return strings .Split (dsn , "," ), nil
1588
+ }
1570
1589
1571
- if len (os .Getenv ("DATA_SOURCE_USER_FILE" )) != 0 {
1572
- fileContents , err := ioutil .ReadFile (os .Getenv ("DATA_SOURCE_USER_FILE" ))
1573
- if err != nil {
1574
- panic (err )
1575
- }
1576
- user = strings .TrimSpace (string (fileContents ))
1577
- } else {
1578
- user = os .Getenv ("DATA_SOURCE_USER" )
1579
- }
1590
+ var user , pass , uri string
1580
1591
1581
- if len (os .Getenv ("DATA_SOURCE_PASS_FILE" )) != 0 {
1582
- fileContents , err := ioutil .ReadFile (os .Getenv ("DATA_SOURCE_PASS_FILE" ))
1583
- if err != nil {
1584
- panic (err )
1585
- }
1586
- pass = strings .TrimSpace (string (fileContents ))
1587
- } else {
1588
- pass = os .Getenv ("DATA_SOURCE_PASS" )
1592
+ dataSourceUserFile := os .Getenv ("DATA_SOURCE_USER_FILE" )
1593
+ if len (dataSourceUserFile ) != 0 {
1594
+ fileContents , err := ioutil .ReadFile (dataSourceUserFile )
1595
+ if err != nil {
1596
+ return nil , fmt .Errorf ("failed loading data source user file %s: %s" , dataSourceUserFile , err .Error ())
1589
1597
}
1598
+ user = strings .TrimSpace (string (fileContents ))
1599
+ } else {
1600
+ user = os .Getenv ("DATA_SOURCE_USER" )
1601
+ }
1590
1602
1591
- ui := url .UserPassword (user , pass ).String ()
1603
+ dataSourcePassFile := os .Getenv ("DATA_SOURCE_PASS_FILE" )
1604
+ if len (dataSourcePassFile ) != 0 {
1605
+ fileContents , err := ioutil .ReadFile (dataSourcePassFile )
1606
+ if err != nil {
1607
+ return nil , fmt .Errorf ("failed loading data source pass file %s: %s" , dataSourcePassFile , err .Error ())
1608
+ }
1609
+ pass = strings .TrimSpace (string (fileContents ))
1610
+ } else {
1611
+ pass = os .Getenv ("DATA_SOURCE_PASS" )
1612
+ }
1592
1613
1593
- if len (os .Getenv ("DATA_SOURCE_URI_FILE" )) != 0 {
1594
- fileContents , err := ioutil .ReadFile (os .Getenv ("DATA_SOURCE_URI_FILE" ))
1595
- if err != nil {
1596
- panic (err )
1597
- }
1598
- uri = strings .TrimSpace (string (fileContents ))
1599
- } else {
1600
- uri = os .Getenv ("DATA_SOURCE_URI" )
1614
+ ui := url .UserPassword (user , pass ).String ()
1615
+ dataSrouceURIFile := os .Getenv ("DATA_SOURCE_URI_FILE" )
1616
+ if len (dataSrouceURIFile ) != 0 {
1617
+ fileContents , err := ioutil .ReadFile (dataSrouceURIFile )
1618
+ if err != nil {
1619
+ return nil , fmt .Errorf ("failed loading data source URI file %s: %s" , dataSrouceURIFile , err .Error ())
1601
1620
}
1621
+ uri = strings .TrimSpace (string (fileContents ))
1622
+ } else {
1623
+ uri = os .Getenv ("DATA_SOURCE_URI" )
1624
+ }
1602
1625
1603
- dsn = "postgresql://" + ui + "@" + uri
1626
+ dsn = "postgresql://" + ui + "@" + uri
1604
1627
1605
- return []string {dsn }
1606
- }
1607
- return strings .Split (dsn , "," )
1628
+ return []string {dsn }, nil
1608
1629
}
1609
1630
1610
1631
func contains (a []string , x string ) bool {
@@ -1637,19 +1658,25 @@ func main() {
1637
1658
return
1638
1659
}
1639
1660
1640
- dsn := getDataSources ()
1661
+ dsn , err := getDataSources ()
1662
+ if err != nil {
1663
+ log .Fatalf ("failed reading data sources: %s" , err .Error ())
1664
+ }
1665
+
1641
1666
if len (dsn ) == 0 {
1642
1667
log .Fatal ("couldn't find environment variables describing the datasource to use" )
1643
1668
}
1644
1669
1645
- exporter := NewExporter ( dsn ,
1670
+ opts := [] ExporterOpt {
1646
1671
DisableDefaultMetrics (* disableDefaultMetrics ),
1647
1672
DisableSettingsMetrics (* disableSettingsMetrics ),
1648
1673
AutoDiscoverDatabases (* autoDiscoverDatabases ),
1649
1674
WithUserQueriesPath (* queriesPath ),
1650
1675
WithConstantLabels (* constantLabelsList ),
1651
1676
ExcludeDatabases (* excludeDatabases ),
1652
- )
1677
+ }
1678
+
1679
+ exporter := NewExporter (dsn , opts ... )
1653
1680
defer func () {
1654
1681
exporter .servers .Close ()
1655
1682
}()
0 commit comments