Skip to main content

Rules

Rules are defined under a rules object. The terms map allows common expressions to be reused in one or more rules to improve readability and avoid repeating expressions. The terms map is optional.

FieldRequired?Description
rulesYesThe list of rule definitions. Each rule embeds one or more rule.
termsNoA map of named expressions that may be reused in one or more rules. Conditions may be referenced by name in rules as needed. Term names are globally reusable/unique.

Rule

A rule describes a network of conditions that must be true across one or more abstract data source. A rule is either a collection or an ordered list of conditions. Collections are called a set and ordered lists are called a sequence.

tip

One or more nested conditions from another sequence or set may be used as as a condition of another set or sequence. Nested expressions enable complex positive and negative conditions to be created across distributed data sources.

Conditions are literal strings, jq queries (for structured data), or regular expressions.

FieldRequired?Description
sequenceYesAn ordered collection of two or more positive conditions and/or zero or more negative conditions where order matters and repetitions (duplicates) are allowed, e.g. A followed by B followed by and C. Positive conditions must be described in an order object.
setYesA collection of one or more distinct positive and/or negative conditions forming a group, e.g. A, B, and C in any order with no duplicates. Positive conditions must be described in a match object.

Rules Document

examples/00-example.yaml
rules:
- cre:
id: cre-2025-0
metadata:
id: rule-id-0
hash: rule-hash-0
rule:
set:
window: 5s
event:
source: kafka
match:
- commonExpression1
- "this is another match"
- cre:
id: cre-2025-1
metadata:
id: rule-id-1
hash: rule-hash-1
rule:
sequence:
window: 10s
event:
source: kafka
order:
- regex: "foo(.+)bar"
- commonExpression1
- commonExpression2

# Optional terms section for reusing common conditions
terms:
commonExpression1:
regex: "bo(.+)r"
commonExpression2:
value: "some other match"

Add the rule document on the command line or in the configuration file to use the new rules.

examples/00-example.log
2019/02/05 12:07:37 [notice] 1629#1629: signal process started
2019/02/05 12:07:38 [error] 1629#1629: open() "/run/nginx.pid" failed (2: No such file or directory)
2019/02/05 12:07:39 [emerg] 1655#1655: bind() to 0.0.0.0:80 failed (98: Address already in use)
2019/02/05 12:07:40 [emerg] 1655#1655: bind() to baaz
2019/02/05 12:07:41 [emerg] 1655#1655: bind() foo bar
2019/02/05 12:07:42 [emerg] 1655#1655: bind() boooor
2019/02/05 12:07:43 [emerg] 1655#1655: bind() some other match
2019/02/05 12:07:44 [emerg] 1655#1655: bind() this is another match
2019/02/05 12:07:45 [emerg] 1655#1655: bind() just kidding false alarm this is normal
2019/02/05 12:07:46 [emerg] 1655#1655: bind() to [::]:444 failed (98: Address already in use)
2019/02/05 12:07:47 [emerg] 1655#1655: still could not bind() to baaaz
2019/02/05 12:07:48 [alert] 1631#1631: unlink() "/run/nginx.pid" failed (2: No such file or directory)
cat examples/00-example.log | preq -r ./examples/00-rules-document-example.yaml -d

Example output

Parsing rules           done! [2 rules in 1ms; 2.47K rules/s]
Problems detected done! [2 in 1ms; 2.43K/s]
Matching lines done! [12 lines in 1ms; 14.42K lines/s]
Reading stdin done! [906B in 0s; 2.91MB/s]
cre-2025-0 critical [1 hits @ 2019-02-05T06:07:37-06:00]
cre-2025-1 critical [1 hits @ 2019-02-05T06:07:38-06:00]

Wrote report to preq-report-1743512495.json
preq-report-1743512495.json
[
{
"cre": {
"id": "cre-2025-1"
},
"hits": [
{
"timestamp": "2019-02-05T06:07:41-06:00",
"entry": "2019/02/05 12:07:41 [emerg] 1655#1655: bind() foo bar"
},
{
"timestamp": "2019-02-05T06:07:42-06:00",
"entry": "2019/02/05 12:07:42 [emerg] 1655#1655: bind() boooor"
},
{
"timestamp": "2019-02-05T06:07:43-06:00",
"entry": "2019/02/05 12:07:43 [emerg] 1655#1655: bind() some other match"
}
],
"id": "cre-2025-1",
"rule_hash": "rule-hash-1",
"rule_id": "rule-id-0",
"timestamp": "2019-02-05T06:07:41-06:00"
},
{
"cre": {
"id": "cre-2025-0"
},
"hits": [
{
"timestamp": "2019-02-05T06:07:42-06:00",
"entry": "2019/02/05 12:07:42 [emerg] 1655#1655: bind() boooor"
},
{
"timestamp": "2019-02-05T06:07:44-06:00",
"entry": "2019/02/05 12:07:44 [emerg] 1655#1655: bind() this is another match"
}
],
"id": "cre-2025-0",
"rule_hash": "rule-hash-0",
"rule_id": "rule-id=0",
"timestamp": "2019-02-05T06:07:42-06:00"
}
]

The -d option disables community CREs while testing a new rule to contribute to the problem detection community. See Getting Started for more information.

note

Try out this example in the playground!

Set

A set is a collection of one or more distinct (unique) positive and (optionally) negative conditions that form a group of matches that can occur in any order. Duplicate conditions—conditions that match the exact same thing—are not allowed in a set. Conditions in a set must be unique.

warning

Positive conditions must be described in a match object. Negative conditions use a negate object.

One positive condition

Use match to define conditions in a set. A set is used for rules that have only one positive condition.

examples/01-set-single-example.yaml
rules:
- cre:
id: set-example
rule:
set:
event:
source: kafka
match:
- "foo(.+)bar"

This set rule will match the following data.

examples/01-example.log
2019/02/05 12:07:37 [notice] 1629#1629: signal process started
2019/02/05 12:07:37 [error] 1629#1629: open() "/run/nginx.pid" failed (2: No such file or directory)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to 0.0.0.0:80 failed (98: Address already in use)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to foo bar
2019/02/05 12:07:39 [emerg] 1655#1655: bind() to test
2019/02/05 12:07:40 [emerg] 1655#1655: bind() to 0.0.0.0:444 failed (98: Address already in use)
2019/02/05 12:07:41 [emerg] 1655#1655: bind() just kidding false alarm this is normal
2019/02/05 12:07:42 [emerg] 1655#1655: bind() to [::]:444 failed (98: Address already in use)
2019/02/05 12:07:43 [emerg] 1655#1655: still could not bind() to baaaz
2019/02/05 12:07:44 [alert] 1631#1631: unlink() "/run/nginx.pid" failed (2: No such file or directory)
cat 01-example.yaml | preq -r ./01-set-single-example.yaml -d -N

Example output

Parsing rules           done! [1 rules in 0s; 2.26K rules/s]
Problems detected done! [1 in 0s; 2.15K/s]
Reading stdin done! [822B in 0s; 3.10MB/s]
Matching lines done! [10 lines in 0s; 36.23K lines/s]
set-example critical [1 hits @ 2019-02-05T06:07:38-06:00]

Multiple positive conditions

More than one positive condition can be used in a set.

examples/02-set-multiple-example-good-window.yaml
rules:
- cre:
id: set-example-2
rule:
set:
window: 10s
event:
source: kafka
match:
- value: "test"
- regex: "foo(.+)bar"
- regex: "b(.+)az"

This set rule will match the following data.

examples/02-example.log
2019/02/05 12:07:37 [notice] 1629#1629: signal process started
2019/02/05 12:07:37 [error] 1629#1629: open() "/run/nginx.pid" failed (2: No such file or directory)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to 0.0.0.0:80 failed (98: Address already in use)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to foo bar
2019/02/05 12:07:39 [emerg] 1655#1655: bind() to test
2019/02/05 12:07:40 [emerg] 1655#1655: bind() to 0.0.0.0:444 failed (98: Address already in use)
2019/02/05 12:07:41 [emerg] 1655#1655: bind() just kidding false alarm this is normal
2019/02/05 12:07:42 [emerg] 1655#1655: bind() to [::]:444 failed (98: Address already in use)
2019/02/05 12:07:43 [emerg] 1655#1655: still could not bind() to baaaz
2019/02/05 12:07:44 [alert] 1631#1631: unlink() "/run/nginx.pid" failed (2: No such file or directory)
cat 02-example.log | preq -r ./02-set-multiple-example-good-window.yaml -d -N

