Skip to content
This repository was archived by the owner on Mar 13, 2024. It is now read-only.

Commit a95fad8

Browse files
committed
Refactor scrape() to break out the major components.
This puts us on the road to dropping the bash tests and doing it all in Go.
1 parent 53ad0ef commit a95fad8

File tree

1 file changed

+111
-105
lines changed

1 file changed

+111
-105
lines changed

postgres_exporter.go

Lines changed: 111 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,14 @@ import (
1010
"os"
1111
"strconv"
1212
"time"
13+
"regexp"
14+
"errors"
1315

1416
"gopkg.in/yaml.v2"
1517

1618
_ "github.com/lib/pq"
1719
"github.com/prometheus/client_golang/prometheus"
1820
"github.com/prometheus/common/log"
19-
"regexp"
2021
)
2122

2223
var Version string = "0.0.1"
@@ -589,36 +590,9 @@ func newDesc(subsystem, name, help string) *prometheus.Desc {
589590
)
590591
}
591592

592-
func (e *Exporter) scrape(ch chan<- prometheus.Metric) {
593-
defer func(begun time.Time) {
594-
e.duration.Set(time.Since(begun).Seconds())
595-
}(time.Now())
596-
597-
e.error.Set(0)
598-
e.totalScrapes.Inc()
599-
600-
db, err := sql.Open("postgres", e.dsn)
601-
if err != nil {
602-
log.Infoln("Error opening connection to database:", err)
603-
e.error.Set(1)
604-
return
605-
}
606-
defer db.Close()
607-
608-
log.Debugln("Querying Postgres Version")
609-
versionRow := db.QueryRow("SELECT version();")
610-
var versionString string
611-
err = versionRow.Scan(&versionString)
612-
if err != nil {
613-
log.Errorln("Error scanning version string:", err)
614-
e.error.Set(1)
615-
return
616-
}
617-
shortVersion := parseVersion(versionString)
618-
// Output the version as a special metric
619-
versionDesc := prometheus.NewDesc(fmt.Sprintf("%s_%s", namespace, staticLabelName), "Version string as reported by postgres", []string{"version", "short_version"}, nil)
620-
ch <- prometheus.MustNewConstMetric(versionDesc, prometheus.UntypedValue, 1, versionString, shortVersion)
621-
593+
// Query the SHOW variables from the query map
594+
// TODO: make this more functional
595+
func (e *Exporter) queryShowVariables(ch chan<- prometheus.Metric, db *sql.DB) {
622596
log.Debugln("Querying SHOW variables")
623597
for _, mapping := range e.variableMap {
624598
for columnName, columnMapping := range mapping.columnMappings {
@@ -647,96 +621,128 @@ func (e *Exporter) scrape(ch chan<- prometheus.Metric) {
647621
ch <- prometheus.MustNewConstMetric(columnMapping.desc, columnMapping.vtype, fval)
648622
}
649623
}
624+
}
650625

651-
for namespace, mapping := range e.metricMap {
652-
log.Debugln("Querying namespace: ", namespace)
653-
func() {
654-
query, er := queryOverrides[namespace]
655-
if er == false {
656-
query = fmt.Sprintf("SELECT * FROM %s;", namespace)
657-
}
626+
func queryNamespaceMapping(ch chan<- prometheus.Metric, db *sql.DB, namespace string, mapping MetricMapNamespace) error {
627+
query, er := queryOverrides[namespace]
628+
if er == false {
629+
query = fmt.Sprintf("SELECT * FROM %s;", namespace)
630+
}
658631

659-
// Don't fail on a bad scrape of one metric
660-
rows, err := db.Query(query)
661-
if err != nil {
662-
log.Infoln("Error running query on database: ", namespace, err)
663-
e.error.Set(1)
664-
return
665-
}
666-
defer rows.Close()
632+
// Don't fail on a bad scrape of one metric
633+
rows, err := db.Query(query)
634+
if err != nil {
635+
return errors.New(fmt.Sprintln("Error running query on database: ", namespace, err))
636+
}
637+
defer rows.Close()
667638

668-
var columnNames []string
669-
columnNames, err = rows.Columns()
670-
if err != nil {
671-
log.Infoln("Error retrieving column list for: ", namespace, err)
672-
e.error.Set(1)
673-
return
674-
}
639+
var columnNames []string
640+
columnNames, err = rows.Columns()
641+
if err != nil {
642+
return errors.New(fmt.Sprintln("Error retrieving column list for: ", namespace, err))
643+
}
675644

676-
// Make a lookup map for the column indices
677-
var columnIdx = make(map[string]int, len(columnNames))
678-
for i, n := range columnNames {
679-
columnIdx[n] = i
680-
}
645+
// Make a lookup map for the column indices
646+
var columnIdx = make(map[string]int, len(columnNames))
647+
for i, n := range columnNames {
648+
columnIdx[n] = i
649+
}
681650

682-
var columnData = make([]interface{}, len(columnNames))
683-
var scanArgs = make([]interface{}, len(columnNames))
684-
for i := range columnData {
685-
scanArgs[i] = &columnData[i]
686-
}
651+
var columnData = make([]interface{}, len(columnNames))
652+
var scanArgs = make([]interface{}, len(columnNames))
653+
for i := range columnData {
654+
scanArgs[i] = &columnData[i]
655+
}
656+
657+
for rows.Next() {
658+
err = rows.Scan(scanArgs...)
659+
if err != nil {
660+
return errors.New(fmt.Sprintln("Error retrieving rows:", namespace, err))
661+
}
687662

688-
for rows.Next() {
689-
err = rows.Scan(scanArgs...)
690-
if err != nil {
691-
log.Infoln("Error retrieving rows:", namespace, err)
692-
e.error.Set(1)
693-
return
663+
// Get the label values for this row
664+
var labels = make([]string, len(mapping.labels))
665+
for idx, columnName := range mapping.labels {
666+
labels[idx], _ = dbToString(columnData[columnIdx[columnName]])
667+
}
668+
669+
// Loop over column names, and match to scan data. Unknown columns
670+
// will be filled with an untyped metric number *if* they can be
671+
// converted to float64s. NULLs are allowed and treated as NaN.
672+
for idx, columnName := range columnNames {
673+
if metricMapping, ok := mapping.columnMappings[columnName]; ok {
674+
// Is this a metricy metric?
675+
if metricMapping.discard {
676+
continue
694677
}
695678

696-
// Get the label values for this row
697-
var labels = make([]string, len(mapping.labels))
698-
for idx, columnName := range mapping.labels {
679+
value, ok := dbToFloat64(columnData[idx])
680+
if !ok {
681+
log.Errorln("Unexpected error parsing column: ", namespace, columnName, columnData[idx])
682+
continue
683+
}
699684

700-
labels[idx], _ = dbToString(columnData[columnIdx[columnName]])
685+
// Generate the metric
686+
ch <- prometheus.MustNewConstMetric(metricMapping.desc, metricMapping.vtype, value, labels...)
687+
} else {
688+
// Unknown metric. Report as untyped if scan to float64 works, else note an error too.
689+
desc := prometheus.NewDesc(fmt.Sprintf("%s_%s", namespace, columnName), fmt.Sprintf("Unknown metric from %s", namespace), nil, nil)
690+
691+
// Its not an error to fail here, since the values are
692+
// unexpected anyway.
693+
value, ok := dbToFloat64(columnData[idx])
694+
if !ok {
695+
log.Warnln("Unparseable column type - discarding: ", namespace, columnName, err)
696+
continue
701697
}
702698

703-
// Loop over column names, and match to scan data. Unknown columns
704-
// will be filled with an untyped metric number *if* they can be
705-
// converted to float64s. NULLs are allowed and treated as NaN.
706-
for idx, columnName := range columnNames {
707-
if metricMapping, ok := mapping.columnMappings[columnName]; ok {
708-
// Is this a metricy metric?
709-
if metricMapping.discard {
710-
continue
711-
}
699+
ch <- prometheus.MustNewConstMetric(desc, prometheus.UntypedValue, value, labels...)
700+
}
701+
}
702+
}
703+
return nil
704+
}
712705

713-
value, ok := dbToFloat64(columnData[idx])
714-
if !ok {
715-
e.error.Set(1)
716-
log.Errorln("Unexpected error parsing column: ", namespace, columnName, columnData[idx])
717-
continue
718-
}
706+
func (e *Exporter) scrape(ch chan<- prometheus.Metric) {
707+
defer func(begun time.Time) {
708+
e.duration.Set(time.Since(begun).Seconds())
709+
}(time.Now())
719710

720-
// Generate the metric
721-
ch <- prometheus.MustNewConstMetric(metricMapping.desc, metricMapping.vtype, value, labels...)
722-
} else {
723-
// Unknown metric. Report as untyped if scan to float64 works, else note an error too.
724-
desc := prometheus.NewDesc(fmt.Sprintf("%s_%s", namespace, columnName), fmt.Sprintf("Unknown metric from %s", namespace), nil, nil)
711+
e.error.Set(0)
712+
e.totalScrapes.Inc()
725713

726-
// Its not an error to fail here, since the values are
727-
// unexpected anyway.
728-
value, ok := dbToFloat64(columnData[idx])
729-
if !ok {
730-
log.Warnln("Unparseable column type - discarding: ", namespace, columnName, err)
731-
continue
732-
}
714+
db, err := sql.Open("postgres", e.dsn)
715+
if err != nil {
716+
log.Infoln("Error opening connection to database:", err)
717+
e.error.Set(1)
718+
return
719+
}
720+
defer db.Close()
733721

734-
ch <- prometheus.MustNewConstMetric(desc, prometheus.UntypedValue, value, labels...)
735-
}
736-
}
722+
log.Debugln("Querying Postgres Version")
723+
versionRow := db.QueryRow("SELECT version();")
724+
var versionString string
725+
err = versionRow.Scan(&versionString)
726+
if err != nil {
727+
log.Errorln("Error scanning version string:", err)
728+
e.error.Set(1)
729+
return
730+
}
731+
shortVersion := parseVersion(versionString)
732+
// Output the version as a special metric
733+
versionDesc := prometheus.NewDesc(fmt.Sprintf("%s_%s", namespace, staticLabelName), "Version string as reported by postgres", []string{"version", "short_version"}, nil)
734+
ch <- prometheus.MustNewConstMetric(versionDesc, prometheus.UntypedValue, 1, versionString, shortVersion)
737735

738-
}
739-
}()
736+
// Handle querying the show variables
737+
e.queryShowVariables(ch, db)
738+
739+
for namespace, mapping := range e.metricMap {
740+
log.Debugln("Querying namespace: ", namespace)
741+
err = queryNamespaceMapping(ch, db, namespace, mapping)
742+
if err != nil {
743+
log.Infoln(err)
744+
e.error.Set(1)
745+
}
740746
}
741747
}
742748

0 commit comments

Comments
 (0)