Recommendations
Synopsis
The Pipeline Editor's
It is deliberately not the geoip processor with no if above it is perfectly valid, and it also runs a database lookup for every event including the ones nobody will look at.
Findings carry one of two severities:
| Severity | Means |
|---|---|
| A measurable cost, an unsupportable construct, or behavior that changes with the input | |
| Worth changing, but nothing breaks if you do not |
Each finding names the pipeline and the line it came from. Analysis runs over the text as it stands in the editor, unsaved edits included; a pipeline that does not currently parse produces no recommendations at all rather than guesses.
Per-Event Cost
Everything in this group is multiplied by your event rate.
| Rule | Severity | Fires when |
|---|---|---|
Built-in vmetric script | Warning | A script processor runs the vmetric engine, whose cost is paid on the hot path for every event rather than amortized |
| External API call | Warning | A processor makes an outbound HTTP call per event, so throughput is bounded by the remote service |
| DNS resolution per event | Warning | dns_lookup performs a network round trip on the ingest path |
| DNS lookup without a cache | Suggestion | dns_lookup has no cache_ttl, so the same names resolve again per event |
| Unguarded enrichment lookups | Suggestion | Two or more enrichment processors (cache_get, enrich, geoip, lookup) run with no if, including for events whose source field is empty |
| Pipeline pauses | Warning | wait blocks the pipeline for a fixed duration per event — it does not poll and does not wait on a condition |
| Detection rules evaluated in-line | Suggestion | A detection processor evaluates its whole rule pack against every event unconditionally |
| Late event filter | Suggestion | A discarding processor sits past position 5 of a list of 8 or more, so everything above it runs for events that are then thrown away |
| Same condition repeated | Suggestion | Four or more consecutive processors carry the identical condition, so it is evaluated once each per event |
| Duplicate processor block | Suggestion | A block is byte-for-byte identical to an earlier one, condition included |
Expensive processor inside a foreach | Warning | The body runs once per array element, and the array length comes from the event |
Nested foreach | Suggestion | Two nested loops, both bounded by lengths that arrive with the event |
Patterns and Parsing
| Rule | Severity | Fires when |
|---|---|---|
| Nested quantifier | Warning | A regex has a quantifier inside a quantified group. On a non-matching line the engine tries every way of splitting the input, so time grows exponentially with line length |
| Regular-expression-heavy pipeline | Suggestion | More than 20 regex processors, past which per-event cost is dominated by pattern matching |
grok tries many patterns | Suggestion | More than 8 patterns on one grok; patterns are attempted in order, so the last one pays for every attempt before it |
date without formats | Warning | date has no formats, so the timestamp either fails or lands on a guess that changes with the input |
kv over an unbounded key set | Suggestion | kv runs with neither include_keys nor exclude_keys, writing a field for every key the device happened to send |
| Wildcard field move | Suggestion | A move pattern matches every field, so the result depends entirely on what arrives |
Correctness and Reachability
| Rule | Severity | Fires when |
|---|---|---|
| Processors after an unconditional terminal | Warning | break, drop, final, or return ends the run with no if on it, so nothing below it can execute |
| Pipeline calls itself | Warning | A pipeline processor names the pipeline it sits in. Without something to stop the recursion, each event re-enters until the engine cuts it off |
| No error handling | Suggestion | Processors that throw on unexpected input are present with no root on_failure to catch them, so one odd event fails the whole chain |
Secrets and Portability
| Rule | Severity | Fires when |
|---|---|---|
| Literal credential | Warning | A credential option holds a literal value, which then travels through version history, review, and every export of the pipeline |
| Literal endpoint address | Suggestion | An endpoint option holds an address directly, so the pipeline only works where that address is reachable |
A literal credential is already in your version history by the time this fires. Moving it to Vault or an environment reference does not undo that — the value it names still needs rotating.
Readability and Maintenance
| Rule | Severity | Fires when |
|---|---|---|
| Pipeline too long | Warning | More than 500 lines in one pipeline |
| Too many processors | Warning | More than 120 processors in one pipeline |
| Deeply nested processors | Suggestion | Nesting past 4 levels, where the run order and which on_failure catches what stop being obvious |
| Complex condition | Suggestion | A condition with 4 or more operators, re-evaluated per event and hard to change safely |
| No description | Suggestion | A pipeline past roughly 100 lines or 20 processors with no top-level description |
| Disabled processor | Suggestion | disabled: true — skipped at runtime, but still read and maintained by everyone who opens the file |
| Note left in the pipeline | Suggestion | A TODO, FIXME, XXX, or HACK comment, which outlives the reason it was written |
| JavaScript script engine | Suggestion | A script processor runs the jsexpr engine, whose contents cannot be reproduced from the pipeline definition alone when something goes wrong inside it |
What the Rules Are Checked Against
Every processor and option name a rule matches on comes from the editor's own processor catalog, which is generated from the engine's processor list. A rule whose processor could not be found there was dropped rather than guessed, so a rule firing on a name the engine does not have cannot happen.
Findings are capped at 100 per analysis. script rules read the lang option, since that is what selects the engine; type is accepted as a spelling of it, and the type on a script as an unknown option separately.