Example output

Parsing rules           done! [1 rules in 0s; 2.41K rules/s]
Problems detected done! [1 in 0s; 2.33K/s]
Reading stdin done! [822B in 0s; 4.11MB/s]
Matching lines done! [10 lines in 0s; 50.00K lines/s]
set-example-2 critical [1 hits @ 2019-02-05T06:07:38-06:00]

The same rule will also match this data even though the conditions are in a different order:

examples/02-example-out-of-order.log
2019/02/05 12:07:37 [notice] 1629#1629: signal process started
2019/02/05 12:07:37 [error] 1629#1629: open() "/run/nginx.pid" failed (2: No such file or directory)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to 0.0.0.0:80 failed (98: Address already in use)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to test
2019/02/05 12:07:39 [emerg] 1655#1655: bind() to foo bar
2019/02/05 12:07:40 [emerg] 1655#1655: bind() to 0.0.0.0:444 failed (98: Address already in use)
2019/02/05 12:07:41 [emerg] 1655#1655: bind() just kidding false alarm this is normal
2019/02/05 12:07:42 [emerg] 1655#1655: bind() to [::]:444 failed (98: Address already in use)
2019/02/05 12:07:43 [emerg] 1655#1655: still could not bind() to baaaz
2019/02/05 12:07:44 [alert] 1631#1631: unlink() "/run/nginx.pid" failed (2: No such file or directory)
cat 02-example-1.log | preq -r ./02-set-multiple-example.yaml -d -N

Example output

Parsing rules           done! [1 rules in 0s; 2.41K rules/s]
Problems detected done! [1 in 0s; 2.33K/s]
Reading stdin done! [822B in 0s; 4.11MB/s]
Matching lines done! [10 lines in 0s; 50.00K lines/s]
set-example-2 critical [1 hits @ 2019-02-05T06:07:38-06:00]

Match window

Two or more positive conditions require a match window. A match window describes the maximum amount of time that is allowed to pass between matches of the first and last positive conditions. If one or more conditions matches outside the window, the rule will not fire.

examples/02-set-multiple-example-bad-window.yaml
rules:
- cre:
id: set-example-2
rule:
set:
window: 1s
event:
source: kafka
match:
- value: "test"
- regex: "foo(.+)bar"
- regex: "b(.+)az"

This set rule will not match the same data as above because the third positive condition occurs outside the rule's new 1s window.

examples/02-example.log
2019/02/05 12:07:37 [notice] 1629#1629: signal process started
2019/02/05 12:07:37 [error] 1629#1629: open() "/run/nginx.pid" failed (2: No such file or directory)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to 0.0.0.0:80 failed (98: Address already in use)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to foo bar
2019/02/05 12:07:39 [emerg] 1655#1655: bind() to test
2019/02/05 12:07:40 [emerg] 1655#1655: bind() to 0.0.0.0:444 failed (98: Address already in use)
2019/02/05 12:07:41 [emerg] 1655#1655: bind() just kidding false alarm this is normal
2019/02/05 12:07:42 [emerg] 1655#1655: bind() to [::]:444 failed (98: Address already in use)
2019/02/05 12:07:43 [emerg] 1655#1655: still could not bind() to baaaz
2019/02/05 12:07:44 [alert] 1631#1631: unlink() "/run/nginx.pid" failed (2: No such file or directory)
cat 02-example-1.log | preq -r ./02-set-multiple-example-bad-window.yaml -d -N

Example output

Parsing rules           done! [1 rules in 0s; 2.41K rules/s]
Problems detected done! [1 in 0s; 2.33K/s]
Reading stdin done! [822B in 0s; 4.11MB/s]
Matching lines done! [10 lines in 0s; 50.00K lines/s]

Negative conditions

A set may use one or more negative condition to reduce false positive detections (FPs).

examples/03-set-negative-example.yaml
rules:
- cre:
id: set-negative
rule:
set:
window: 10s
event:
source: kafka
match:
- value: "test"
- regex: "foo(.+)bar"
- regex: "b(.+)az"
negate:
- already in use

This set rule will not match the following data because of the negative condition highlighted below.

examples/03-example.log
2019/02/05 12:07:37 [notice] 1629#1629: signal process started
2019/02/05 12:07:37 [error] 1629#1629: open() "/run/nginx.pid" failed (2: No such file or directory)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to 0.0.0.0:80 failed (98: Address already in use)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to foo bar
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to test
2019/02/05 12:07:38 [emerg] 1655#1655: bind() just kidding false alarm this is normal
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to [::]:444 failed (98: Address already in use)
2019/02/05 12:07:38 [emerg] 1655#1655: still could not bind() to baaaz
2019/02/05 12:07:41 [alert] 1631#1631: unlink() "/run/nginx.pid" failed (2: No such file or directory)
cat 03-example.log | preq -r ./03-set-negative-example.yaml -d -N

Example output

Parsing rules           done! [1 rules in 1ms; 1.01K rules/s]
Problems detected done! [0 in 1ms; 0/s]
Reading stdin done! [725B in 0s; 4.01MB/s]
Matching lines done! [9 lines in 0s; 49.72K lines/s]

By default, negative conditions cannot occur between the first and last positive condition. But this behavior can be modified with negate options.

One positive and one negative condition may be used in a set. However, without additional negate options, the negative condition must occur at the exact same time to fire.

examples/04-set-1x1-example.yaml
rules:
- cre:
id: set-1x1
rule:
set:
window: 10s
event:
source: kafka
match:
- value: "test"
negate:
- already in use

This set rule will not match the following data because of the negative condition occurs at the same time as the positive condition.

examples/04-example.log
2019/02/05 12:07:37 [notice] 1629#1629: signal process started
2019/02/05 12:07:37 [error] 1629#1629: open() "/run/nginx.pid" failed (2: No such file or directory)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to 0.0.0.0:80 failed (98: Address already in use)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to foo bar
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to test
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to 0.0.0.0:444 failed (98: Address already in use)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() just kidding false alarm this is normal
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to [::]:444 failed (98: Address already in use)
2019/02/05 12:07:38 [emerg] 1655#1655: still could not bind() to baaaz
2019/02/05 12:07:41 [alert] 1631#1631: unlink() "/run/nginx.pid" failed (2: No such file or directory)
cat 04-example.log | preq -r ./04-set-1x1-example.yaml -d -N

Example output

Parsing rules           done! [1 rules in 1ms; 1.32K rules/s]
Problems detected done! [0 in 1ms; 0/s]
Reading stdin done! [822B in 0s; 5.11MB/s]
Matching lines done! [10 lines in 0s; 60.98K lines/s]

If the negative condition occurs at a different time, the rule will fire.

Common pitfalls

An order is not allowed in a set

danger

Use match instead. The order term is reserved for describing positive conditions in a sequence.

examples/05-bad-set-example.yaml
rules:
- cre:
id: bad-order
rule:
set:
event:
source: kafka
order: # Use match instead
- bar
$ cat *.log | preq -r 05-bad-set-example.yaml -d -N
Rules error: set missing match

One negative not allowed

danger

A single negative condition alone is not allowed in either a set or a sequence.

examples/06-bad-set-example.yaml
rules:
- cre:
id: bad-negate
rule:
set:
event:
source: kafka
negate: # Negates may not be used without positive conditions
- "foo(.+)bar"
$ cat *.log | preq -r 06-bad-set-example.yaml -d -N
Rules error: missing one or more positive condition under a match statement

Two positive terms without a window not allowed

danger

A window is required for more than one positive term.

examples/07-bad-set-window.yaml
rules:
- cre:
id: bad-window
rule:
set:
# missing window
event:
source: kafka
match:
- foo
- bar
$ cat *.log | preq -r 07-bad-set-window.yaml -d -N
Rules error: invalid window

Sequence

A sequence defines two or more positive conditions that must occur in the same order as defined in the order object. A sequence with only one positive condition is not allowed. Use a set instead for a single positive condition.

warning

A single negative condition is not allowed without two or more positive conditions in a sequence.

