Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add OR/AND/NOT in the condition associated with the processors #1983

Merged
merged 1 commit into from
Jul 11, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ https://github.com/elastic/beats/compare/v5.0.0-alpha4...master[Check the HEAD d
- Periodically log internal metrics. {pull}1955[1955]
- Add enable-setting to all output modules. {pull}1987[1987]
- Command line flag -c can be used multiple times. {pull}1985[1985]
- Add OR/AND/NOT to the condition associated with the processors. {pull}1983[1983]

*Metricbeat*

Expand Down
27 changes: 27 additions & 0 deletions filebeat/tests/system/test_processors.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,30 @@ def test_drop_event(self):
assert "beat.name" in output
assert "message" in output
assert "test" in output["message"]

def test_condition(self):
"""
Check condition in processors
"""
self.render_config_template(
path=os.path.abspath(self.working_dir) + "/test*.log",
drop_event={
"condition": "not.contains.source: test",
},
)
with open(self.working_dir + "/test1.log", "w") as f:
f.write("test1 message\n")

with open(self.working_dir + "/test2.log", "w") as f:
f.write("test2 message\n")

filebeat = self.start_beat()
self.wait_until(lambda: self.output_has(lines=2))
filebeat.check_kill_and_wait()

output = self.read_output(
required_fields=["@timestamp", "type"],
)[0]
assert "beat.name" in output
assert "message" in output
assert "test" in output["message"]
155 changes: 117 additions & 38 deletions libbeat/docs/processors-config.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -15,36 +15,23 @@

//TODO: Remove was Filters from the above title and remove extra sections that show the alpha4 configuration

coming[5.0.0-beta1,Filters are being renamed to "processors" to better reflect the capabilities they provide. If you are using 5.0.0-alpha4 or earlier, these are still called "filters" even though the documentation refers to "processors"]
include::../../libbeat/docs/processors.asciidoc[]

You can define a set of `processors` in the +{beatname_lc}.yml+ config file to reduce the number
of fields that are exported by the Beat.

If multiple processors are defined, they are executed in the order they are defined. The initial event is passed to the
first processor and what results from it is passed to the second processor until all processors are applied. The
condition is checked against the event that is received as input and it might differ from the original event.

[source,yaml]
-------
event -> processor 1 -> event1 -> processor 2 -> event2 ...
-------

See <<exported-fields>> for the full list of possible fields.

Each processor receives a condition and optionally a set of arguments. The action is executed only if the condition
Each processor has associated an action with a set of parameters and optionally a condition. If the condition is
present, then the action is executed only if the condition
is fulfilled. If no condition is passed then the action is always executed.

deprecated[5.0.0-alpha4,The `filters` section is being renamed to `processors` in 5.0.0-beta1. Therefore the following configuration is deprecated]
deprecated[5.0.0-alpha4,The `filters` configuration option is being renamed to `processors` in 5.0.0-beta1. Therefore the following configuration is deprecated]

[source,yaml]
------
filters:
- action1:
condition1
[arguments]
- action2:
condition2
[arguments]
- <action>:
<condition>
<parameters>
- <action>:
<condition>
<parameters>
...
------

Expand All @@ -53,18 +40,21 @@ coming[5.0.0-beta1,Begin using the following configuration starting with 5.0.0-b
[source,yaml]
------
processors:
- action1:
- <action>:
when:
condition1
[arguments]
- action2:
<condition>
<parameters>
- <action>:
when:
condition2
[arguments]
<condition>
<parameters>
...

------

where <action> can be a way to select the fields that are exported or a way to add meta data to the event , <condition> contains the definition of the condition.
and <parameters> is the list of parameters passed along the <action>.

See <<filtering-and-enhancing-data>> for specific {beatname_uc} examples.

[[filtering-condition]]
Expand All @@ -75,20 +65,17 @@ them. You can see a list of the <<exported-fields,`exported fields`>>.

For each field, you can specify a simple field name or a nested map, for example `dns.question.name`.

[source,yaml]
----
condition:
field1: value1
[field2: value2]
...
----

Supported conditions are:
A condition can be:

* <<condition-equals,`equals`>>
* <<condition-contains,`contains`>>
* <<condition-regexp,`regexp`>>
* <<condition-range, `range`>>
* <<condition-or, `or`>>
* <<condition-and, `and`>>
* <<condition-not, `not`>>



[[condition-equals]]
Expand Down Expand Up @@ -126,7 +113,7 @@ The `regexp` condition checks the field against a regular expression. The condit

For example, the following condition checks if the process name contains `foo`:

[source,yaml]]
[source,yaml]
-----
reqexp:
proc.name: "foo.*"
Expand Down Expand Up @@ -162,6 +149,97 @@ range:
cpu.user_p: 0.8
------


coming[5.0.0-beta1, You can combine multiple conditions with the `or`, `and` or `not` operators]

[[condition-or]]
===== OR

The `or` operator receives a list of conditions.

[source,yaml]
-------
or:
- <condition1>
- <condition2>
- <condition3>
...

-------

For example the condition `http.code = 304 OR http.code = 404` translates to:

[source,yaml]
------
or:
- equals:
http.code: 304
- equals:
http.code: 404
------


[[condition-and]]
===== AND

The `and` operator receives a list of conditions.

[source,yaml]
-------
and:
- <condition1>
- <condition2>
- <condition3>
...

-------

For example the condition `http.code = 200 AND status = OK` translates to:

[source,yaml]
------
and:
- equals:
http.code: 200
- equals:
status: OK
------

To configure a condition like `<condition1> OR <condition2> AND <condition3>`:

[source,yaml]
------
or:
- <condition1>
- and:
- <condition2>
- <condition3>

------

[[condition-not]]
===== NOT

The `not` operator receives the condition to negate.

[source,yaml]
-------
not:
<condition>

-------

For example the condition `NOT status = OK` translates to:

[source,yaml]
------
not:
equals:
status: OK
------



==== Actions

The supported filter actions are:
Expand All @@ -170,6 +248,7 @@ The supported filter actions are:
* <<drop-fields,`drop_fields`>>
* <<drop-event,`drop_event`>>

See <<exported-fields>> for the full list of possible fields.

[[include-fields]]
===== include_fields
Expand Down
5 changes: 5 additions & 0 deletions libbeat/docs/processors.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,9 @@ enhancing events with additional metadata. Each processor receives an event, app
and returns the event. If you define a list of processors, they are executed in the order they are defined in the
configuration file.

[source,yaml]
-------
event -> processor 1 -> event1 -> processor 2 -> event2 ...
-------

The processors are defined in the {beatname_uc} configuration file.
82 changes: 82 additions & 0 deletions libbeat/processors/condition.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ type Condition struct {
contains map[string]string
regexp map[string]*regexp.Regexp
rangexp map[string]RangeValue
or []Condition
and []Condition
not *Condition
}

func NewCondition(config *ConditionConfig) (*Condition, error) {
Expand Down Expand Up @@ -55,10 +58,33 @@ func NewCondition(config *ConditionConfig) (*Condition, error) {
if err := c.setRange(config.Range); err != nil {
return nil, err
}
} else if len(config.OR) > 0 {
for _, cond_config := range config.OR {
cond, err := NewCondition(&cond_config)
if err != nil {
return nil, err
}
c.or = append(c.or, *cond)
}
} else if len(config.AND) > 0 {
for _, cond_config := range config.AND {
cond, err := NewCondition(&cond_config)
if err != nil {
return nil, err
}
c.and = append(c.and, *cond)
}
} else if config.NOT != nil {
cond, err := NewCondition(config.NOT)
if err != nil {
return nil, err
}
c.not = cond
} else {
return nil, fmt.Errorf("missing condition")
}

logp.Debug("processors", "New condition %s", c)
return &c, nil
}

Expand Down Expand Up @@ -166,6 +192,18 @@ func (c *Condition) setRange(cfg *ConditionFields) error {

func (c *Condition) Check(event common.MapStr) bool {

if len(c.or) > 0 {
return c.checkOR(event)
}

if len(c.and) > 0 {
return c.checkAND(event)
}

if c.not != nil {
return c.checkNOT(event)
}

if !c.checkEquals(event) {
return false
}
Expand Down Expand Up @@ -323,6 +361,34 @@ func (c *Condition) checkRange(event common.MapStr) bool {
return true
}

func (c *Condition) checkOR(event common.MapStr) bool {

for _, cond := range c.or {
if cond.Check(event) {
return true
}
}
return false
}

func (c *Condition) checkAND(event common.MapStr) bool {

for _, cond := range c.and {
if !cond.Check(event) {
return false
}
}
return true
}

func (c *Condition) checkNOT(event common.MapStr) bool {

if c.not.Check(event) {
return false
}
return true
}

func (c Condition) String() string {

s := ""
Expand All @@ -339,6 +405,22 @@ func (c Condition) String() string {
if len(c.rangexp) > 0 {
s = s + fmt.Sprintf("range: %v", c.rangexp)
}
if len(c.or) > 0 {
for _, cond := range c.or {
s = s + cond.String() + " or "
}
s = s[:len(s)-len(" or ")] //delete the last or
}
if len(c.and) > 0 {
for _, cond := range c.and {
s = s + cond.String() + " and "
}
s = s[:len(s)-len(" and ")] //delete the last and
}
if c.not != nil {
s = s + "not " + c.not.String()
}

return s
}

Expand Down
Loading