mirror of https://github.com/grafana/loki
feat(logql): Support drop labels in logql pipeline (#7975)
This PR introduces `drop` stage in logql pipeline. Fixes #7870, Fixes #7368pull/8126/head
parent
7a1fcab465
commit
8df5803d9a
@ -0,0 +1,85 @@ |
||||
package log |
||||
|
||||
import ( |
||||
"github.com/grafana/loki/pkg/logqlmodel" |
||||
"github.com/prometheus/prometheus/model/labels" |
||||
) |
||||
|
||||
type DropLabels struct { |
||||
dropLabels []DropLabel |
||||
} |
||||
|
||||
type DropLabel struct { |
||||
Matcher *labels.Matcher |
||||
Name string |
||||
} |
||||
|
||||
func NewDropLabel(matcher *labels.Matcher, name string) DropLabel { |
||||
return DropLabel{ |
||||
Matcher: matcher, |
||||
Name: name, |
||||
} |
||||
} |
||||
|
||||
func NewDropLabels(dl []DropLabel) *DropLabels { |
||||
return &DropLabels{dropLabels: dl} |
||||
} |
||||
|
||||
func (dl *DropLabels) Process(ts int64, line []byte, lbls *LabelsBuilder) ([]byte, bool) { |
||||
for _, dropLabel := range dl.dropLabels { |
||||
if dropLabel.Matcher != nil { |
||||
dropLabelMatches(dropLabel.Matcher, lbls) |
||||
continue |
||||
} |
||||
name := dropLabel.Name |
||||
dropLabelNames(name, lbls) |
||||
} |
||||
return line, true |
||||
} |
||||
|
||||
func (dl *DropLabels) RequiredLabelNames() []string { return []string{} } |
||||
|
||||
func isErrorLabel(name string) bool { |
||||
return name == logqlmodel.ErrorLabel |
||||
} |
||||
|
||||
func isErrorDetailsLabel(name string) bool { |
||||
return name == logqlmodel.ErrorDetailsLabel |
||||
} |
||||
|
||||
func dropLabelNames(name string, lbls *LabelsBuilder) { |
||||
if isErrorLabel(name) { |
||||
lbls.ResetError() |
||||
return |
||||
} |
||||
if isErrorDetailsLabel(name) { |
||||
lbls.ResetErrorDetails() |
||||
return |
||||
} |
||||
if _, ok := lbls.Get(name); ok { |
||||
lbls.Del(name) |
||||
} |
||||
} |
||||
|
||||
func dropLabelMatches(matcher *labels.Matcher, lbls *LabelsBuilder) { |
||||
var value string |
||||
name := matcher.Name |
||||
if isErrorLabel(name) { |
||||
value = lbls.GetErr() |
||||
if matcher.Matches(value) { |
||||
lbls.ResetError() |
||||
} |
||||
return |
||||
} |
||||
if isErrorDetailsLabel(name) { |
||||
value = lbls.GetErrorDetails() |
||||
if matcher.Matches(value) { |
||||
lbls.ResetErrorDetails() |
||||
} |
||||
return |
||||
} |
||||
value, _ = lbls.Get(name) |
||||
if matcher.Matches(value) { |
||||
lbls.Del(name) |
||||
} |
||||
} |
@ -0,0 +1,160 @@ |
||||
package log |
||||
|
||||
import ( |
||||
"sort" |
||||
"testing" |
||||
|
||||
"github.com/prometheus/prometheus/model/labels" |
||||
"github.com/stretchr/testify/require" |
||||
|
||||
"github.com/grafana/loki/pkg/logqlmodel" |
||||
) |
||||
|
||||
func Test_DropLabels(t *testing.T) { |
||||
tests := []struct { |
||||
Name string |
||||
dropLabels []DropLabel |
||||
err string |
||||
errDetails string |
||||
lbs labels.Labels |
||||
want labels.Labels |
||||
}{ |
||||
{ |
||||
"drop by name", |
||||
[]DropLabel{ |
||||
{ |
||||
nil, |
||||
"app", |
||||
}, |
||||
{ |
||||
nil, |
||||
"namespace", |
||||
}, |
||||
}, |
||||
"", |
||||
"", |
||||
labels.Labels{ |
||||
{Name: "app", Value: "foo"}, |
||||
{Name: "namespace", Value: "prod"}, |
||||
{Name: "pod_uuid", Value: "foo"}, |
||||
}, |
||||
labels.Labels{ |
||||
{Name: "pod_uuid", Value: "foo"}, |
||||
}, |
||||
}, |
||||
{ |
||||
"drop by __error__", |
||||
[]DropLabel{ |
||||
{ |
||||
labels.MustNewMatcher(labels.MatchEqual, logqlmodel.ErrorLabel, errJSON), |
||||
"", |
||||
}, |
||||
{ |
||||
nil, |
||||
"__error_details__", |
||||
}, |
||||
}, |
||||
errJSON, |
||||
"json error", |
||||
labels.Labels{ |
||||
{Name: "app", Value: "foo"}, |
||||
{Name: "namespace", Value: "prod"}, |
||||
{Name: "pod_uuid", Value: "foo"}, |
||||
}, |
||||
labels.Labels{ |
||||
{Name: "app", Value: "foo"}, |
||||
{Name: "namespace", Value: "prod"}, |
||||
{Name: "pod_uuid", Value: "foo"}, |
||||
}, |
||||
}, |
||||
{ |
||||
"drop with wrong __error__ value", |
||||
[]DropLabel{ |
||||
{ |
||||
labels.MustNewMatcher(labels.MatchEqual, logqlmodel.ErrorLabel, errLogfmt), |
||||
"", |
||||
}, |
||||
}, |
||||
errJSON, |
||||
"json error", |
||||
labels.Labels{ |
||||
{Name: "app", Value: "foo"}, |
||||
{Name: "namespace", Value: "prod"}, |
||||
{Name: "pod_uuid", Value: "foo"}, |
||||
}, |
||||
labels.Labels{ |
||||
{Name: "app", Value: "foo"}, |
||||
{Name: "namespace", Value: "prod"}, |
||||
{Name: "pod_uuid", Value: "foo"}, |
||||
{Name: logqlmodel.ErrorLabel, Value: errJSON}, |
||||
{Name: logqlmodel.ErrorDetailsLabel, Value: "json error"}, |
||||
}, |
||||
}, |
||||
{ |
||||
"drop by __error_details__", |
||||
[]DropLabel{ |
||||
{ |
||||
labels.MustNewMatcher(labels.MatchRegexp, logqlmodel.ErrorDetailsLabel, "expecting json.*"), |
||||
"", |
||||
}, |
||||
{ |
||||
nil, |
||||
"__error__", |
||||
}, |
||||
}, |
||||
errJSON, |
||||
"expecting json object but it is not", |
||||
labels.Labels{ |
||||
{Name: "app", Value: "foo"}, |
||||
{Name: "namespace", Value: "prod"}, |
||||
{Name: "pod_uuid", Value: "foo"}, |
||||
}, |
||||
labels.Labels{ |
||||
{Name: "app", Value: "foo"}, |
||||
{Name: "namespace", Value: "prod"}, |
||||
{Name: "pod_uuid", Value: "foo"}, |
||||
}, |
||||
}, |
||||
{ |
||||
"drop labels with names and matcher", |
||||
[]DropLabel{ |
||||
{ |
||||
labels.MustNewMatcher(labels.MatchEqual, logqlmodel.ErrorLabel, errJSON), |
||||
"", |
||||
}, |
||||
{ |
||||
nil, |
||||
"__error_details__", |
||||
}, |
||||
{ |
||||
nil, |
||||
"app", |
||||
}, |
||||
{ |
||||
nil, |
||||
"namespace", |
||||
}, |
||||
}, |
||||
errJSON, |
||||
"json error", |
||||
labels.Labels{ |
||||
{Name: "app", Value: "foo"}, |
||||
{Name: "namespace", Value: "prod"}, |
||||
{Name: "pod_uuid", Value: "foo"}, |
||||
}, |
||||
labels.Labels{ |
||||
{Name: "pod_uuid", Value: "foo"}, |
||||
}, |
||||
}, |
||||
} |
||||
for _, tt := range tests { |
||||
dropLabels := NewDropLabels(tt.dropLabels) |
||||
lbls := NewBaseLabelsBuilder().ForLabels(tt.lbs, tt.lbs.Hash()) |
||||
lbls.Reset() |
||||
lbls.SetErr(tt.err) |
||||
lbls.SetErrorDetails(tt.errDetails) |
||||
dropLabels.Process(0, []byte(""), lbls) |
||||
sort.Sort(tt.want) |
||||
require.Equal(t, tt.want, lbls.LabelsResult().Labels()) |
||||
} |
||||
} |
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue