-
Notifications
You must be signed in to change notification settings - Fork 3.6k
/
filter.go
118 lines (98 loc) · 2.82 KB
/
filter.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
package prometheus
import (
"sort"
proto "github.com/golang/protobuf/proto"
"github.com/prometheus/client_golang/prometheus"
dto "github.com/prometheus/client_model/go"
)
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 constuct 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 { return proto.CompactTextString(l) }
func (*labelPairs) ProtoMessage() {}