feat(prometheus): add label and family transformers

pull/11347/head
Chris Goller 2019-01-21 14:03:06 -06:00
parent 1062a5defa
commit 51ef08a13a
3 changed files with 358 additions and 1 deletions

35
prometheus/sort.go Normal file
View File

@ -0,0 +1,35 @@
package prometheus
import dto "github.com/prometheus/client_model/go"
// labelPairSorter implements sort.Interface. It is used to sort a slice of
// dto.LabelPair pointers.
type labelPairSorter []*dto.LabelPair
func (s labelPairSorter) Len() int {
return len(s)
}
func (s labelPairSorter) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s labelPairSorter) Less(i, j int) bool {
return s[i].GetName() < s[j].GetName()
}
// familySorter implements sort.Interface. It is used to sort a slice of
// dto.MetricFamily pointers.
type familySorter []*dto.MetricFamily
func (s familySorter) Len() int {
return len(s)
}
func (s familySorter) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s familySorter) Less(i, j int) bool {
return s[i].GetName() < s[j].GetName()
}

View File

@ -1,9 +1,90 @@
package prometheus
import dto "github.com/prometheus/client_model/go"
import (
"sort"
dto "github.com/prometheus/client_model/go"
)
// Transformer modifies prometheus metrics families.
type Transformer interface {
// Transform updates the metrics family
Transform(mfs []*dto.MetricFamily) []*dto.MetricFamily
}
var _ Transformer = (*AddLabels)(nil)
// AddLabels adds labels to all metrics. It will overwrite
// the label if it already exists.
type AddLabels struct {
Labels map[string]string
}
// Transform adds labels to the metrics.
func (a *AddLabels) Transform(mfs []*dto.MetricFamily) []*dto.MetricFamily {
for i := range mfs {
for j, m := range mfs[i].Metric {
// Filter out labels to add
labels := m.Label[:0]
for _, l := range m.Label {
if _, ok := a.Labels[l.GetName()]; !ok {
labels = append(labels, l)
}
}
// Add all new labels to the metric
for k, v := range a.Labels {
labels = append(labels, L(k, v))
}
sort.Sort(labelPairSorter(labels))
mfs[i].Metric[j].Label = labels
}
}
return mfs
}
var _ Transformer = (*RemoveLabels)(nil)
// RemoveLabels adds labels to all metrics. It will overwrite
// the label if it already exists.
type RemoveLabels struct {
Labels map[string]struct{}
}
// Transform removes labels from the metrics.
func (r *RemoveLabels) Transform(mfs []*dto.MetricFamily) []*dto.MetricFamily {
for i := range mfs {
for j, m := range mfs[i].Metric {
// Filter out labels
labels := m.Label[:0]
for _, l := range m.Label {
if _, ok := r.Labels[l.GetName()]; !ok {
labels = append(labels, l)
}
}
mfs[i].Metric[j].Label = labels
}
}
return mfs
}
var _ Transformer = (*RenameFamilies)(nil)
// RenameFamilies changes the name of families to another name
type RenameFamilies struct {
FromTo map[string]string
}
// Transform renames metric families names.
func (r *RenameFamilies) Transform(mfs []*dto.MetricFamily) []*dto.MetricFamily {
renamed := mfs[:0]
for _, mf := range mfs {
if to, ok := r.FromTo[mf.GetName()]; ok {
mf.Name = &to
}
renamed = append(renamed, mf)
}
sort.Sort(familySorter(renamed))
return renamed
}

View File

