Skip to content

Commit 134f36c

Browse files
committed
Merge branch 'prom-client'
Export all metrics, go and process stats, and expvar metrics with the Prometheus exposition format. Fixes #174
2 parents dbe5fe0 + aed96b0 commit 134f36c

File tree

4 files changed

+90
-273
lines changed

4 files changed

+90
-273
lines changed

TODO

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,5 @@ go list -f "{{if not .Standard}}{{.ImportPath}}{{end}}" $(go list -f '{{join .De
6464
Request joining (request coalescing) for the log-watcher when sending updates about files, in case
6565
one's already in the queue. Maybe helps with fsnotify overflow, and the
6666
duplicate notify problem from poll-only mode?
67+
68+
Programs may not use mtail_ as a metric prefix.

internal/exporter/prometheus.go

Lines changed: 52 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -6,35 +6,34 @@ package exporter
66
import (
77
"expvar"
88
"fmt"
9-
"net/http"
10-
"sort"
119
"strings"
1210

11+
"github.com/golang/glog"
1312
"github.com/google/mtail/internal/metrics"
13+
"github.com/google/mtail/internal/metrics/datum"
14+
15+
"github.com/prometheus/client_golang/prometheus"
1416
)
1517

1618
var (
1719
metricExportTotal = expvar.NewInt("metric_export_total")
1820
)
1921

20-
const (
21-
prometheusFormat = "%s{%s} %s\n"
22-
)
23-
2422
func noHyphens(s string) string {
2523
return strings.Replace(s, "-", "_", -1)
2624
}
2725

28-
// HandlePrometheusMetrics exports the metrics in a format readable by
29-
// Prometheus via HTTP.
30-
func (e *Exporter) HandlePrometheusMetrics(w http.ResponseWriter, r *http.Request) {
26+
// Describe implements the prometheus.Collector interface.
27+
func (e *Exporter) Describe(c chan<- *prometheus.Desc) {
28+
prometheus.DescribeByCollect(e, c)
29+
}
30+
31+
// Collect implements the prometheus.Collector interface.
32+
func (e *Exporter) Collect(c chan<- prometheus.Metric) {
3133
e.store.RLock()
3234
defer e.store.RUnlock()
3335

34-
w.Header().Add("Content-type", "text/plain; version=0.0.4")
35-
3636
for _, ml := range e.store.Metrics {
37-
emittype := true
3837
lastSource := ""
3938
for _, m := range ml {
4039
m.RLock()
@@ -45,49 +44,57 @@ func (e *Exporter) HandlePrometheusMetrics(w http.ResponseWriter, r *http.Reques
4544
}
4645
metricExportTotal.Add(1)
4746

48-
if emittype {
49-
fmt.Fprintf(w,
50-
"# TYPE %s %s\n",
51-
noHyphens(m.Name),
52-
kindToPrometheusType(m.Kind))
53-
emittype = false
54-
}
55-
56-
lc := make(chan *metrics.LabelSet)
57-
go m.EmitLabelSets(lc)
58-
for l := range lc {
59-
if m.Source != "" && m.Source != lastSource {
60-
fmt.Fprintf(w, "# %s defined at %s\n", noHyphens(m.Name), m.Source)
61-
// suppress redundant source comments
47+
lsc := make(chan *metrics.LabelSet)
48+
go m.EmitLabelSets(lsc)
49+
for ls := range lsc {
50+
if lastSource == "" {
6251
lastSource = m.Source
6352
}
64-
line := metricToPrometheus(m, l, e.omitProgLabel)
65-
fmt.Fprint(w, line)
53+
var keys []string
54+
var vals []string
55+
if !e.omitProgLabel {
56+
keys = append(keys, "prog")
57+
vals = append(vals, m.Program)
58+
}
59+
for k, v := range ls.Labels {
60+
keys = append(keys, k)
61+
vals = append(vals, v)
62+
}
63+
pM, err := prometheus.NewConstMetric(
64+
prometheus.NewDesc(noHyphens(m.Name),
65+
fmt.Sprintf("defined at %s", lastSource), keys, nil),
66+
promTypeForKind(m.Kind),
67+
promValueForDatum(ls.Datum),
68+
vals...)
69+
if err != nil {
70+
glog.Warning(err)
71+
continue
72+
}
73+
c <- prometheus.NewMetricWithTimestamp(ls.Datum.TimeUTC(), pM)
6674
}
6775
m.RUnlock()
6876
}
6977
}
7078
}
7179

72-
func metricToPrometheus(m *metrics.Metric, l *metrics.LabelSet, omitProgLabel bool) string {
73-
s := make([]string, 0, len(l.Labels)+1)
74-
for k, v := range l.Labels {
75-
// Prometheus quotes the value of each label=value pair.
76-
s = append(s, fmt.Sprintf("%s=%q", k, v))
77-
}
78-
sort.Strings(s)
79-
if !omitProgLabel {
80-
s = append(s, fmt.Sprintf("prog=\"%s\"", m.Program))
80+
func promTypeForKind(k metrics.Kind) prometheus.ValueType {
81+
switch k {
82+
case metrics.Counter:
83+
return prometheus.CounterValue
84+
case metrics.Gauge:
85+
return prometheus.GaugeValue
86+
case metrics.Timer:
87+
return prometheus.GaugeValue
8188
}
82-
return fmt.Sprintf(prometheusFormat,
83-
noHyphens(m.Name),
84-
strings.Join(s, ","),
85-
l.Datum.ValueString())
89+
return prometheus.UntypedValue
8690
}
8791

88-
func kindToPrometheusType(kind metrics.Kind) string {
89-
if kind != metrics.Timer {
90-
return strings.ToLower(kind.String())
92+
func promValueForDatum(d datum.Datum) float64 {
93+
switch n := d.(type) {
94+
case *datum.IntDatum:
95+
return float64(n.Get())
96+
case *datum.FloatDatum:
97+
return n.Get()
9198
}
92-
return "gauge"
99+
return 0.
93100
}

internal/exporter/prometheus_test.go

Lines changed: 0 additions & 224 deletions
This file was deleted.

0 commit comments

Comments
 (0)