Skip to content

Commit 1fcd300

Browse files
authored
Provide invert (like grep -v) match capability (#509)
1 parent 25272c2 commit 1fcd300

File tree

7 files changed

+76
-47
lines changed

7 files changed

+76
-47
lines changed

processor/cascadingfilterprocessor/README.md

Lines changed: 38 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ of the provided values (either at resource of span level)
4747
(use `s` or `ms` as the suffix to indicate unit)
4848
- `properties: { name_pattern: <regex>`}: selects the span if its operation name matches the provided regular expression
4949

50+
To invert the decision (which is still a subject to rate limiting), additional property can be configured:
51+
- `invert_match: <invert>` (default=`false`): when set to `true`, the opposite decision is selected for the trace. E.g.
52+
if trace matches a given string attribute and `invert_match=true`, then the trace is not selected
53+
5054
## Limiting the number of spans
5155

5256
There are two `spans_per_second` settings. The global one and the policy-one.
@@ -79,39 +83,40 @@ processors:
7983
probabilistic_filtering_ratio: 0.1
8084
policies:
8185
[
82-
{
83-
name: test-policy-1,
84-
},
85-
{
86-
name: test-policy-2,
87-
numeric_attribute: {key: key1, min_value: 50, max_value: 100}
88-
},
89-
{
90-
name: test-policy-3,
91-
string_attribute: {key: key2, values: [value1, value2]}
92-
},
93-
{
94-
name: test-policy-4,
95-
spans_per_second: 35,
96-
},
97-
{
98-
name: test-policy-5,
99-
spans_per_second: 123,
100-
numeric_attribute: {key: key1, min_value: 50, max_value: 100}
101-
},
102-
{
103-
name: test-policy-6,
104-
spans_per_second: 50,
105-
properties: {min_duration: 9s }
106-
},
107-
{
108-
name: test-policy-7,
109-
properties: {
110-
name_pattern: "foo.*",
111-
min_number_of_spans: 10,
112-
min_duration: 9s
113-
}
114-
},
86+
{
87+
name: test-policy-1,
88+
},
89+
{
90+
name: test-policy-2,
91+
numeric_attribute: { key: key1, min_value: 50, max_value: 100 }
92+
},
93+
{
94+
name: test-policy-3,
95+
string_attribute: { key: key2, values: [ value1, value2 ] }
96+
},
97+
{
98+
name: test-policy-4,
99+
spans_per_second: 35,
100+
},
101+
{
102+
name: test-policy-5,
103+
spans_per_second: 123,
104+
numeric_attribute: { key: key1, min_value: 50, max_value: 100 },
105+
invert_match: true
106+
},
107+
{
108+
name: test-policy-6,
109+
spans_per_second: 50,
110+
properties: { min_duration: 9s }
111+
},
112+
{
113+
name: test-policy-7,
114+
properties: {
115+
name_pattern: "foo.*",
116+
min_number_of_spans: 10,
117+
min_duration: 9s
118+
}
119+
},
115120
{
116121
name: everything_else,
117122
spans_per_second: -1

processor/cascadingfilterprocessor/config/config.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ type PolicyCfg struct {
3232
PropertiesCfg PropertiesCfg `mapstructure:"properties"`
3333
// SpansPerSecond specifies the rule budget that should never be exceeded for it
3434
SpansPerSecond int64 `mapstructure:"spans_per_second"`
35+
// InvertMatch specifies if the match should be inverted. Default: false
36+
InvertMatch bool `mapstructure:"invert_match"`
3537
}
3638

3739
// PropertiesCfg holds the configurable settings to create a duration filter

processor/cascadingfilterprocessor/config_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ func TestLoadConfig(t *testing.T) {
7676
SpansPerSecond: 123,
7777
NumericAttributeCfg: &config.NumericAttributeCfg{
7878
Key: "key1", MinValue: 50, MaxValue: 100},
79+
InvertMatch: true,
7980
},
8081
{
8182
Name: "test-policy-6",

processor/cascadingfilterprocessor/sampling/policy_factory.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ type policyEvaluator struct {
4646
maxSpansPerSecond int64
4747
spansInCurrentSecond int64
4848

49+
invertMatch bool
50+
4951
logger *zap.Logger
5052
}
5153

@@ -124,5 +126,6 @@ func NewFilter(logger *zap.Logger, cfg *config.PolicyCfg) (PolicyEvaluator, erro
124126
currentSecond: 0,
125127
spansInCurrentSecond: 0,
126128
maxSpansPerSecond: cfg.SpansPerSecond,
129+
invertMatch: cfg.InvertMatch,
127130
}, nil
128131
}

processor/cascadingfilterprocessor/sampling/policy_filter.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,9 +156,15 @@ func (pe *policyEvaluator) evaluateRules(_ pdata.TraceID, trace *TraceData) (Dec
156156
conditionMet.operationName &&
157157
conditionMet.numericAttr &&
158158
conditionMet.stringAttr {
159+
if pe.invertMatch {
160+
return NotSampled, nil
161+
}
159162
return Sampled, nil
160163
}
161164

165+
if pe.invertMatch {
166+
return Sampled, nil
167+
}
162168
return NotSampled, nil
163169
}
164170

processor/cascadingfilterprocessor/sampling/span_properties_filter_test.go

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,13 @@ func newSpanPropertiesFilter(operationNamePattern *string, minDuration *time.Dur
4646
}, nil
4747
}
4848

49+
func evaluate(t *testing.T, evaluator policyEvaluator, traces *TraceData, expectedDecision Decision) {
50+
u, _ := uuid.NewRandom()
51+
decision, err := evaluator.Evaluate(pdata.NewTraceID(u), traces)
52+
assert.NoError(t, err)
53+
assert.Equal(t, expectedDecision, decision)
54+
}
55+
4956
func TestPartialSpanPropertiesFilter(t *testing.T) {
5057
opFilter, _ := newSpanPropertiesFilter(&operationNamePattern, nil, nil)
5158
durationFilter, _ := newSpanPropertiesFilter(nil, &minDuration, nil)
@@ -74,15 +81,13 @@ func TestPartialSpanPropertiesFilter(t *testing.T) {
7481

7582
for _, c := range cases {
7683
t.Run(c.Desc, func(t *testing.T) {
77-
u, _ := uuid.NewRandom()
78-
decision, err := c.Evaluator.Evaluate(pdata.NewTraceID(u), matchingTraces)
79-
assert.NoError(t, err)
80-
assert.Equal(t, decision, Sampled)
81-
82-
u, _ = uuid.NewRandom()
83-
decision, err = c.Evaluator.Evaluate(pdata.NewTraceID(u), nonMatchingTraces)
84-
assert.NoError(t, err)
85-
assert.Equal(t, decision, NotSampled)
84+
c.Evaluator.invertMatch = false
85+
evaluate(t, c.Evaluator, matchingTraces, Sampled)
86+
evaluate(t, c.Evaluator, nonMatchingTraces, NotSampled)
87+
88+
c.Evaluator.invertMatch = true
89+
evaluate(t, c.Evaluator, matchingTraces, NotSampled)
90+
evaluate(t, c.Evaluator, nonMatchingTraces, Sampled)
8691
})
8792
}
8893
}
@@ -117,11 +122,17 @@ func TestSpanPropertiesFilter(t *testing.T) {
117122

118123
for _, c := range cases {
119124
t.Run(c.Desc, func(t *testing.T) {
125+
// Regular match
120126
filter, _ := newSpanPropertiesFilter(&operationNamePattern, &minDuration, &minNumberOfSpans)
121-
u, _ := uuid.NewRandom()
122-
decision, err := filter.Evaluate(pdata.NewTraceID(u), c.Trace)
123-
assert.NoError(t, err)
124-
assert.Equal(t, decision, c.Decision)
127+
evaluate(t, filter, c.Trace, c.Decision)
128+
129+
// Invert match
130+
filter.invertMatch = true
131+
invertDecision := Sampled
132+
if c.Decision == Sampled {
133+
invertDecision = NotSampled
134+
}
135+
evaluate(t, filter, c.Trace, invertDecision)
125136
})
126137
}
127138
}

processor/cascadingfilterprocessor/testdata/cascading_filter_config.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ processors:
3131
{
3232
name: test-policy-5,
3333
spans_per_second: 123,
34-
numeric_attribute: {key: key1, min_value: 50, max_value: 100}
34+
numeric_attribute: {key: key1, min_value: 50, max_value: 100},
35+
invert_match: true
3536
},
3637
{
3738
name: test-policy-6,

0 commit comments

Comments
 (0)