Skip to content

Commit

Permalink
Merge pull request #11 from bloomberg/grepExclude
Browse files Browse the repository at this point in the history
Add exclude and grep functions
  • Loading branch information
Aergonus authored Jan 19, 2018
2 parents a4966a7 + 35e5f94 commit ea3b672
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 0 deletions.
2 changes: 2 additions & 0 deletions docs/graphite.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ averageSeries(seriesLists) series | avg | Stable
consolidateBy(seriesList, func) seriesList | | Stable
diffSeries(seriesLists) series | | Stable
divideSeries(seriesList, dividend, divisor) seriesList| | Stable
exclude(seriesList, pattern) seriesList | | Stable
grep(seriesList, pattern) seriesList | | Stable
groupByTags(seriesList, func, tagList) seriesList | | Stable
maxSeries(seriesList) series | max | Stable
minSeries(seriesList) series | min | Stable
Expand Down
49 changes: 49 additions & 0 deletions expr/func_grep.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package expr

import (
"regexp"

"github.com/grafana/metrictank/api/models"
)

type FuncGrep struct {
in GraphiteFunc
pattern *regexp.Regexp
excludeMatches bool
}

func NewGrep() GraphiteFunc {
return &FuncGrep{excludeMatches: false}
}

func NewExclude() GraphiteFunc {
return &FuncGrep{excludeMatches: true}
}

func (s *FuncGrep) Signature() ([]Arg, []Arg) {
return []Arg{
ArgSeriesList{val: &s.in},
ArgRegex{key: "pattern", val: &s.pattern},
}, []Arg{
ArgSeriesList{},
}
}

func (s *FuncGrep) Context(context Context) Context {
return context
}

func (s *FuncGrep) Exec(cache map[Req][]models.Series) ([]models.Series, error) {
series, err := s.in.Exec(cache)
if err != nil {
return nil, err
}

var outputs []models.Series
for _, serie := range series {
if s.pattern.MatchString(serie.Target) != s.excludeMatches {
outputs = append(outputs, serie)
}
}
return outputs, nil
}
115 changes: 115 additions & 0 deletions expr/func_grep_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package expr

import (
"fmt"
"regexp"
"testing"

"github.com/grafana/metrictank/api/models"
)

func TestGrep(t *testing.T) {
cases := []struct {
pattern string
in []string
matches []string
nonmatches []string
}{
{
"this",
[]string{"series.name.this.ok"},
[]string{"series.name.this.ok"},
[]string{},
},
{
`cpu\d`,
[]string{"series.cpu1.ok", "series.cpu2.ok", "series.cpu.notok", "series.cpu3.ok"},
[]string{"series.cpu1.ok", "series.cpu2.ok", "series.cpu3.ok"},
[]string{"series.cpu.notok"},
},
{
`cpu[02468]`,
[]string{"series.cpu1.ok", "series.cpu2.ok", "series.cpu.notok", "series.cpu3.ok"},
[]string{"series.cpu2.ok"},
[]string{"series.cpu1.ok", "series.cpu.notok", "series.cpu3.ok"},
},
}
for i, c := range cases {
var in []models.Series
for _, name := range c.in {
in = append(in, models.Series{
Target: name,
})
}

{
f := NewGrep()
grep := f.(*FuncGrep)
grep.pattern = regexp.MustCompile(c.pattern)
grep.in = NewMock(in)
checkGrepOutput(t, f, i, c.matches)
}

{
f := NewExclude()
grep := f.(*FuncGrep)
grep.pattern = regexp.MustCompile(c.pattern)
grep.in = NewMock(in)
checkGrepOutput(t, f, i, c.nonmatches)
}
}
}

func checkGrepOutput(t *testing.T, f GraphiteFunc, i int, expected []string) {
got, err := f.Exec(make(map[Req][]models.Series))
if err != nil {
t.Fatalf("case %d: err should be nil. got %q", i, err)
}
if len(got) != len(expected) {
t.Fatalf("case %d: expected %d output series, got %d", i, len(expected), len(got))
}
for i, o := range expected {
g := got[i]
if o != g.Target {
t.Fatalf("case %d: expected target %q, got %q", i, o, g.Target)
}
}
}

func BenchmarkGrep_1(b *testing.B) {
benchmarkGrep(b, 1)
}
func BenchmarkGrep_10(b *testing.B) {
benchmarkGrep(b, 10)
}
func BenchmarkGrep_100(b *testing.B) {
benchmarkGrep(b, 100)
}
func BenchmarkGrep_1000(b *testing.B) {
benchmarkGrep(b, 1000)
}
func BenchmarkGrep_100000(b *testing.B) {
benchmarkGrep(b, 100000)
}

func benchmarkGrep(b *testing.B, numSeries int) {
var input []models.Series
for i := 0; i < numSeries; i++ {
series := models.Series{
Target: fmt.Sprintf("metrictank.stats.env.instance.input.plugin%d.metrics_received.counter32", i),
}
input = append(input, series)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
f := NewGrep()
grep := f.(*FuncGrep)
grep.pattern = regexp.MustCompile("input.plugin[0246].metrics")
grep.in = NewMock(input)
got, err := f.Exec(make(map[Req][]models.Series))
if err != nil {
b.Fatalf("%s", err)
}
results = got
}
}
2 changes: 2 additions & 0 deletions expr/funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ func init() {
"consolidateBy": {NewConsolidateBy, true},
"diffSeries": {NewAggregateConstructor("diff", crossSeriesDiff), true},
"divideSeries": {NewDivideSeries, true},
"exclude": {NewExclude, true},
"grep": {NewGrep, true},
"groupByTags": {NewGroupByTags, true},
"max": {NewAggregateConstructor("max", crossSeriesMax), true},
"maxSeries": {NewAggregateConstructor("max", crossSeriesMax), true},
Expand Down

0 comments on commit ea3b672

Please sign in to comment.