mirror of https://github.com/grafana/loki
impr/logcli: Added label output filters + tests (#563)
* impr/logcli: Added label filters + tests * Address review * udpate readmepull/588/head
parent
0734c9f0a6
commit
0885f64e60
@ -0,0 +1,130 @@ |
||||
package main |
||||
|
||||
import ( |
||||
"reflect" |
||||
"testing" |
||||
|
||||
"github.com/prometheus/prometheus/pkg/labels" |
||||
) |
||||
|
||||
func Test_commonLabels(t *testing.T) { |
||||
type args struct { |
||||
lss []labels.Labels |
||||
} |
||||
tests := []struct { |
||||
name string |
||||
args args |
||||
want labels.Labels |
||||
}{ |
||||
{ |
||||
"Extract common labels source > target", |
||||
args{ |
||||
[]labels.Labels{mustParseLabels(`{foo="bar", bar="foo"}`), mustParseLabels(`{bar="foo", foo="foo", baz="baz"}`)}, |
||||
}, |
||||
mustParseLabels(`{bar="foo"}`), |
||||
}, |
||||
{ |
||||
"Extract common labels source > target", |
||||
args{ |
||||
[]labels.Labels{mustParseLabels(`{foo="bar", bar="foo"}`), mustParseLabels(`{bar="foo", foo="bar", baz="baz"}`)}, |
||||
}, |
||||
mustParseLabels(`{foo="bar", bar="foo"}`), |
||||
}, |
||||
{ |
||||
"Extract common labels source < target", |
||||
args{ |
||||
[]labels.Labels{mustParseLabels(`{foo="bar", bar="foo"}`), mustParseLabels(`{bar="foo"}`)}, |
||||
}, |
||||
mustParseLabels(`{bar="foo"}`), |
||||
}, |
||||
{ |
||||
"Extract common labels source < target no common", |
||||
args{ |
||||
[]labels.Labels{mustParseLabels(`{foo="bar", bar="foo"}`), mustParseLabels(`{fo="bar"}`)}, |
||||
}, |
||||
labels.Labels{}, |
||||
}, |
||||
{ |
||||
"Extract common labels source = target no common", |
||||
args{ |
||||
[]labels.Labels{mustParseLabels(`{foo="bar"}`), mustParseLabels(`{fooo="bar"}`)}, |
||||
}, |
||||
labels.Labels{}, |
||||
}, |
||||
} |
||||
for _, tt := range tests { |
||||
t.Run(tt.name, func(t *testing.T) { |
||||
if got := commonLabels(tt.args.lss); !reflect.DeepEqual(got, tt.want) { |
||||
t.Errorf("commonLabels() = %v, want %v", got, tt.want) |
||||
} |
||||
}) |
||||
} |
||||
} |
||||
|
||||
func Test_subtract(t *testing.T) { |
||||
type args struct { |
||||
a labels.Labels |
||||
b labels.Labels |
||||
} |
||||
tests := []struct { |
||||
name string |
||||
args args |
||||
want labels.Labels |
||||
}{ |
||||
{ |
||||
"Subtract labels source > target", |
||||
args{ |
||||
mustParseLabels(`{foo="bar", bar="foo"}`), |
||||
mustParseLabels(`{bar="foo", foo="foo", baz="baz"}`), |
||||
}, |
||||
mustParseLabels(`{foo="bar"}`), |
||||
}, |
||||
{ |
||||
"Subtract labels source < target", |
||||
args{ |
||||
mustParseLabels(`{foo="bar", bar="foo"}`), |
||||
mustParseLabels(`{bar="foo"}`), |
||||
}, |
||||
mustParseLabels(`{foo="bar"}`), |
||||
}, |
||||
{ |
||||
"Subtract labels source < target no sub", |
||||
args{ |
||||
mustParseLabels(`{foo="bar", bar="foo"}`), |
||||
mustParseLabels(`{fo="bar"}`), |
||||
}, |
||||
mustParseLabels(`{bar="foo", foo="bar"}`), |
||||
}, |
||||
{ |
||||
"Subtract labels source = target no sub", |
||||
args{ |
||||
mustParseLabels(`{foo="bar"}`), |
||||
mustParseLabels(`{fiz="buz"}`), |
||||
}, |
||||
mustParseLabels(`{foo="bar"}`), |
||||
}, |
||||
{ |
||||
"Subtract labels source > target no sub", |
||||
args{ |
||||
mustParseLabels(`{foo="bar"}`), |
||||
mustParseLabels(`{fiz="buz", foo="baz"}`), |
||||
}, |
||||
mustParseLabels(`{foo="bar"}`), |
||||
}, |
||||
{ |
||||
"Subtract labels source > target no sub", |
||||
args{ |
||||
mustParseLabels(`{a="b", foo="bar", baz="baz", fizz="fizz"}`), |
||||
mustParseLabels(`{foo="bar", baz="baz", buzz="buzz", fizz="fizz"}`), |
||||
}, |
||||
mustParseLabels(`{a="b"}`), |
||||
}, |
||||
} |
||||
for _, tt := range tests { |
||||
t.Run(tt.name, func(t *testing.T) { |
||||
if got := subtract(tt.args.a, tt.args.b); !reflect.DeepEqual(got, tt.want) { |
||||
t.Errorf("subtract() = %v, want %v", got, tt.want) |
||||
} |
||||
}) |
||||
} |
||||
} |
||||
@ -0,0 +1,109 @@ |
||||
package main |
||||
|
||||
import ( |
||||
"fmt" |
||||
"log" |
||||
"sort" |
||||
"strings" |
||||
"time" |
||||
|
||||
"github.com/fatih/color" |
||||
"github.com/grafana/loki/pkg/logproto" |
||||
"github.com/prometheus/prometheus/pkg/labels" |
||||
"github.com/prometheus/prometheus/promql" |
||||
) |
||||
|
||||
// print a log entry
|
||||
func printLogEntry(ts time.Time, lbls string, line string) { |
||||
fmt.Println( |
||||
color.BlueString(ts.Format(time.RFC3339)), |
||||
color.RedString(lbls), |
||||
strings.TrimSpace(line), |
||||
) |
||||
} |
||||
|
||||
// add some padding after labels
|
||||
func padLabel(ls labels.Labels, maxLabelsLen int) string { |
||||
labels := ls.String() |
||||
if len(labels) < maxLabelsLen { |
||||
labels += strings.Repeat(" ", maxLabelsLen-len(labels)) |
||||
} |
||||
return labels |
||||
} |
||||
|
||||
// parse labels from string
|
||||
func mustParseLabels(labels string) labels.Labels { |
||||
ls, err := promql.ParseMetric(labels) |
||||
if err != nil { |
||||
log.Fatalf("Failed to parse labels: %+v", err) |
||||
} |
||||
return ls |
||||
} |
||||
|
||||
// parse labels from response stream
|
||||
func parseLabels(resp *logproto.QueryResponse) (map[string]labels.Labels, []labels.Labels) { |
||||
cache := make(map[string]labels.Labels, len(resp.Streams)) |
||||
lss := make([]labels.Labels, 0, len(resp.Streams)) |
||||
for _, stream := range resp.Streams { |
||||
ls := mustParseLabels(stream.Labels) |
||||
cache[stream.Labels] = ls |
||||
lss = append(lss, ls) |
||||
} |
||||
return cache, lss |
||||
} |
||||
|
||||
// return common labels between given lavels set
|
||||
func commonLabels(lss []labels.Labels) labels.Labels { |
||||
if len(lss) == 0 { |
||||
return nil |
||||
} |
||||
|
||||
result := lss[0] |
||||
for i := 1; i < len(lss); i++ { |
||||
result = intersect(result, lss[i]) |
||||
} |
||||
return result |
||||
} |
||||
|
||||
// intersect two labels set
|
||||
func intersect(a, b labels.Labels) labels.Labels { |
||||
|
||||
set := labels.Labels{} |
||||
ma := a.Map() |
||||
mb := b.Map() |
||||
|
||||
for ka, va := range ma { |
||||
if vb, ok := mb[ka]; ok { |
||||
if vb == va { |
||||
set = append(set, labels.Label{ |
||||
Name: ka, |
||||
Value: va, |
||||
}) |
||||
} |
||||
} |
||||
} |
||||
sort.Sort(set) |
||||
return set |
||||
} |
||||
|
||||
// subtract labels set b from labels set a
|
||||
func subtract(a, b labels.Labels) labels.Labels { |
||||
|
||||
set := labels.Labels{} |
||||
ma := a.Map() |
||||
mb := b.Map() |
||||
|
||||
for ka, va := range ma { |
||||
if vb, ok := mb[ka]; ok { |
||||
if vb == va { |
||||
continue |
||||
} |
||||
} |
||||
set = append(set, labels.Label{ |
||||
Name: ka, |
||||
Value: va, |
||||
}) |
||||
} |
||||
sort.Sort(set) |
||||
return set |
||||
} |
||||
Loading…
Reference in new issue