Multiple positive conditions

examples/08-sequence-example-good-window.yaml
rules:
- cre:
id: seq-example-1
rule:
sequence:
event:
source: kafka
window: 10s
order:
- regex: "foo(.+)bar"
- value: "test"
- regex: "b(.+)az"

This rule matches the following data:

examples/08-example.log
2019/02/05 12:07:37 [notice] 1629#1629: signal process started
2019/02/05 12:07:37 [error] 1629#1629: open() "/run/nginx.pid" failed (2: No such file or directory)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to 0.0.0.0:80 failed (98: Address already in use)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to foo bar
2019/02/05 12:07:39 [emerg] 1655#1655: bind() to test
2019/02/05 12:07:40 [emerg] 1655#1655: bind() to 0.0.0.0:444 failed (98: Address already in use)
2019/02/05 12:07:41 [emerg] 1655#1655: bind() just kidding false alarm this is normal
2019/02/05 12:07:42 [emerg] 1655#1655: bind() to [::]:444 failed (98: Address already in use)
2019/02/05 12:07:43 [emerg] 1655#1655: still could not bind() to baaaz
2019/02/05 12:07:44 [alert] 1631#1631: unlink() "/run/nginx.pid" failed (2: No such file or directory)
cat 08-example.log | preq -r 08-sequence-example-good-window.yaml -d -N

Example output

Parsing rules           done! [1 rules in 0s; 1.08K rules/s]
Problems detected done! [1 in 1ms; 1.06K/s]
Reading stdin done! [736B in 0s; 1.09MB/s]
Matching lines done! [9 lines in 0s; 13.16K lines/s]
seq-example-1 critical [1 hits @ 2019-02-05T06:07:38-06:00]

Order window

Two or more positive conditions in a sequence require an order window. An order window describes the maximum amount of time that is allowed to pass between matches of the first and last positive conditions. If one or more conditions match outside the window, the rule will not fire.

examples/08-sequence-example-bad-window.yaml
rules:
- cre:
id: seq-example-1
rule:
sequence:
event:
source: kafka
window: 1s
order:
- regex: "foo(.+)bar"
- value: "test"
- regex: "b(.+)az"

This rule does not match the following data because the third positive conditions occurs outside the rule's new 1s window:

examples/08-example.log
2019/02/05 12:07:37 [notice] 1629#1629: signal process started
2019/02/05 12:07:37 [error] 1629#1629: open() "/run/nginx.pid" failed (2: No such file or directory)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to 0.0.0.0:80 failed (98: Address already in use)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to foo bar
2019/02/05 12:07:39 [emerg] 1655#1655: bind() to test
2019/02/05 12:07:40 [emerg] 1655#1655: bind() to 0.0.0.0:444 failed (98: Address already in use)
2019/02/05 12:07:41 [emerg] 1655#1655: bind() just kidding false alarm this is normal
2019/02/05 12:07:42 [emerg] 1655#1655: bind() to [::]:444 failed (98: Address already in use)
2019/02/05 12:07:43 [emerg] 1655#1655: still could not bind() to baaaz
2019/02/05 12:07:44 [alert] 1631#1631: unlink() "/run/nginx.pid" failed (2: No such file or directory)
cat 08-example.log | preq -r 08-sequence-example-good-window.yaml -d -N

Example output

Parsing rules           done! [1 rules in 0s; 1.08K rules/s]
Problems detected done! [1 in 1ms; 1.06K/s]
Reading stdin done! [736B in 0s; 1.09MB/s]
Matching lines done! [9 lines in 0s; 13.16K lines/s]
seq-example-1 critical [1 hits @ 2019-02-05T06:07:38-06:00]

Negative conditions

examples/09-sequence-negate-example.yaml
- rule:
sequence:
event:
source: kafka
window: 10s
order:
- regex: "foo(.+)bar"
- value: "test"
- regex: "b(.+)az"
negate:
- already in use

This rule will not fire on the following data because of the highlighted negative conditions that occurs after the first and before the last positive condition.

exmaples/09-example.log
2019/02/05 12:07:37 [notice] 1629#1629: signal process started
2019/02/05 12:07:37 [error] 1629#1629: open() "/run/nginx.pid" failed (2: No such file or directory)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to 0.0.0.0:80 failed (98: Address already in use)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to foo bar
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to test
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to 0.0.0.0:444 failed (98: Address already in use)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to [::]:444 failed (98: Address already in use)
2019/02/05 12:07:38 [emerg] 1655#1655: still could not bind() to baaaz
2019/02/05 12:07:41 [alert] 1631#1631: unlink() "/run/nginx.pid" failed (2: No such file or directory)
cat 09-example.log | ../bin/preq-linux-amd64 -r 09-sequence-negate-example.yaml -d -N

Example output

Parsing rules           done! [1 rules in 1ms; 1.19K rules/s]
Problems detected done! [0 in 1ms; 0/s]
Reading stdin done! [736B in 0s; 5.41MB/s]
Matching lines done! [9 lines in 0s; 66.67K lines/s]

Common pitfalls

match conditions not allowed

danger

A match cannot be used with a sequence. Use order instead.

examples/10-bad-sequence-match-example.yaml
rules:
- cre:
id: bad-seq-match
rule:
sequence:
event:
source: kafka
window: 10s
match: # use order instead
- regex: "foo(.+)bar"
- value: "test"
- regex: "b(.+)az"
$ cat *.log | preq -r ./10-bad-sequence-match-example.yaml -d -N
Rules error: sequence missing order

One positive condition not allowed

danger

A sequence with only one condition is not supported.

examples/11-bad-sequence-one-condition.yaml
rules:
- cre:
id: bad-seq-one-condition
rule:
sequence:
event:
source: kafka
order:
- regex: "foo(.+)bar" # one condition not allowed
$ cat *.log | preq -r ./11-bad-sequence-one-condition.yaml -d -N
Rules error: sequences require two or more positive conditions

One negative condition not allowed

danger

A negative condition by itself is not allowed. It always requires relative positive conditions.

examples/12-bad-sequence-one-negate.yaml
rules:
- cre:
id: bad-seq-one-negate-condition
rule:
sequence:
event:
source: kafka
negate:
- regex: "foo(.+)bar" # one negate condition not allowed
$ cat *.log | preq -r ./12-bad-sequence-one-negate.yaml -d -N
Rules error: sequences require two or more positive conditions

Conditions

Conditions describe how to match patterns that exist in any data source with a timestamp. Positive (or "matching") and negative (or "inverse" or "absent") conditions on data sources can be expressed in one of three ways:

FieldTypeDescription
valuestringA UTF-8 encoded string that supports unicode characters.
regexstringRE2-style regular expressions
jqstringA jq formatted query string

String Values

Strings can be added in quotes using the value field to match this string in the example data below.

examples/13-string-example.yaml
rules:
- cre:
id: string-example-1
rule:
set:
event:
source: kafka
match:
- value: "[emerg] 1655#1655: still could not bind()"

This will match the highlighted line below.

examples/13-example.log
2019/02/05 12:07:37 [notice] 1629#1629: signal process started
2019/02/05 12:07:37 [error] 1629#1629: open() "/run/nginx.pid" failed (2: No such file or directory)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to 0.0.0.0:80 failed (98: Address already in use)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to 0.0.0.0:443 failed (98: Address already in use)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to [::]:443 failed (98: Address already in use)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to 0.0.0.0:444 failed (98: Address already in use)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to [::]:444 failed (98: Address already in use)
2019/02/05 12:07:38 [emerg] 1655#1655: still could not bind()
2019/02/05 12:07:41 [alert] 1631#1631: unlink() "/run/nginx.pid" failed (2: No such file or directory)
cat 13-example.log | preq -r ./13-string-example.yaml -d -N 

Example output

Parsing rules           done! [1 rules in 1ms; 1.15K rules/s]
Problems detected done! [1 in 1ms; 1.13K/s]
Reading stdin done! [807B in 0s; 3.62MB/s]
string-example-1 critical [1 hits @ 2019-02-05T06:07:38-06:00]
Matching lines done! [9 lines in 0s; 40.18K lines/s]

String values may also be expressed without the value field.

