forked from flashcat/categraf
143 lines
3.4 KiB
Go
143 lines
3.4 KiB
Go
// Copyright 2015 Google Inc. All Rights Reserved.
|
|
// This file is available under the Apache license.
|
|
|
|
package exporter
|
|
|
|
import (
|
|
"expvar"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"strings"
|
|
|
|
"flashcat.cloud/categraf/inputs/mtail/internal/metrics"
|
|
"flashcat.cloud/categraf/inputs/mtail/internal/metrics/datum"
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
"github.com/prometheus/common/expfmt"
|
|
)
|
|
|
|
var metricExportTotal = expvar.NewInt("metric_export_total")
|
|
|
|
func noHyphens(s string) string {
|
|
return strings.ReplaceAll(s, "-", "_")
|
|
}
|
|
|
|
// Describe implements the prometheus.Collector interface.
|
|
func (e *Exporter) Describe(c chan<- *prometheus.Desc) {
|
|
prometheus.DescribeByCollect(e, c)
|
|
}
|
|
|
|
// Collect implements the prometheus.Collector interface.
|
|
func (e *Exporter) Collect(c chan<- prometheus.Metric) {
|
|
lastMetric := ""
|
|
lastSource := ""
|
|
|
|
/* #nosec G104 always retursn nil */
|
|
e.store.Range(func(m *metrics.Metric) error {
|
|
m.RLock()
|
|
// We don't have a way of converting text metrics to prometheus format.
|
|
if m.Kind == metrics.Text {
|
|
m.RUnlock()
|
|
return nil
|
|
}
|
|
metricExportTotal.Add(1)
|
|
|
|
lsc := make(chan *metrics.LabelSet)
|
|
go m.EmitLabelSets(lsc)
|
|
for ls := range lsc {
|
|
if lastMetric != m.Name {
|
|
// glog.V(2).Infof("setting source to %s", m.Source)
|
|
lastSource = m.Source
|
|
lastMetric = m.Name
|
|
}
|
|
var keys []string
|
|
var vals []string
|
|
if !e.omitProgLabel {
|
|
keys = append(keys, "prog")
|
|
vals = append(vals, m.Program)
|
|
}
|
|
for k, v := range ls.Labels {
|
|
keys = append(keys, k)
|
|
vals = append(vals, v)
|
|
}
|
|
var pM prometheus.Metric
|
|
var err error
|
|
if m.Kind == metrics.Histogram {
|
|
pM, err = prometheus.NewConstHistogram(
|
|
prometheus.NewDesc(noHyphens(m.Name),
|
|
fmt.Sprintf("defined at %s", lastSource), keys, nil),
|
|
datum.GetBucketsCount(ls.Datum),
|
|
datum.GetBucketsSum(ls.Datum),
|
|
datum.GetBucketsCumByMax(ls.Datum),
|
|
vals...)
|
|
} else {
|
|
pM, err = prometheus.NewConstMetric(
|
|
prometheus.NewDesc(noHyphens(m.Name),
|
|
fmt.Sprintf("defined at %s", lastSource), keys, nil),
|
|
promTypeForKind(m.Kind),
|
|
promValueForDatum(ls.Datum),
|
|
vals...)
|
|
}
|
|
if err != nil {
|
|
log.Println(err)
|
|
return nil
|
|
}
|
|
// By default no timestamp is emitted to Prometheus. Setting a
|
|
// timestamp is not recommended. It can lead to unexpected results
|
|
// if the timestamp is not updated or moved fowarded enough to avoid
|
|
// triggering Promtheus staleness handling.
|
|
// Read more in docs/faq.md
|
|
if e.emitTimestamp {
|
|
c <- prometheus.NewMetricWithTimestamp(ls.Datum.TimeUTC(), pM)
|
|
} else {
|
|
c <- pM
|
|
}
|
|
}
|
|
m.RUnlock()
|
|
return nil
|
|
})
|
|
}
|
|
|
|
// Write is used to write Prometheus metrics to an io.Writer.
|
|
func (e *Exporter) Write(w io.Writer) error {
|
|
reg := prometheus.NewRegistry()
|
|
err := reg.Register(e)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
mfs, err := reg.Gather()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
enc := expfmt.NewEncoder(w, expfmt.FmtText)
|
|
for _, mf := range mfs {
|
|
err := enc.Encode(mf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func promTypeForKind(k metrics.Kind) prometheus.ValueType {
|
|
switch k {
|
|
case metrics.Counter:
|
|
return prometheus.CounterValue
|
|
case metrics.Gauge:
|
|
return prometheus.GaugeValue
|
|
case metrics.Timer:
|
|
return prometheus.GaugeValue
|
|
}
|
|
return prometheus.UntypedValue
|
|
}
|
|
|
|
func promValueForDatum(d datum.Datum) float64 {
|
|
switch n := d.(type) {
|
|
case *datum.Int:
|
|
return float64(n.Get())
|
|
case *datum.Float:
|
|
return n.Get()
|
|
}
|
|
return 0.
|
|
}
|