Created
January 2, 2015 17:34
-
-
Save grobie/f75535195750e0dbd45b to your computer and use it in GitHub Desktop.
consul metrics exporter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// consulCollector implements the prometheus.Collector interface. | |
type consulCollector struct { | |
bin string | |
errc chan error | |
metrics map[string]prometheus.Gauge | |
} | |
func newConsulCollector(bin string, errc chan error) prometheus.Collector { | |
return &consulCollector{ | |
bin: bin, | |
errc: errc, | |
metrics: map[string]prometheus.Gauge{}, | |
} | |
} | |
func (c *consulCollector) Collect(metricc chan<- prometheus.Metric) { | |
err := c.updateMetrics() | |
if err != nil { | |
c.errc <- err | |
return | |
} | |
for _, m := range c.metrics { | |
m.Collect(metricc) | |
} | |
} | |
// TODO(alx): Clarify proper usage of Describe and improve error handling. | |
func (c *consulCollector) Describe(descc chan<- *prometheus.Desc) { | |
err := c.updateMetrics() | |
if err != nil { | |
c.errc <- err | |
return | |
} | |
for _, m := range c.metrics { | |
descc <- m.Desc() | |
} | |
} | |
func (c *consulCollector) updateMetrics() error { | |
stats, err := getConsulStats(c.bin) | |
if err != nil { | |
return fmt.Errorf("consul info failed: %s", err) | |
} | |
for name, value := range stats { | |
if _, ok := c.metrics[name]; !ok { | |
c.metrics[name] = prometheus.NewGauge( | |
prometheus.GaugeOpts{ | |
Namespace: "consul", | |
Name: name, | |
Help: fmt.Sprintf("%s from consul info", name), | |
}, | |
) | |
} | |
c.metrics[name].Set(float64(value)) | |
} | |
return nil | |
} | |
type consulStats map[string]int64 | |
func getConsulStats(bin string) (consulStats, error) { | |
info := exec.Command(bin, "info") | |
outPipe, err := info.StdoutPipe() | |
if err != nil { | |
return nil, err | |
} | |
if err := info.Start(); err != nil { | |
return nil, err | |
} | |
stats, err := parseConsulStats(outPipe) | |
if err != nil { | |
return nil, err | |
} | |
if err := info.Wait(); err != nil { | |
return nil, err | |
} | |
return stats, nil | |
} | |
func parseConsulStats(r io.Reader) (consulStats, error) { | |
var ( | |
s = bufio.NewScanner(r) | |
stats = consulStats{} | |
ignoredFields = map[string]struct{}{ | |
"arch": struct{}{}, | |
"os": struct{}{}, | |
"state": struct{}{}, | |
"version": struct{}{}, | |
} | |
ignoredKeys = map[string]struct{}{ | |
"build": struct{}{}, | |
} | |
key string | |
) | |
for s.Scan() { | |
line := strings.TrimSpace(s.Text()) | |
if strings.Contains(line, ":") { | |
key = strings.TrimSuffix(line, ":") | |
} | |
if _, ok := ignoredKeys[key]; ok { | |
continue | |
} | |
if strings.Contains(line, "=") { | |
var ( | |
parts = strings.SplitN(line, "=", 2) | |
field, value = strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]) | |
name = strings.Join([]string{key, field}, "_") | |
) | |
if _, ok := ignoredFields[field]; ok { | |
continue | |
} | |
switch value { | |
case "true": | |
stats[name] = 1 | |
case "false": | |
stats[name] = 0 | |
case "never": | |
stats[name] = 0 | |
default: | |
i, err := strconv.ParseInt(value, 10, 64) | |
if err != nil { | |
return nil, err | |
} | |
stats[name] = i | |
} | |
} | |
} | |
if err := s.Err(); err != nil { | |
return nil, err | |
} | |
return stats, nil | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment