Anomaly detection
You will learn the exact rules behind each kind of finding, the limits that apply to patterns and findings, and every anomaly detection setting.
For how it works in plain language, see Patterns and findings.
Scan window
Each scan looks at the latest complete 15-minute or 1-hour window (the stream's Scan every) that ended at least 2 minutes ago, so recent events have time to be stored. A window is never scanned twice. If ingest is still behind the window, the scan is skipped and retried a minute later.
Baseline
| Baseline | Compares with | Needs |
|---|---|---|
| Daily | The same window on each of the last 7 days | At least 4 of those windows with data |
| Weekly | The same window on the same weekday over the last 4 weeks | At least 3 of those windows, and 4 weeks of pattern history |
Cordo uses whichever baseline varies less for that subject. The usual value is the baseline's median. Deviation is a robust z-score: the distance from the median divided by the median absolute deviation (scaled to match a standard deviation), with a floor so a perfectly flat history cannot make tiny changes look huge.
A past window counts only if the stream received events in it. Pattern, service, and error-rate baselines also skip windows from before the stream's first pattern was seen; stream volume skips windows from before the stream was created.
Sensitivity
| Sensitivity | Deviation needed |
|---|---|
| Low | 6 |
| Medium (default) | 4 |
| High | 3 |
A spike, drop, error rate, volume, or metric finding needs at least this deviation. New patterns do not use it.
Finding kinds
Minimum events is the stream setting (default 20).
| Kind | API kind |
Raised when | Severity |
|---|---|---|---|
| New pattern | new_pattern |
A pattern is first seen in the window, with at least 5 events or at least one error or warning | Critical when more than half its events are errors and it has at least 10; Warning with any errors or warnings; otherwise Info |
| Spike | pattern_spike |
At least Minimum events in the window, at least twice the usual count, and the deviation reaches the sensitivity | Critical when more than half its events are errors; otherwise Warning |
| Drop | pattern_drop |
The pattern had events in at least 80% of baseline windows, its usual count is at least twice Minimum events, the window has at most 20% of usual, and the deviation reaches the sensitivity (downwards) | Warning |
| Error rate | error_rate_shift |
A service has at least Minimum events in the window, its error share is at least 10 percentage points above usual, and the deviation reaches the sensitivity | Critical when the error share reaches 50%; otherwise Warning |
| Volume | volume_shift |
The stream's total is at least 50% above or below usual (and the larger of the two is at least Minimum events), and the deviation reaches the sensitivity | Warning |
| Volume (silent) | volume_shift |
The stream has no events in the window but usually has at least 100 | Critical |
| Metric | metric_anomaly |
A recorded metric series is at least 50% above or below usual and the deviation reaches the sensitivity. Minimum events applies only to count metrics | Critical at three times usual or a drop to zero; otherwise Warning |
Severity only rises while a finding stays open.
Late delivery (replayed or buffered logs)
Scans still bucket events by receipt time (ingested_at). When a producer
event_time is present and older than the start of the scanned window by more
than one scan period (15 minutes or 1 hour), that event counts as late.
Events with no event_time count as on time, so streams that do not send
producer times behave exactly as before.
For a spike, a volume surge, or a new pattern, if at least half of
the excess volume (or of the window total for a new pattern) is late, Cordo
still raises the finding but caps its severity at Info and adds a
late_delivery reason that says how late the events were on average (for
example, "most of these events were produced 40 minutes before they arrived").
Volume drops, silence, pattern drops, error-rate shifts, and metric anomalies
are not demoted this way — an outage drop stays real even when a later replay
fills a following window.
What each scan checks
| Subject | Limit per scan |
|---|---|
| Stream volume | 1 |
| Services, for error rate | The 50 busiest |
| Patterns | New patterns first, then the 200 busiest, then those with the largest baselines (for drops), up to 500 subjects in total |
| Recorded metrics | Every enabled metric with Anomaly scan on; up to 50 series per metric, busiest first |
Metrics that are behind, failing, or have no recorded value for the window are held: they raise nothing new and their open findings stay open.
Lifecycle
| Event | Rule |
|---|---|
| Detected again | The open finding for the same subject gains a detection; no new finding |
| Resolved: not seen in the next two scans | Two scans in a row did not detect it |
| Resolved: no longer new | A new pattern finding is more than 24 hours old |
| Resolved: too many open findings | More than 500 open; the least severe, then least recently seen, close first |
| Dismissed | By a person with a reason, or by a mute (reason Muted) |
| Reopened | A dismissed finding, unless another open finding covers the same subject |
A subject is the kind plus its pattern, service, or metric series.
Incidents
Findings first detected in the same scan window are grouped into one incident when two or more are created together. A finding created in a later scan joins an open incident when it shares a service or pattern with a member. Dismissing or muting a finding removes it from the incident; the incident resolves when no open members remain. Findings monitors can alert once per incident instead of per finding.
Learning period
Seven days from the stream's first pattern. Findings raised then carry
learning: true, appear in the app, and never alert. If the stream's first
pattern is more than seven days old when you turn detection on, there is no
learning period.
Limits
| Limit | Value |
|---|---|
| Patterns per stream | Guide of 500; hard cap of 1,000. Past the cap, new kinds of log line get no pattern |
| Pattern expiry | None; patterns last as long as the stream |
| Message length used for a pattern | First line, up to 2,048 bytes and 64 words |
| Open findings per stream | 500 |
| Closed findings kept | 30 days after they resolve or are dismissed |
| Active mutes per stream | 200 |
| Expired mutes kept | 30 days |
| Ignored services | 50, each up to 128 bytes |
| Findings monitor services / patterns | 50 each |
| Sample events on a finding | Up to 20 in the app; up to 10 over MCP |
| Dismiss reason | Up to 500 characters |
Settings
Edit stream → Anomaly detection, or PATCH /v1/streams/{id} with an
anomaly_detection object. Changing settings needs the Settings stream
role.
| UI label | API field | Values | Default |
|---|---|---|---|
| Turn on anomaly detection | enabled |
true, false |
false |
| Scan every | frequency |
15m, 1h |
1h |
| Sensitivity | sensitivity |
low, medium, high |
medium |
| Minimum events | min_events |
1 to 1,000,000 | 20 |
| Ignore services | excluded_services |
Up to 50 service names | [] |
Ignore services leaves those services' events out of the scan's pattern, error rate, and volume counts. Recorded metrics are scanned as recorded.
On each recorded metric, Anomaly scan (anomaly_scan, default true)
includes or excludes it.
Scan health
The same anomaly_detection object reports, read-only:
| Field | Meaning |
|---|---|
enabled_at |
When detection was last turned on |
learning, learning_until |
Whether the stream is in its learning period, and until when |
last_scan_at, next_scan_at |
The most recent and next scheduled scan |
last_scan_duration_ms |
How long the last scan took |
last_scan_error |
Why the last scan failed: scan timed out, analytics store unavailable, or scan failed |
last_scan_skipped |
analytics_lag when the last scan waited for ingest to catch up |
can_edit |
Whether you may change the settings |
Mutes
| Field | Values |
|---|---|
| Subject | Any of pattern_id, service, kind; empty fields match anything |
until |
24h, 7d, or never (shown as Always) |
note |
Optional |
A mute stops new findings for its subject and dismisses the matching open ones.
A kind mute for metric_anomaly covers every metric on the stream.