examples/14-string-example.yaml
rules:
- cre:
id: string-example-2
rule:
set:
event:
source: kafka
match:
- "[emerg] 1655#1655: still could not bind()"
cat 14-example.log | preq -r ./14-string-example.yaml -d -N

Example output

Parsing rules           done! [1 rules in 0s; 1.70K rules/s]
Problems detected done! [1 in 0s; 1.64K/s]
Reading stdin done! [807B in 0s; 2.97MB/s]
string-example-2 critical [1 hits @ 2019-02-05T06:07:38-06:00]
Matching lines done! [9 lines in 0s; 32.03K lines/s]

Escape Sequences

Use a backslash \ to escape the following special characters in a string value:

  • \r carriage return CR
  • \n new line LF
  • \t tab
  • \' single quotes '
  • \" double quotes "
  • \\ backslash \
  • \u for 4-digit hexadecimal code points (e.g., \u00E9 for é)
  • \U for 8-digit hexadecimal code points (e.g., \U0001F600 for 😀)

Regular Expressions

Regular expressions can be added in quotes using the regex field. Regular expressions ignore parts of a condition that may change frequently, like the 1655#1655 process and thread IDs in the example.

examples/15-regex-example.yaml
rules:
- cre:
id: regex-example-1
rule:
set:
event:
source: kafka
match:
- regex: "emerg(.+)still could not bind()"

This regular expression will match the highlighted line below.

examples/15-example.log
2019/02/05 12:07:37 [notice] 1629#1629: signal process started
2019/02/05 12:07:37 [error] 1629#1629: open() "/run/nginx.pid" failed (2: No such file or directory)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to 0.0.0.0:80 failed (98: Address already in use)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to 0.0.0.0:443 failed (98: Address already in use)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to [::]:443 failed (98: Address already in use)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to 0.0.0.0:444 failed (98: Address already in use)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to [::]:444 failed (98: Address already in use)
2019/02/05 12:07:38 [emerg] 1655#1655: still could not bind()
2019/02/05 12:07:41 [alert] 1631#1631: unlink() "/run/nginx.pid" failed (2: No such file or directory)
cat 15-example.log | preq -r ./15-regex-example.yaml -d -N

Example output

Parsing rules           done! [1 rules in 0s; 2.89K rules/s]
Problems detected done! [1 in 0s; 2.78K/s]
Reading stdin done! [807B in 0s; 5.24MB/s]
regex-example-1 critical [1 hits @ 2019-02-05T06:07:38-06:00]
Matching lines done! [9 lines in 0s; 55.90K lines/s]

The following regular expression special characters must be escaped using a backslash \:

