influxdb/prometheus/filter.go

129 lines
2.9 KiB
Go

package prometheus
import (
"fmt"
"sort"
"strings"
"github.com/prometheus/client_golang/prometheus"
dto "github.com/prometheus/client_model/go"
"google.golang.org/protobuf/proto"
)
var _ prometheus.Gatherer = (*Filter)(nil)
// Filter filters the metrics from Gather using Matcher.
type Filter struct {
Gatherer prometheus.Gatherer
Matcher Matcher
}
// Gather filters all metrics to only those that match the Matcher.
func (f *Filter) Gather() ([]*dto.MetricFamily, error) {
mfs, err := f.Gatherer.Gather()
if err != nil {
return nil, err
}
return f.Matcher.Match(mfs), nil
}
// Matcher is used to match families of prometheus metrics.
type Matcher map[string]Labels // family name to label/value
// NewMatcher returns a new matcher.
func NewMatcher() Matcher {
return Matcher{}
}
// Family helps construct match by adding a metric family to match to.
func (m Matcher) Family(name string, lps ...*dto.LabelPair) Matcher {
// prometheus metrics labels are sorted by label name.
sort.Slice(lps, func(i, j int) bool {
return lps[i].GetName() < lps[j].GetName()
})
pairs := &labelPairs{
Label: lps,
}
family, ok := m[name]
if !ok {
family = make(Labels)
}
family[pairs.String()] = true
m[name] = family
return m
}
// Match returns all metric families that match.
func (m Matcher) Match(mfs []*dto.MetricFamily) []*dto.MetricFamily {
if len(mfs) == 0 {
return mfs
}
filteredFamilies := []*dto.MetricFamily{}
for _, mf := range mfs {
labels, ok := m[mf.GetName()]
if !ok {
continue
}
metrics := []*dto.Metric{}
match := false
for _, metric := range mf.Metric {
if labels.Match(metric) {
match = true
metrics = append(metrics, metric)
}
}
if match {
filteredFamilies = append(filteredFamilies, &dto.MetricFamily{
Name: mf.Name,
Help: mf.Help,
Type: mf.Type,
Metric: metrics,
})
}
}
sort.Sort(familySorter(filteredFamilies))
return filteredFamilies
}
// L is used with Family to create a series of label pairs for matching.
func L(name, value string) *dto.LabelPair {
return &dto.LabelPair{
Name: proto.String(name),
Value: proto.String(value),
}
}
// Labels are string representations of a set of prometheus label pairs that
// are used to match to metric.
type Labels map[string]bool
// Match checks if the metric's labels matches this set of labels.
func (ls Labels) Match(metric *dto.Metric) bool {
lp := &labelPairs{metric.Label}
return ls[lp.String()] || ls[""] // match empty string so no labels can be matched.
}
// labelPairs is used to serialize a portion of dto.Metric into a serializable
// string.
type labelPairs struct {
Label []*dto.LabelPair `protobuf:"bytes,1,rep,name=label" json:"label,omitempty"`
}
func (l *labelPairs) Reset() {}
func (l *labelPairs) String() string {
var a []string
for _, lbl := range l.Label {
a = append(a, fmt.Sprintf("label:<%s> ", lbl.String()))
}
return strings.Join(a, "")
}
func (*labelPairs) ProtoMessage() {}