@@ -315,6 +315,16 @@ var builtinMetricMaps = map[string]intermediateMetricMap{
315315 true ,
316316 0 ,
317317 },
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+ },
318328 "pg_stat_archiver" : {
319329 map [string ]ColumnMapping {
320330 "archived_count" : {COUNTER , "Number of WAL files that have been successfully archived" , nil , nil },
@@ -408,6 +418,16 @@ var queryOverrides = map[string][]OverrideQuery{
408418 },
409419 },
410420
421+ "pg_replication_slots" : {
422+ {
423+ semver .MustParseRange (">=9.4.0" ),
424+ `
425+ SELECT slot_name, database, active, pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)
426+ FROM pg_replication_slots
427+ ` ,
428+ },
429+ },
430+
411431 "pg_stat_archiver" : {
412432 {
413433 semver .MustParseRange (">=0.0.0" ),
@@ -1562,50 +1582,51 @@ func (e *Exporter) scrapeDSN(ch chan<- prometheus.Metric, dsn string) error {
15621582// DATA_SOURCE_NAME always wins so we do not break older versions
15631583// reading secrets from files wins over secrets in environment variables
15641584// DATA_SOURCE_NAME > DATA_SOURCE_{USER|PASS}_FILE > DATA_SOURCE_{USER|PASS}
1565- func getDataSources () []string {
1585+ func getDataSources () ( []string , error ) {
15661586 var dsn = os .Getenv ("DATA_SOURCE_NAME" )
1567- if len (dsn ) == 0 {
1568- var user string
1569- var pass string
1570- var uri string
1587+ if len (dsn ) != 0 {
1588+ return strings .Split (dsn , "," ), nil
1589+ }
15711590
1572- if len (os .Getenv ("DATA_SOURCE_USER_FILE" )) != 0 {
1573- fileContents , err := ioutil .ReadFile (os .Getenv ("DATA_SOURCE_USER_FILE" ))
1574- if err != nil {
1575- panic (err )
1576- }
1577- user = strings .TrimSpace (string (fileContents ))
1578- } else {
1579- user = os .Getenv ("DATA_SOURCE_USER" )
1580- }
1591+ var user , pass , uri string
15811592
1582- if len (os .Getenv ("DATA_SOURCE_PASS_FILE" )) != 0 {
1583- fileContents , err := ioutil .ReadFile (os .Getenv ("DATA_SOURCE_PASS_FILE" ))
1584- if err != nil {
1585- panic (err )
1586- }
1587- pass = strings .TrimSpace (string (fileContents ))
1588- } else {
1589- pass = os .Getenv ("DATA_SOURCE_PASS" )
1593+ dataSourceUserFile := os .Getenv ("DATA_SOURCE_USER_FILE" )
1594+ if len (dataSourceUserFile ) != 0 {
1595+ fileContents , err := ioutil .ReadFile (dataSourceUserFile )
1596+ if err != nil {
1597+ return nil , fmt .Errorf ("failed loading data source user file %s: %s" , dataSourceUserFile , err .Error ())
15901598 }
1599+ user = strings .TrimSpace (string (fileContents ))
1600+ } else {
1601+ user = os .Getenv ("DATA_SOURCE_USER" )
1602+ }
15911603
1592- ui := url .UserPassword (user , pass ).String ()
1604+ dataSourcePassFile := os .Getenv ("DATA_SOURCE_PASS_FILE" )
1605+ if len (dataSourcePassFile ) != 0 {
1606+ fileContents , err := ioutil .ReadFile (dataSourcePassFile )
1607+ if err != nil {
1608+ return nil , fmt .Errorf ("failed loading data source pass file %s: %s" , dataSourcePassFile , err .Error ())
1609+ }
1610+ pass = strings .TrimSpace (string (fileContents ))
1611+ } else {
1612+ pass = os .Getenv ("DATA_SOURCE_PASS" )
1613+ }
15931614
1594- if len (os .Getenv ("DATA_SOURCE_URI_FILE" )) != 0 {
1595- fileContents , err := ioutil .ReadFile (os .Getenv ("DATA_SOURCE_URI_FILE" ))
1596- if err != nil {
1597- panic (err )
1598- }
1599- uri = strings .TrimSpace (string (fileContents ))
1600- } else {
1601- uri = os .Getenv ("DATA_SOURCE_URI" )
1615+ ui := url .UserPassword (user , pass ).String ()
1616+ dataSrouceURIFile := os .Getenv ("DATA_SOURCE_URI_FILE" )
1617+ if len (dataSrouceURIFile ) != 0 {
1618+ fileContents , err := ioutil .ReadFile (dataSrouceURIFile )
1619+ if err != nil {
1620+ return nil , fmt .Errorf ("failed loading data source URI file %s: %s" , dataSrouceURIFile , err .Error ())
16021621 }
1622+ uri = strings .TrimSpace (string (fileContents ))
1623+ } else {
1624+ uri = os .Getenv ("DATA_SOURCE_URI" )
1625+ }
16031626
1604- dsn = "postgresql://" + ui + "@" + uri
1627+ dsn = "postgresql://" + ui + "@" + uri
16051628
1606- return []string {dsn }
1607- }
1608- return strings .Split (dsn , "," )
1629+ return []string {dsn }, nil
16091630}
16101631
16111632func contains (a []string , x string ) bool {
@@ -1638,19 +1659,25 @@ func main() {
16381659 return
16391660 }
16401661
1641- dsn := getDataSources ()
1662+ dsn , err := getDataSources ()
1663+ if err != nil {
1664+ log .Fatalf ("failed reading data sources: %s" , err .Error ())
1665+ }
1666+
16421667 if len (dsn ) == 0 {
16431668 log .Fatal ("couldn't find environment variables describing the datasource to use" )
16441669 }
16451670
1646- exporter := NewExporter ( dsn ,
1671+ opts := [] ExporterOpt {
16471672 DisableDefaultMetrics (* disableDefaultMetrics ),
16481673 DisableSettingsMetrics (* disableSettingsMetrics ),
16491674 AutoDiscoverDatabases (* autoDiscoverDatabases ),
16501675 WithUserQueriesPath (* queriesPath ),
16511676 WithConstantLabels (* constantLabelsList ),
16521677 ExcludeDatabases (* excludeDatabases ),
1653- )
1678+ }
1679+
1680+ exporter := NewExporter (dsn , opts ... )
16541681 defer func () {
16551682 exporter .servers .Close ()
16561683 }()
0 commit comments