Regular expression special characters
\.+*?()|[]{}^$`
warning

An escape backslash \ must also be escaped in the Yaml string. This means two backslashes are needed. One for the Yaml string and the other for the regular expression.

16-regex-example.yaml
rules:
- cre:
id: regex-example-2
rule:
set:
event:
source: kafka
match:
- regex: "\\[emerg] (.+) still could not bind()"

This will match the highlighted line below.

16-example.log
2019/02/05 12:07:37 [notice] 1629#1629: signal process started
2019/02/05 12:07:37 [error] 1629#1629: open() "/run/nginx.pid" failed (2: No such file or directory)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to 0.0.0.0:80 failed (98: Address already in use)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to 0.0.0.0:443 failed (98: Address already in use)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to [::]:443 failed (98: Address already in use)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to 0.0.0.0:444 failed (98: Address already in use)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to [::]:444 failed (98: Address already in use)
2019/02/05 12:07:38 [emerg] 1655#1655: still could not bind()
2019/02/05 12:07:41 [alert] 1631#1631: unlink() "/run/nginx.pid" failed (2: No such file or directory)
cat 16-example.log | preq -r ./16-regex-example.yaml -d -N

Example output

Parsing rules           done! [1 rules in 1ms; 1.10K rules/s]
Problems detected done! [1 in 1ms; 1.08K/s]
Reading stdin done! [807B in 0s; 3.77MB/s]
Matching lines done! [9 lines in 0s; 43.69K lines/s]
regex-example-2 critical [1 hits @ 2019-02-05T06:07:38-06:00]

jq Expressions

jq expressions may be used on structured JSON or Yaml data sources.

examples/17-jq-example.yaml
rules:
- cre:
id: jq-example-1
rule:
set:
event:
source: kafka
match:
- jq: ".event == \"worker_process_crash\""

This jq expression will match the highlighted line below.

examples/17-example.log
{"timestamp":"2025-03-26T14:01:02Z","level":"info","service":"nginx-ingress","namespace":"default","pod":"nginx-ingress-abc123","request_id":"req001","method":"GET","path":"/","status":200,"latency_ms":5,"upstream":"web-service:80","client_ip":"192.168.1.10"}
{"timestamp":"2025-03-26T14:01:03Z","level":"info","service":"nginx-ingress","namespace":"default","pod":"nginx-ingress-abc123","request_id":"req002","method":"GET","path":"/api/status","status":200,"latency_ms":12,"upstream":"api-service:8080","client_ip":"192.168.1.11"}
{"timestamp":"2025-03-26T14:01:04Z","level":"error","event":"worker_process_crash","message":"worker process 4123 exited on signal 9","service":"nginx-ingress","namespace":"default","pod":"nginx-ingress-abc123","worker_pid":4123,"exit_signal":9}
{"timestamp":"2025-03-26T14:01:10Z","level":"warn","service":"nginx-ingress","namespace":"default","pod":"nginx-ingress-abc123","request_id":"req003","method":"POST","path":"/api/login","status":401,"latency_ms":15,"upstream":"auth-service:9000","client_ip":"192.168.1.12"}
{"timestamp":"2025-03-26T14:01:11Z","level":"info","service":"nginx-ingress","namespace":"default","pod":"nginx-ingress-abc123","request_id":"req004","method":"GET","path":"/metrics","status":200,"latency_ms":1,"upstream":"metrics-service:9090","client_ip":"192.168.1.15"}
{"timestamp":"2025-03-26T14:01:12Z","level":"info","service":"nginx-ingress","namespace":"default","pod":"nginx-ingress-abc123","request_id":"req005","method":"GET","path":"/healthz","status":200,"latency_ms":1,"upstream":"health-service:8081","client_ip":"192.168.1.18"}
cat 17-example.log | preq -r 17-jq-example.yaml -d -N

Example output

Parsing rules           done! [1 rules in 1ms; 973 rules/s]
Problems detected done! [1 in 1ms; 990/s]
Reading stdin done! [1.60KB in 0s; 7.23MB/s]
jq-example-1 critical [1 hits @ 2025-03-26T09:01:04-05:00]
Matching lines done! [6 lines in 0s; 26.67K lines/s]

Use more complex jq queries to match on more than one field in the data.

examples/18-jq-example.yaml
rules:
- cre:
id: jq-example-2
rule:
set:
event:
source: kafka
match:
- jq: "select(.event == \"worker_process_crash\" and .level == \"error\")"
cat 18-example.log | preq -r ./18-jq-example.yaml -d -N

Example output

Parsing rules           done! [1 rules in 1ms; 850 rules/s]
Problems detected done! [1 in 1ms; 885/s]
Reading stdin done! [1.60KB in 0s; 7.23MB/s]
jq-example-2 critical [1 hits @ 2025-03-26T09:01:04-05:00]
Matching lines done! [6 lines in 0s; 26.09K lines/s]

Common pitfalls

Avoid Yaml literal-block scalers |

danger

Avoid using Yaml literal-block scalars (e.g. |) in conditions. The literal-block scalar adds a line feed \n at the end of the string.

examples/19-bad-literal-block-example.yaml
rules:
- cre:
id: bad-yaml-literal-block
rule:
set:
event:
source: kafka
match:
- regex: |
"\\[emerg] (.+) still could not bind()"

This will add a \n to the end of the expression. As a result, it will no longer match the example data.

$ cat *.log | preq -r 19-bad-literal-block-example.yaml -d -N
Parsing rules done! [1 rules in 0s; 1.88K rules/s]
Problems detected done! [0 in 0s; 0/s]
Reading stdin done! [11.95KB in 0s; 47.22MB/s]
Matching lines done! [105 lines in 0s; 420.00K lines/s]

A regular expression may not be used without using the regex field.

examples/20-bad-regex-example.yaml
rules:
- cre:
id: bad-regex-example
rule:
set:
event:
source: kafka
match:
- "\\[emerg] (.+) still could not bind()" # use regex: instead

Negative conditions

Negative conditions are used to reduce false positives in a set or sequence. By default, a negative condition cannot occur between the first and last positive condition in a match or an order.

Simple negative condition

examples/21-negative-example.yaml
- rule:
sequence:
event:
source: kafka
window: 10s
order:
- regex: "foo(.+)bar"
- value: "test"
- regex: "b(.+)az"
negate:
- this is normal

This sequence rule would not match the following data because of the negative condition that occurs between the first and last positive condition in the sequence.

examples/21-example.log
2019/02/05 12:07:37 [notice] 1629#1629: signal process started
2019/02/05 12:07:37 [error] 1629#1629: open() "/run/nginx.pid" failed (2: No such file or directory)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to 0.0.0.0:80 failed (98: Address already in use)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to foo bar
2019/02/05 12:07:39 [emerg] 1655#1655: bind() to test
2019/02/05 12:07:40 [emerg] 1655#1655: bind() to 0.0.0.0:444 failed (98: Address already in use)
2019/02/05 12:07:41 [emerg] 1655#1655: bind() just kidding false alarm this is normal
2019/02/05 12:07:42 [emerg] 1655#1655: bind() to [::]:444 failed (98: Address already in use)
2019/02/05 12:07:43 [emerg] 1655#1655: still could not bind() to baaaz
2019/02/05 12:07:44 [alert] 1631#1631: unlink() "/run/nginx.pid" failed (2: No such file or directory)
cat 21-example.log | preq -r ./21-negative-example.yaml -d -N

Example output

Parsing rules           done! [1 rules in 1ms; 948 rules/s]
Problems detected done! [0 in 1ms; 0/s]
Reading stdin done! [822B in 0s; 3.01MB/s]
Matching lines done! [10 lines in 0s; 34.97K lines/s]

The same sequence rule would match the following data because the negative condition happens after the last positive condition.

examples/22-example.log
2019/02/05 12:07:37 [notice] 1629#1629: signal process started
2019/02/05 12:07:37 [error] 1629#1629: open() "/run/nginx.pid" failed (2: No such file or directory)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to 0.0.0.0:80 failed (98: Address already in use)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to foo bar
2019/02/05 12:07:39 [emerg] 1655#1655: bind() to test
2019/02/05 12:07:40 [emerg] 1655#1655: bind() to 0.0.0.0:444 failed (98: Address already in use)
2019/02/05 12:07:42 [emerg] 1655#1655: bind() to [::]:444 failed (98: Address already in use)
2019/02/05 12:07:43 [emerg] 1655#1655: still could not bind() to baaaz
2019/02/05 12:07:44 [alert] 1631#1631: unlink() "/run/nginx.pid" failed (2: No such file or directory)
2019/02/05 12:07:45 [emerg] 1655#1655: bind() just kidding false alarm this is normal
cat 22-example.log | preq -r ./21-negative-example.yaml -d -N

Example output

Parsing rules           done! [1 rules in 0s; 1.27K rules/s]
Problems detected done! [1 in 1ms; 1.20K/s]
Reading stdin done! [822B in 0s; 1.24MB/s]
Matching lines done! [10 lines in 0s; 14.99K lines/s]
negate-example critical [1 hits @ 2019-02-05T06:07:38-06:00]

It would also match this data because the negative condition happens before the first positive condition.

example/23-example.log
2019/02/05 12:07:37 [notice] 1629#1629: signal process started
2019/02/05 12:07:37 [error] 1629#1629: open() "/run/nginx.pid" failed (2: No such file or directory)
2019/02/05 12:07:37 [emerg] 1655#1655: bind() just kidding false alarm this is normal
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to 0.0.0.0:80 failed (98: Address already in use)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to foo bar
2019/02/05 12:07:39 [emerg] 1655#1655: bind() to test
2019/02/05 12:07:40 [emerg] 1655#1655: bind() to 0.0.0.0:444 failed (98: Address already in use)
2019/02/05 12:07:42 [emerg] 1655#1655: bind() to [::]:444 failed (98: Address already in use)
2019/02/05 12:07:43 [emerg] 1655#1655: still could not bind() to baaaz
2019/02/05 12:07:44 [alert] 1631#1631: unlink() "/run/nginx.pid" failed (2: No such file or directory)
cat 23-example.log | preq -r ./21-negative-example.yaml -d -N

Example output

Parsing rules           done! [1 rules in 1ms; 1.22K rules/s]
Problems detected done! [1 in 1ms; 1.20K/s]
Reading stdin done! [822B in 0s; 5.04MB/s]
negate-example critical [1 hits @ 2019-02-05T06:07:38-06:00]
Matching lines done! [10 lines in 0s; 61.35K lines/s]

To match the last two examples add negate options to the negative conditions.

Multiple negative conditions

More than one negative condition may be used.

examples/24-multiple-negatives.yaml
rules:
- cre:
id: multiple-negatives
rule:
sequence:
event:
source: kafka
window: 10s
order:
- regex: "foo(.+)bar"
- value: "test"
- regex: "b(.+)az"
negate:
- FP1
- FP2
- FP3
examples/24-example.log
2019/02/05 12:07:37 [notice] 1629#1629: signal process started
2019/02/05 12:07:37 [error] 1629#1629: open() "/run/nginx.pid" failed (2: No such file or directory)
2019/02/05 12:07:37 [emerg] 1655#1655: bind() just kidding false alarm this is normal
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to 0.0.0.0:80 failed (98: Address already in use)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to foo bar
2019/02/05 12:07:39 [emerg] 1655#1655: bind() to test
2019/02/05 12:07:40 [emerg] 1655#1655: bind() to 0.0.0.0:444 failed (98: Address already in use)
2019/02/05 12:07:40 [emerg] FP1
2019/02/05 12:07:40 [emerg] FP2
2019/02/05 12:07:41 [emerg] FP3
2019/02/05 12:07:41 [emerg] 1655#1655: bind() to [::]:444 failed (98: Address already in use)
2019/02/05 12:07:42 [emerg] 1655#1655: still could not bind() to baaaz
2019/02/05 12:07:43 [alert] 1631#1631: unlink() "/run/nginx.pid" failed (2: No such file or directory)
cat 24-example.log | preq -r ./24-multiple-negatives.yaml -d -N

Example output

Parsing rules           done! [1 rules in 0s; 1.80K rules/s]
Problems detected done! [0 in 0s; 0/s]
Reading stdin done! [918B in 0s; 1.78MB/s]
Matching lines done! [13 lines in 0s; 24.81K lines/s]

Negate options

Negative conditions provide additional options to match (or reset) the rule. This is helpful in chaotic distributed systems where additional windows of time is needed before, during, or after positive conditions to prevent a rule from firing on a false positive (FP).

Window

The negate window is used to extend or move the amount of time in which a negative condition cannot occur relative to the positive conditions.

The following set rule defines one positive and one negative condition.

examples/25-negate-options-1x1.yaml
rules:
- cre:
id: negate-options-1x1
rule:
set:
event:
source: kafka
match:
- regex: "foo(.+)bar"
negate:
- FP1

This rule will not fire on the data below because the negative condition occurs at the same time as the positive condition.

examples/25-example.log
2019/02/05 12:07:37 [notice] 1629#1629: signal process started
2019/02/05 12:07:37 [error] 1629#1629: open() "/run/nginx.pid" failed (2: No such file or directory)
2019/02/05 12:07:37 [emerg] 1655#1655: bind() just kidding false alarm this is normal
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to 0.0.0.0:80 failed (98: Address already in use)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to foo bar
2019/02/05 12:07:38 [emerg] FP1
2019/02/05 12:07:39 [emerg] 1655#1655: bind() to test
2019/02/05 12:07:40 [emerg] 1655#1655: bind() to 0.0.0.0:444 failed (98: Address already in use)
2019/02/05 12:07:41 [emerg] 1655#1655: bind() to [::]:444 failed (98: Address already in use)
2019/02/05 12:07:42 [emerg] 1655#1655: still could not bind() to baaaz
2019/02/05 12:07:43 [alert] 1631#1631: unlink() "/run/nginx.pid" failed (2: No such file or directory)
cat 25-example.log | preq -r ./25-negate-options-1x1.yaml -d -N

Example output

Parsing rules           done! [1 rules in 0s; 2.30K rules/s]
Problems detected done! [0 in 0s; 0/s]
Reading stdin done! [918B in 0s; 5.56MB/s]
Matching lines done! [13 lines in 0s; 77.38K lines/s]

If the negative condition occurs at a different time than the positive condition, the rule will now fire.

examples/25-example-new-negate-time.log
2019/02/05 12:07:37 [notice] 1629#1629: signal process started
2019/02/05 12:07:37 [error] 1629#1629: open() "/run/nginx.pid" failed (2: No such file or directory)
2019/02/05 12:07:37 [emerg] 1655#1655: bind() just kidding false alarm this is normal
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to 0.0.0.0:80 failed (98: Address already in use)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to foo bar
2019/02/05 12:07:39 [emerg] 1655#1655: bind() to test
2019/02/05 12:07:40 [emerg] 1655#1655: bind() to 0.0.0.0:444 failed (98: Address already in use)
2019/02/05 12:07:41 [emerg] FP1
2019/02/05 12:07:41 [emerg] 1655#1655: bind() to [::]:444 failed (98: Address already in use)
2019/02/05 12:07:42 [emerg] 1655#1655: still could not bind() to baaaz
2019/02/05 12:07:43 [alert] 1631#1631: unlink() "/run/nginx.pid" failed (2: No such file or directory)
cat 25-example-new-negate-time.log | preq -r ./25-negate-options-1x1.yaml -d -N

Example output

Parsing rules           done! [1 rules in 0s; 2.61K rules/s]
Problems detected done! [1 in 0s; 2.51K/s]
Reading stdin done! [918B in 0s; 5.67MB/s]
negate-options-1x1 critical [1 hits @ 2019-02-05T06:07:38-06:00]
Matching lines done! [13 lines in 0s; 78.31K lines/s]

Match Width

The default behavior of a negative condition is it cannot occur between the first and last positive condition. This period of time between the first and last occurrence of a positive condition is referred to as its match width.

tip

The negate option window modifies the default behavior of negative conditions. It extends the time beyond the width of the positive matches. When defined, a negate window adds the specified amount of time to the width of the match that occurred.

examples/27-negate-window.yaml
rules:
- cre:
id: negate-window
rule:
set:
event:
source: kafka
match:
- regex: "foo(.+)bar"
negate:
- value: FP1
window: 5s

This rule will no longer fire on the data above (26-example.log) because the negative condition cannot happen for up to 5s seconds after the positive condition.

examples/26-example.log
2019/02/05 12:07:37 [notice] 1629#1629: signal process started
2019/02/05 12:07:37 [error] 1629#1629: open() "/run/nginx.pid" failed (2: No such file or directory)
2019/02/05 12:07:37 [emerg] 1655#1655: bind() just kidding false alarm this is normal
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to 0.0.0.0:80 failed (98: Address already in use)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to foo bar
2019/02/05 12:07:39 [emerg] 1655#1655: bind() to test
2019/02/05 12:07:40 [emerg] 1655#1655: bind() to 0.0.0.0:444 failed (98: Address already in use)
2019/02/05 12:07:41 [emerg] 1655#1655: bind() to [::]:444 failed (98: Address already in use)
2019/02/05 12:07:42 [emerg] 1655#1655: still could not bind() to baaaz
2019/02/05 12:07:43 [emerg] FP1
2019/02/05 12:07:46 [alert] 1631#1631: unlink() "/run/nginx.pid" failed (2: No such file or directory)
cat 26-example.log | preq -r ./26-negate-window.yaml -d -N

Example output

Parsing rules           done! [1 rules in 0s; 1.94K rules/s]
Problems detected done! [0 in 0s; 0/s]
Reading stdin done! [918B in 0s; 5.16MB/s]
Matching lines done! [13 lines in 0s; 68.78K lines/s]

This rule no longer matches because:

  • The positive condition occurs at 12:07:38 on line 5 in 26-example.log
  • The negative condition occurs 5 seconds later at 12:07:43 on line 8 in 26-example.log

If the negative condition occurs outside of the 5 second negate window, then the rule will fire again.

examples/26-example-moved-negative.log
2019/02/05 12:07:37 [notice] 1629#1629: signal process started
2019/02/05 12:07:37 [error] 1629#1629: open() "/run/nginx.pid" failed (2: No such file or directory)
2019/02/05 12:07:37 [emerg] 1655#1655: bind() just kidding false alarm this is normal
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to 0.0.0.0:80 failed (98: Address already in use)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to foo bar
2019/02/05 12:07:39 [emerg] 1655#1655: bind() to test
2019/02/05 12:07:40 [emerg] 1655#1655: bind() to 0.0.0.0:444 failed (98: Address already in use)
2019/02/05 12:07:44 [emerg] FP1 happens 6 seconds later so the rule fires
2019/02/05 12:07:47 [emerg] 1655#1655: bind() to [::]:444 failed (98: Address already in use)
2019/02/05 12:07:48 [emerg] 1655#1655: still could not bind() to baaaz
2019/02/05 12:07:49 [alert] 1631#1631: unlink() "/run/nginx.pid" failed (2: No such file or directory)
cat 26-example-moved-negative.log | preq -r ./26-negate-window.yaml -d -N

Example output

Parsing rules           done! [1 rules in 0s; 2.08K rules/s]
Problems detected done! [1 in 0s; 2.00K/s]
Reading stdin done! [960B in 0s; 5.33MB/s]
Matching lines done! [13 lines in 0s; 70.65K lines/s]
negate-window critical [1 hits @ 2019-02-05T06:07:38-06:00]

Multiple positive conditions width

When more than one positive condition is used with a negate window, the width of the positive matches—the period of time between the first and last match—will be added to the negate window when no other negate options are expressed.

examples/27-negate-window.yaml
rules:
- cre:
id: negate-window-2
rule:
set:
event:
source: kafka
window: 5s
match:
- regex: "foo(.+)bar"
- value: "test"
- regex: "b(.+)az"
negate:
- value: FP2
window: 18s

In the example data below:

examples/27-example.log
2019/02/05 12:07:37 [notice] 1629#1629: signal process started
2019/02/05 12:07:37 [error] 1629#1629: open() "/run/nginx.pid" failed (2: No such file or directory)
2019/02/05 12:07:37 [emerg] 1655#1655: bind() just kidding false alarm this is normal
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to 0.0.0.0:80 failed (98: Address already in use)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to foo bar
2019/02/05 12:07:39 [emerg] 1655#1655: bind() to test
2019/02/05 12:07:40 [emerg] 1655#1655: bind() to 0.0.0.0:444 failed (98: Address already in use)
2019/02/05 12:07:41 [emerg] 1655#1655: bind() to [::]:444 failed (98: Address already in use)
2019/02/05 12:07:42 [emerg] 1655#1655: still could not bind() to baaaz
2019/02/05 12:07:43 [emerg] FP1
2019/02/05 12:07:46 [alert] 1631#1631: unlink() "/run/nginx.pid" failed (2: No such file or directory)
2019/02/05 12:08:00 [emerg] FP2

The width of these positive conditions is 4s:

  • foo(.+)bar occurs at 12:07:38
  • b(.+)az occurs at 12:07:42 4 seconds later

A negate window of 18s means the negative condition cannot occur within 22s after the first positive condition when the width of the positive conditions is 4s and the negate window is an additional 18s.

Because "FP2" occurs 22s after the first positive condition, the rule will not fire.

cat 27-example.log | preq -r ./27-negate-window.yaml -d -N

Example output

Parsing rules           done! [1 rules in 0s; 2.90K rules/s]
Problems detected done! [0 in 0s; 0/s]
Reading stdin done! [886B in 0s; 8.60MB/s]
Matching lines done! [12 lines in 0s; 108.11K lines/s]

If the width of the positive conditions is smaller, then the rule will fire.

examples/27-example-smaller.log
2019/02/05 12:07:37 [notice] 1629#1629: signal process started
2019/02/05 12:07:37 [error] 1629#1629: open() "/run/nginx.pid" failed (2: No such file or directory)
2019/02/05 12:07:37 [emerg] 1655#1655: bind() just kidding false alarm this is normal
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to 0.0.0.0:80 failed (98: Address already in use)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to foo bar
2019/02/05 12:07:39 [emerg] 1655#1655: bind() to test
2019/02/05 12:07:40 [emerg] 1655#1655: bind() to 0.0.0.0:444 failed (98: Address already in use)
2019/02/05 12:07:41 [emerg] 1655#1655: bind() to [::]:444 failed (98: Address already in use)
2019/02/05 12:07:41 [emerg] 1655#1655: still could not bind() to baaaz
2019/02/05 12:07:43 [emerg] FP1
2019/02/05 12:07:46 [alert] 1631#1631: unlink() "/run/nginx.pid" failed (2: No such file or directory)
2019/02/05 12:08:00 [emerg] FP2

Because the match width is 3s in the data above, the rule will now fire. The negative condition now cannot occur within 21s of the first positive condition when the match width is 3s and the negate window is an additional 18s. Because "FP2" occurs 22s after the first positive condition, the rule will fire.

cat 27-example-smaller.log | preq -r 27-negate-window.yaml -d -N

Example output

Parsing rules           done! [1 rules in 1ms; 1.02K rules/s]
Problems detected done! [1 in 1ms; 1.00K/s]
Reading stdin done! [886B in 0s; 3.90MB/s]
Matching lines done! [12 lines in 0s; 52.40K lines/s]
negate-window-2 critical [1 hits @ 2019-02-05T06:07:38-06:00]

Similarly, if the negate window is decreased, then the rule will fire again if the match window stays the same.

examples/27-negate-window-shorter.yaml
rules:
- cre:
id: negate-window-shorter
rule:
set:
event:
source: kafka
window: 5s
match:
- regex: "foo(.+)bar"
- value: "test"
- regex: "b(.+)az"
negate:
- value: FP2
window: 17s
cat 27-example.log | preq -r 27-negate-window-shorter.yaml -d -N

Example output

Parsing rules           done! [1 rules in 0s; 1.93K rules/s]
Problems detected done! [1 in 0s; 1.87K/s]
Reading stdin done! [886B in 0s; 4.57MB/s]
Matching lines done! [12 lines in 0s; 60.30K lines/s]
negate-window-2 critical [1 hits @ 2019-02-05T06:07:38-06:00]

This new negate window means the negative condition cannot occur within 21s of the first positive condition when the match width is 4s (in 27-example.log). Because "FP2" occurs 22s after the first positive condition, the rule will fire.

Anchor

The negate option anchor may be used to change the relative positive condition from which a negate window is measured. This is useful when the negative condition has a known dependency on a specific positive term.

Recall the negate window defines the time a negative condition cannot occur as the width of the positive conditions plus the additional negate window. By default, this window of time is relative to the first positive condition (or anchor: 0).

tip

Use anchor to change when the relative negative window begins. The default negate window is relative to the first positive condition (i.e. when anchor: 0).

To change the anchor to start the negate window after the second positive condition:

examples/28-negate-anchor.yaml
rules:
- cre:
id: negate-anchor
rule:
set:
event:
source: kafka
window: 5s
match:
- regex: "foo(.+)bar"
- value: "test"
- regex: "b(.+)az"
negate:
- value: FP2
window: 17s
anchor: 1
examples/28-example.log
2019/02/05 12:07:37 [notice] 1629#1629: signal process started
2019/02/05 12:07:37 [error] 1629#1629: open() "/run/nginx.pid" failed (2: No such file or directory)
2019/02/05 12:07:37 [emerg] 1655#1655: bind() just kidding false alarm this is normal
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to 0.0.0.0:80 failed (98: Address already in use)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to foo bar
2019/02/05 12:07:39 [emerg] 1655#1655: bind() to test
2019/02/05 12:07:40 [emerg] 1655#1655: bind() to 0.0.0.0:444 failed (98: Address already in use)
2019/02/05 12:07:41 [emerg] 1655#1655: bind() to [::]:444 failed (98: Address already in use)
2019/02/05 12:07:42 [emerg] 1655#1655: still could not bind() to baaaz
2019/02/05 12:07:43 [emerg] FP1
2019/02/05 12:07:46 [alert] 1631#1631: unlink() "/run/nginx.pid" failed (2: No such file or directory)
2019/02/05 12:08:00 [emerg] FP2

The negative condition cannot occur 21s after the second positive condition (anchor: 1):

  • The match width of time between the first and last positive condition is 4s
  • The second positive condition "test" occurs at 12:07:39
  • 4s (match width) + 17s (negate window) = 21s
  • The negative condition "FP2" occurs 21s later at 12:08:00 (within the new total negate window of 21s)

Because the negative condition occurs within the total negate window anchored on the second positive condition, this rule does not fire.

cat 28-example.log | preq -r ./28-negate-anchor-0.yaml -d -N

Example output

Parsing rules           done! [1 rules in 1ms; 945 rules/s]
Problems detected done! [0 in 1ms; 0/s]
Reading stdin done! [918B in 0s; 5.46MB/s]
Matching lines done! [13 lines in 0s; 76.47K lines/s]

Slide

tip

Use the negate option slide to shift the total negate window to the left to define when negative conditions cannot happen before positive conditions.

examples/29-negate-slide.yaml
rules:
- cre:
id: negate-slide
rule:
set:
event:
source: kafka
window: 5s
match:
- regex: "foo(.+)bar"
- value: "test"
- regex: "b(.+)az"
negate:
- value: FP1
slide: -8s

This rule moves the negate window to the left of the first positive condition (anchor: 0) by 8s.

examples/29-example.log
2019/02/05 12:07:30 [notice] FP1
2019/02/05 12:07:37 [error] 1629#1629: open() "/run/nginx.pid" failed (2: No such file or directory)
2019/02/05 12:07:37 [emerg] 1655#1655: bind() just kidding false alarm this is normal
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to 0.0.0.0:80 failed (98: Address already in use)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to foo bar
2019/02/05 12:07:39 [emerg] 1655#1655: bind() to test
2019/02/05 12:07:40 [emerg] 1655#1655: bind() to 0.0.0.0:444 failed (98: Address already in use)
2019/02/05 12:07:41 [emerg] 1655#1655: bind() to [::]:444 failed (98: Address already in use)
2019/02/05 12:07:42 [emerg] 1655#1655: still could not bind() to baaaz
2019/02/05 12:07:46 [alert] 1631#1631: unlink() "/run/nginx.pid" failed (2: No such file or directory)
2019/02/05 12:08:00 [emerg] FP2

This rule will not fire because the negative condition "FP1" occurs 8s before the first positive condition (within the total negate window that is now shifted to the left by 8s using slide).

cat 29-example.log | preq -r 29-negate-slide.yaml -d -N

Example output

Parsing rules           done! [1 rules in 0s; 2.44K rules/s]
Problems detected done! [0 in 0s; 0/s]
Reading stdin done! [824B in 0s; 5.84MB/s]
Matching lines done! [11 lines in 0s; 78.57K lines/s]

If anchor is added to slide the negate window on the second positive condition, the rule will fire.

examples/29-negate-slide-anchor-1.yaml
rules:
- cre:
id: negate-slide-anchor
rule:
set:
event:
source: kafka
window: 5s
match:
- regex: "foo(.+)bar"
- value: "test"
- regex: "b(.+)az"
negate:
- value: FP1
slide: -8s
anchor: 1
cat 29-example.log | preq -r 29-negate-slide-anchor-1.yaml -d -N

Example output

Parsing rules           done! [1 rules in 1ms; 1.16K rules/s]
Problems detected done! [1 in 1ms; 1.14K/s]
Reading stdin done! [824B in 0s; 4.60MB/s]
negate-slide-anchor critical [1 hits @ 2019-02-05T06:07:38-06:00]
Matching lines done! [11 lines in 0s; 59.14K lines/s]

The negate option window can be added to increase the total negate window in a slide.

examples/29-negate-slide-anchor-1.yaml
rules:
- cre:
id: neg-all-opts
rule:
set:
event:
source: kafka
window: 5s
match:
- regex: "foo(.+)bar"
- value: "test"
- regex: "b(.+)az"
negate:
- value: FP1
slide: -9s
anchor: 1
window: 1s
  • The match width is 4s in 29-example.log
  • The additional negate window is 1s
  • The total negate window is 4s + 1s = 5s
  • A slide of -9s anchored on the second positive condition starts the total negate window of 5s at 12:07:30
  • A negative condition cannot occur between 12:07:30 and 12:07:35 (+5s) when the second positive condition occurs at 12:07:39

The rule will not fire since the negative condition occurs at 12:07:30.

cat 29-example.log | preq -r 29-negate-slide-anchor-1-window.yaml -d -N

Example output

Parsing rules           done! [1 rules in 0s; 2.57K rules/s]
Problems detected done! [0 in 0s; 0/s]
Reading stdin done! [824B in 0s; 5.68MB/s]
Matching lines done! [11 lines in 0s; 74.83K lines/s]

If the negative condition moves to 12:07:36, then the rule will fire because it is now outside of the shifted window before the positive terms.

examples/29-example-fp-moved.log
2019/02/05 12:07:36 [notice] FP1
2019/02/05 12:07:37 [error] 1629#1629: open() "/run/nginx.pid" failed (2: No such file or directory)
2019/02/05 12:07:37 [emerg] 1655#1655: bind() just kidding false alarm this is normal
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to 0.0.0.0:80 failed (98: Address already in use)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to foo bar
2019/02/05 12:07:39 [emerg] 1655#1655: bind() to test
2019/02/05 12:07:40 [emerg] 1655#1655: bind() to 0.0.0.0:444 failed (98: Address already in use)
2019/02/05 12:07:41 [emerg] 1655#1655: bind() to [::]:444 failed (98: Address already in use)
2019/02/05 12:07:42 [emerg] 1655#1655: still could not bind() to baaaz
2019/02/05 12:07:46 [alert] 1631#1631: unlink() "/run/nginx.pid" failed (2: No such file or directory)
2019/02/05 12:08:00 [emerg] FP2
cat 29-example-fp-moved.log | preq -r ./29-negate-slide-anchor-1-window.yaml -d -N

Example output

Parsing rules           done! [1 rules in 1ms; 1.34K rules/s]
Problems detected done! [1 in 1ms; 1.35K/s]
Reading stdin done! [824B in 0s; 5.25MB/s]
neg-all-opts critical [1 hits @ 2019-02-05T06:07:38-06:00]
Matching lines done! [11 lines in 0s; 69.18K lines/s]

Absolute

tip

Use the negate option absolute to avoid adding the match width to the total negate window and instead use only the negate window specified in negative condition. This is useful when the match width is unpredictable.

examples/30-negate-absolute.yaml
rules:
- cre:
id: negate-absolute
rule:
set:
event:
source: kafka
window: 5s
match:
- regex: "foo(.+)bar"
- value: "test"
- regex: "b(.+)az"
negate:
- value: FP2
window: 20s
absolute: true

This rule creates absolute window of 20s anchored by default to the first positive term.

examples/30-example.log
2019/02/05 12:07:30 [notice] FP1
2019/02/05 12:07:37 [error] 1629#1629: open() "/run/nginx.pid" failed (2: No such file or directory)
2019/02/05 12:07:37 [emerg] 1655#1655: bind() just kidding false alarm this is normal
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to 0.0.0.0:80 failed (98: Address already in use)
2019/02/05 12:07:38 [emerg] 1655#1655: bind() to foo bar
2019/02/05 12:07:39 [emerg] 1655#1655: bind() to test
2019/02/05 12:07:40 [emerg] 1655#1655: bind() to 0.0.0.0:444 failed (98: Address already in use)
2019/02/05 12:07:41 [emerg] 1655#1655: bind() to [::]:444 failed (98: Address already in use)
2019/02/05 12:07:42 [emerg] 1655#1655: still could not bind() to baaaz
2019/02/05 12:07:46 [alert] 1631#1631: unlink() "/run/nginx.pid" failed (2: No such file or directory)
2019/02/05 12:07:58 [emerg] FP2

Because the negative condition "FP2" occurs 20s after the first positive condition, this rule does not fire.

cat 30-example.log | preq -r 30-negate-absolute.yaml -d -N

Example output

Parsing rules           done! [1 rules in 0s; 2.58K rules/s]
Problems detected done! [0 in 0s; 0/s]
Reading stdin done! [824B in 0s; 7.85MB/s]
Matching lines done! [11 lines in 0s; 100.00K lines/s]

Common Pitfalls

Anchor index cannot exceed number of positive conditions

danger

Do not use an anchor index that exceeds the range of positive conditions.

examples/31-bad-negate-anchor.yaml
rules:
- cre:
id: negate-bad-anchor
rule:
set:
event:
source: kafka
window: 5s
match:
- regex: "foo(.+)bar"
- value: "test"
- regex: "b(.+)az"
negate:
- value: FP2
window: 20s
anchor: 3 # the range of the three positive anchors is 0, 1, and 2
$ cat *.log | preq -r 31-bad-negate-anchor.yaml -d -N
Rules error: anchor out of range

Data Sources

A rule defines an abstract data source to target an addressable named data type (e.g. log, metric, trace, or event).

examples/13-string-example.yaml
rules:
- cre:
id: string-example-1
rule:
set:
event:
source: kafka
match:
- value: "[emerg] 1655#1655: still could not bind()"

Because the location and address of data sources vary based on the environment (e.g. Kubernetes vs. virtual machines vs. laptops and servers) and distribution (Docker Hub vs. Bitnami vs. Quay.io), CRE rules are created to target a data source abstraction that is translated by the problem detector to the actual source's location in the environment.

The preq problem detector supports targeting log files via stdin (all abstract data sources) or by or addressed from the file system. Refer to Prequel for addressing containers or additional data types.

See Data Sources for more information.

Nested Conditions

One or more nested conditions from another sequence or set may be used as as a condition of another set or sequence. Nested expressions enable complex positive and negative conditions to be created across distributed data sources. It also enables correlations on two or more conditions to test whether they share attributes in common. For example, correlations on conditions by host evaluate to true only when all conditions occur on the same machine.

Nested conditions and correlations are supported in Prequel.

rules:
- cre:
id: nested-example
rule:
sequence:
window: 30s
correlations:
- hostname
order:
- term1
- term2
negate:
- term3

terms:
term1:
sequence:
window: 10s
event:
source: rabbitmq
origin: true
order:
- value: Discarding message
count: 10
- Mnesia overloaded
negate:
- SIGTERM

term2:
sequence:
window: 5s
correlations:
- container_id
order:
- sequence:
window: 1s
event:
source: nginx
order:
- error message
- shutdown
- set:
event:
source: nginx
match:
- 90%
- set:
event:
source: k8s
match:
- field: "reason"
value: "Killing"
term3:
set:
event:
source: k8s
match:
- field: "reason"
value: "NodeShutdown"

The rule above detects the following:

  • At the top level the rule looks for two positive conditions and resets on one negative condition.
    • The first positive condition is a sequence of 11 positive conditions and 1 negative condition in rabbitmq.
    • The second positive condition is another sequence of three positive conditions.
      • The first positive condition is a sequence in nginx of two positive conditions, error message and shutdown.
      • The second positive condition is a set in nginx that matches on 90%
      • The third positive condition is another set that matches a Kubernetes Killing event
      • The correlation on these conditions means all three positive conditions must share the same container_id to match
    • The top level rule also looks for one negative condition
      • A Kubernetes NodeShutdown event

Origin

When using nested conditions, the rule author must specify which expression on an abstract data source constitutes the origin event that will take the blame for the overall detection. The origin is implied when nested conditions are not used since the rule is only targeting one data source.

Correlations

A correlation is used in nested conditions to add additional criteria to the match. The following fields may be used in a correlation expression:

FieldDescription
imageUrlThe container's image_url
containerNameThe name of the container
containerIdThe container's unique container runtime ID
podNameThe name of the Kubernetes pod
filepathThe path to the data on the file system
processNameThe name of the process
namespaceThe Kubernetes namespace
machineIdThe host's unique machine ID (see https://man7.org/linux/man-pages/man5/machine-id.5.html)
hostnameThe fully qualified DNS name of the machine
workloadThe name of the Kubernetes service, statefulset, daemonset, or deployment