Home › Guides › How to group repeated errors in a log
How to group repeated errors in a log
Direct answer: assemble each error with its stack trace, mask the values that change between occurrences (timestamps, IDs, IPs, numbers), fingerprint what remains — for exceptions, the class plus the top application frame — and count identical fingerprints. The result is a list of distinct problems ranked by severity × frequency instead of a wall of near-duplicate lines. The tool below does exactly this, locally, on files up to ~500 MB.
Group a log right here
⚠ Raw values are visible on this screen only. Saved board data and every export stay redacted — exports never contain raw values.
Time range in file: —
This board was restored from this browser's storage — stored samples are the redacted copies by design. The raw-view toggle has no effect on restored boards.
| Severity | Problem (first sample line) | Count | First seen | Last seen |
|---|
No repeated problems — every entry in this file was seen exactly once. Check the singletons below.
Singletons — entries seen exactly once
Grouping is deterministic normalisation: timestamps, IDs, IPs and numbers are masked, then entries are matched on the masked message plus the top application frame. It groups — it does not diagnose. Check a group's samples before you act on it.
Step 1 — Assemble entries before counting anything
A log line is not a log entry. A Java exception arrives as a header line, an exception line,
a dozen frame lines and possibly a Caused by: chain. Python prints a header, then a traceback
block whose final line is the actual exception. If you count lines, one incident becomes forty "problems";
if you count headers without their traces, you lose the throw site — the single most identifying part.
Assembly rule of thumb: a new entry starts at a line that looks like a fresh log record (timestamp, level,
logger), and everything indented, framed, or chained after it belongs to that entry.
Step 2 — Mask what varies between occurrences
Two occurrences of the same problem differ in their values, not their shape. Replace, in a fixed order:
timestamps → <ts>; UUIDs → <uuid>; IPs → <ip>;
memory addresses and hex strings → <addr>/<hex>; ID-shaped tokens
(pod names, session-ish strings) → <id>; line numbers in file paths → <n>;
quoted literals → <str>; emails → <email>; and finally all remaining
numbers → <n>. Fixed order matters: a timestamp must be claimed before the generic number
rule shreds it into <n>-<n>-<n>. Note what is deliberately not masked:
trailing-digit words like sha256 vs sha512 — those are different problems, not
different occurrences.
Step 3 — Fingerprint: message + throw site
For entries with an exception, the fingerprint is exception class + top application frame + masked exception line. The frame is what keeps the same NullPointerException thrown from two different methods as two problems — because they are: different code, different fix. For plain messages, the fingerprint is the masked message, plus the logger/channel when the format provides one (the same words from the payment logger and the auth logger are different problems too).
Step 4 — Rank by severity × frequency, keep singletons visible
Sort groups by severity weight multiplied by log-scaled frequency (log scale so 10,000 occurrences don't bury everything else entirely), break ties by recency, and track first/last seen per group. Entries seen exactly once get their own list: a lone FATAL is often the incident, and burying singletons inside "other" is how triage tools lose them.
Doing it by hand on a small file
On a few thousand lines you can approximate this with shell tools: extract a normalised signature per entry
(strip timestamps and digits with sed), then sort | uniq -c | sort -rn. It breaks
down exactly where you'd expect: multi-line traces, values inside quotes, and files past a few hundred MB —
which is when to use the tool above (the Error Grouper), or the streaming
approach in log file too big to open. Sharing the result?
Redact it first.
Frequently asked questions
What is the difference between grouping and just using grep -c?
grep counts lines that match a pattern you already know. Grouping discovers the patterns: it normalises every entry, clusters the ones that are the same problem, and counts those — including problems you did not know to grep for. Use grep to verify a group’s count afterwards; use grouping to find out what is in the file at all.
Why mask values instead of comparing raw lines?
Because two occurrences of the same problem are never byte-identical: the timestamp, the order ID, the user, the IP and the duration all differ. Compared raw, every occurrence is unique and nothing groups. Masking removes exactly the fields that vary between occurrences so the problem’s shape — its words, its exception, its throw site — is what gets compared.
Can grouping merge two genuinely different problems?
It can if they differ only in a masked value — for example "payment failed: card declined" vs "payment failed: insufficient funds", if the reason sits inside a quoted string that gets masked. That is why groups carry samples: skim them before acting. The strictness option (keep short quoted strings) exists for exactly this case. The reverse failure — one problem split into several groups — is more common and less dangerous.
Does this work on logs from any language?
The assembly step knows Java/JVM traces, Python tracebacks (including chained ones), JavaScript stacks, Go panic blocks, Docker JSON records, Kubernetes/klog lines, JSONL records and syslog. For an unknown format it falls back to "one header line plus indented continuations", which covers most timestamped text logs well enough to group.