@ -0,0 +1,241 @@
package prometheus_test
import (
"reflect"
"testing"
pr "github.com/influxdata/influxdb/prometheus"
dto "github.com/prometheus/client_model/go"
)
func TestAddLabels_Transform(t *testing.T) {
type fields struct {
Labels map[string]string
}
type args struct {
mfs []*dto.MetricFamily
}
tests := []struct {
name string
fields fields
args args
want []*dto.MetricFamily
}{
{
name: "add label from metric replaces label",
fields: fields{
Labels: map[string]string{
"handler": "influxdb",
},
},
args: args{
mfs: []*dto.MetricFamily{
NewCounter("http_api_requests_total", 10,
pr.L("handler", "platform"),
pr.L("method", "GET"),
pr.L("path", "/api/v2"),
pr.L("status", "2XX"),
),
},
},
want: []*dto.MetricFamily{
NewCounter("http_api_requests_total", 10,
pr.L("handler", "influxdb"),
pr.L("method", "GET"),
pr.L("path", "/api/v2"),
pr.L("status", "2XX"),
),
},
},
{
name: "add label from metric replaces label",
fields: fields{
Labels: map[string]string{
"org": "myorg",
},
},
args: args{
mfs: []*dto.MetricFamily{
NewCounter("http_api_requests_total", 10,
pr.L("handler", "platform"),
pr.L("method", "GET"),
pr.L("path", "/api/v2"),
pr.L("status", "2XX"),
),
},
},
want: []*dto.MetricFamily{
NewCounter("http_api_requests_total", 10,
pr.L("handler", "platform"),
pr.L("method", "GET"),
pr.L("org", "myorg"),
pr.L("path", "/api/v2"),
pr.L("status", "2XX"),
),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
a := &pr.AddLabels{
Labels: tt.fields.Labels,
}
if got := a.Transform(tt.args.mfs); !reflect.DeepEqual(got, tt.want) {
t.Errorf("AddLabels.Transform() = %v, want %v", got, tt.want)
}
})
}
}
func TestRemoveLabels_Transform(t *testing.T) {
type fields struct {
Labels map[string]struct{}
}
type args struct {
mfs []*dto.MetricFamily
}
tests := []struct {
name string
fields fields
args args
want []*dto.MetricFamily
}{
{
name: "remove label from metric",
fields: fields{
Labels: map[string]struct{}{
"handler": struct{}{},
},
},
args: args{
mfs: []*dto.MetricFamily{
NewCounter("http_api_requests_total", 10,
pr.L("handler", "platform"),
pr.L("method", "GET"),
pr.L("path", "/api/v2"),
pr.L("status", "2XX"),
),
},
},
want: []*dto.MetricFamily{
NewCounter("http_api_requests_total", 10,
pr.L("method", "GET"),
pr.L("path", "/api/v2"),
pr.L("status", "2XX"),
),
},
},
{
name: "no match removes no labels",
fields: fields{
Labels: map[string]struct{}{
"handler": struct{}{},
},
},
args: args{
mfs: []*dto.MetricFamily{
NewCounter("http_api_requests_total", 10,
pr.L("method", "GET"),
pr.L("path", "/api/v2"),
pr.L("status", "2XX"),
),
},
},
want: []*dto.MetricFamily{
NewCounter("http_api_requests_total", 10,
pr.L("method", "GET"),
pr.L("path", "/api/v2"),
pr.L("status", "2XX"),
),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &pr.RemoveLabels{
Labels: tt.fields.Labels,
}
if got := r.Transform(tt.args.mfs); !reflect.DeepEqual(got, tt.want) {
t.Errorf("RemoveLabels.Transform() = %v, want %v", got, tt.want)
}
})
}
}
func TestRenameFamilies_Transform(t *testing.T) {
type fields struct {
FromTo map[string]string
}
type args struct {
mfs []*dto.MetricFamily
}
tests := []struct {
name string
fields fields
args args
want []*dto.MetricFamily
}{
{
name: "rename metric family in sort order",
fields: fields{
FromTo: map[string]string{
"http_api_requests_total": "api_requests_total",
},
},
args: args{
mfs: []*dto.MetricFamily{
NewCounter("handler", 10,
pr.L("handler", "platform"),
),
NewCounter("http_api_requests_total", 10,
pr.L("handler", "platform"),
pr.L("method", "GET"),
pr.L("path", "/api/v2"),
pr.L("status", "2XX"),
),
},
},
want: []*dto.MetricFamily{
NewCounter("api_requests_total", 10,
pr.L("handler", "platform"),
pr.L("method", "GET"),
pr.L("path", "/api/v2"),
pr.L("status", "2XX"),
),
NewCounter("handler", 10,
pr.L("handler", "platform"),
),
},
},
{
name: "ignored if not found",
fields: fields{
FromTo: map[string]string{
"http_api_requests_total": "api_requests_total",
},
},
args: args{
mfs: []*dto.MetricFamily{
NewCounter("handler", 10,
pr.L("handler", "platform"),
),
},
},
want: []*dto.MetricFamily{
NewCounter("handler", 10,
pr.L("handler", "platform"),
),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &pr.RenameFamilies{
FromTo: tt.fields.FromTo,
}
if got := r.Transform(tt.args.mfs); !reflect.DeepEqual(got, tt.want) {
t.Errorf("RenameFamilies.Transform() = %v, want %v", got, tt.want)
}
})
}
}