Skip to content

Commit 8dad694

Browse files
authored
feat(filter): add exclude filter support to EventFilter (#760)
* feat(filter): add exclude filter support to EventFilter Add excludeEventNames and excludeModules to EventFilterConfig, enabling users to exclude specific events/modules without enumerating all others. Include and exclude for the same dimension are mutually exclusive. * fix(filter): add module name validation and zero event name test Validate Modules and ExcludeModules entries against ModuleName_value to catch typos at config load time. Add test for zero event name under exclude-only filters to lock in the drop-with-error behavior.
1 parent 0bcf609 commit 8dad694

3 files changed

Lines changed: 600 additions & 40 deletions

File tree

pkg/proto/xatu/filter.go

Lines changed: 121 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -6,69 +6,124 @@ import (
66
"github.com/pkg/errors"
77
)
88

9+
// EventFilter defines the interface for filtering decorated events.
910
type EventFilter interface {
10-
// EventNames returns the list of event names to filter on.
11+
// EventNames returns the list of event names to include.
1112
EventNames() []string
12-
// Modules returns the list of modules to filter on.
13+
// ExcludeEventNames returns the list of event names to exclude.
14+
ExcludeEventNames() []string
15+
// Modules returns the list of modules to include.
1316
Modules() []string
17+
// ExcludeModules returns the list of modules to exclude.
18+
ExcludeModules() []string
1419
// ShouldBeDropped returns true if the event should be dropped.
1520
ShouldBeDropped(event *DecoratedEvent) (bool, error)
1621
}
1722

23+
// EventFilterConfig holds configuration for event filtering.
1824
type EventFilterConfig struct {
19-
EventNames []string `yaml:"eventNames"`
20-
Modules []string `yaml:"modules"`
25+
EventNames []string `yaml:"eventNames"`
26+
ExcludeEventNames []string `yaml:"excludeEventNames"`
27+
Modules []string `yaml:"modules"`
28+
ExcludeModules []string `yaml:"excludeModules"`
2129
}
2230

23-
func (f *EventFilterConfig) Validate() error {
24-
for _, eventName := range f.EventNames {
25-
if _, ok := Event_Name_value[eventName]; !ok {
26-
return fmt.Errorf("invalid event name: %s", eventName)
27-
}
28-
}
31+
type eventFilter struct {
32+
config *EventFilterConfig
2933

30-
return nil
34+
eventNames map[string]struct{}
35+
excludeEventNames map[string]struct{}
36+
modules map[string]struct{}
37+
excludeModules map[string]struct{}
3138
}
3239

40+
// NewEventFilter creates a new EventFilter from the given config.
3341
func NewEventFilter(config *EventFilterConfig) (EventFilter, error) {
3442
if err := config.Validate(); err != nil {
3543
return nil, errors.Wrap(err, "invalid event filter config")
3644
}
3745

3846
eventNames := make(map[string]struct{}, len(config.EventNames))
39-
4047
for _, eventName := range config.EventNames {
4148
eventNames[eventName] = struct{}{}
4249
}
4350

44-
modules := make(map[string]struct{}, len(config.Modules))
51+
excludeEventNames := make(map[string]struct{}, len(config.ExcludeEventNames))
52+
for _, eventName := range config.ExcludeEventNames {
53+
excludeEventNames[eventName] = struct{}{}
54+
}
4555

56+
modules := make(map[string]struct{}, len(config.Modules))
4657
for _, module := range config.Modules {
4758
modules[module] = struct{}{}
4859
}
4960

61+
excludeModules := make(map[string]struct{}, len(config.ExcludeModules))
62+
for _, module := range config.ExcludeModules {
63+
excludeModules[module] = struct{}{}
64+
}
65+
5066
return &eventFilter{
51-
config: config,
52-
eventNames: eventNames,
53-
modules: modules,
67+
config: config,
68+
eventNames: eventNames,
69+
excludeEventNames: excludeEventNames,
70+
modules: modules,
71+
excludeModules: excludeModules,
5472
}, nil
5573
}
5674

57-
type eventFilter struct {
58-
config *EventFilterConfig
75+
func (f *EventFilterConfig) Validate() error {
76+
for _, eventName := range f.EventNames {
77+
if _, ok := Event_Name_value[eventName]; !ok {
78+
return fmt.Errorf("invalid event name: %s", eventName)
79+
}
80+
}
81+
82+
for _, eventName := range f.ExcludeEventNames {
83+
if _, ok := Event_Name_value[eventName]; !ok {
84+
return fmt.Errorf("invalid exclude event name: %s", eventName)
85+
}
86+
}
87+
88+
for _, module := range f.Modules {
89+
if _, ok := ModuleName_value[module]; !ok {
90+
return fmt.Errorf("invalid module name: %s", module)
91+
}
92+
}
93+
94+
for _, module := range f.ExcludeModules {
95+
if _, ok := ModuleName_value[module]; !ok {
96+
return fmt.Errorf("invalid exclude module name: %s", module)
97+
}
98+
}
99+
100+
if len(f.EventNames) > 0 && len(f.ExcludeEventNames) > 0 {
101+
return fmt.Errorf("eventNames and excludeEventNames are mutually exclusive")
102+
}
103+
104+
if len(f.Modules) > 0 && len(f.ExcludeModules) > 0 {
105+
return fmt.Errorf("modules and excludeModules are mutually exclusive")
106+
}
59107

60-
eventNames map[string]struct{}
61-
modules map[string]struct{}
108+
return nil
62109
}
63110

64111
func (f *eventFilter) EventNames() []string {
65112
return f.config.EventNames
66113
}
67114

115+
func (f *eventFilter) ExcludeEventNames() []string {
116+
return f.config.ExcludeEventNames
117+
}
118+
68119
func (f *eventFilter) Modules() []string {
69120
return f.config.Modules
70121
}
71122

123+
func (f *eventFilter) ExcludeModules() []string {
124+
return f.config.ExcludeModules
125+
}
126+
72127
func (f *eventFilter) ShouldBeDropped(event *DecoratedEvent) (bool, error) {
73128
if event == nil {
74129
return true, errors.New("event is nil")
@@ -78,11 +133,16 @@ func (f *eventFilter) ShouldBeDropped(event *DecoratedEvent) (bool, error) {
78133
return true, errors.New("event.event is nil")
79134
}
80135

81-
if len(f.eventNames) == 0 && len(f.modules) == 0 {
136+
hasAnyFilter := len(f.eventNames) > 0 ||
137+
len(f.excludeEventNames) > 0 ||
138+
len(f.modules) > 0 ||
139+
len(f.excludeModules) > 0
140+
141+
if !hasAnyFilter {
82142
return false, nil
83143
}
84144

85-
if len(f.eventNames) > 0 {
145+
if len(f.eventNames) > 0 || len(f.excludeEventNames) > 0 {
86146
shouldDrop, err := f.shouldDropFromEventNames(event)
87147
if err != nil {
88148
return true, errors.Wrap(err, "failed to apply event names filter")
@@ -93,7 +153,7 @@ func (f *eventFilter) ShouldBeDropped(event *DecoratedEvent) (bool, error) {
93153
}
94154
}
95155

96-
if len(f.modules) > 0 {
156+
if len(f.modules) > 0 || len(f.excludeModules) > 0 {
97157
shouldDrop, err := f.shouldDropFromModules(event)
98158
if err != nil {
99159
return true, errors.Wrap(err, "failed to apply modules filter")
@@ -108,31 +168,54 @@ func (f *eventFilter) ShouldBeDropped(event *DecoratedEvent) (bool, error) {
108168
}
109169

110170
func (f *eventFilter) shouldDropFromEventNames(event *DecoratedEvent) (bool, error) {
111-
if len(f.eventNames) == 0 {
112-
return false, nil
113-
}
114-
115171
if event.Event.Name == 0 {
116172
return true, errors.New("event.event.name is invalid")
117173
}
118174

119-
_, ok := f.eventNames[event.Event.Name.String()]
175+
name := event.Event.Name.String()
176+
177+
// Include list: drop if NOT in list.
178+
if len(f.eventNames) > 0 {
179+
_, ok := f.eventNames[name]
180+
181+
return !ok, nil
182+
}
183+
184+
// Exclude list: drop if IN list.
185+
if len(f.excludeEventNames) > 0 {
186+
_, ok := f.excludeEventNames[name]
120187

121-
return !ok, nil
188+
return ok, nil
189+
}
190+
191+
return false, nil
122192
}
123193

124194
func (f *eventFilter) shouldDropFromModules(event *DecoratedEvent) (bool, error) {
125-
if len(f.modules) == 0 {
126-
return false, nil
127-
}
195+
moduleName := event.GetMeta().GetClient().GetModuleName()
128196

129-
// If the event has no module set and we're filtering by modules,
130-
// drop the event (it doesn't match any required module).
131-
if event.GetMeta().GetClient().GetModuleName() == 0 {
132-
return true, nil
197+
// Include list: drop if NOT in list.
198+
if len(f.modules) > 0 {
199+
if moduleName == 0 {
200+
return true, nil
201+
}
202+
203+
_, ok := f.modules[moduleName.String()]
204+
205+
return !ok, nil
133206
}
134207

135-
_, ok := f.modules[event.GetMeta().GetClient().GetModuleName().String()]
208+
// Exclude list: drop if IN list.
209+
if len(f.excludeModules) > 0 {
210+
// If the event has no module set, it doesn't match any exclude entry, so keep it.
211+
if moduleName == 0 {
212+
return false, nil
213+
}
214+
215+
_, ok := f.excludeModules[moduleName.String()]
216+
217+
return ok, nil
218+
}
136219

137-
return !ok, nil
220+
return false, nil
138221
}

0 commit comments

Comments
 (0)