fix(logs): style lines by their declared level, not by error keywords

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Andras Bacsai
2026-10-30 19:53:16 +01:00
co-authored by Claude Opus 5.5
parent a80f0c7ca8
commit e36c54c23c
2 changed files with 29 additions and 0 deletions
+21
View File
@@ -45,7 +45,28 @@ export function isExpandableLog(content, truncated) {
return truncated || formatLogDetails(content) !== content;
}
const LEVEL_FIELD = /\b(?:level|lvl|severity|loglevel)"?\s*[=:]\s*"?([a-z]+)\b/;
// A level word at the start of the line, after timestamps: `2026-10-30t16:35:49z info msg`, `[debug] msg`.
const LEADING_LEVEL = /^(?:\S*\d\S*\s+){0,3}[[(<]?([a-z]+)[\])>:]?(?:\s|$)/;
const LEVEL_NAMES = {
error: ['error', 'err', 'fatal', 'panic', 'critical', 'crit', 'alert', 'emerg', 'emergency'],
warning: ['warn', 'warning', 'wrn'],
debug: ['debug', 'dbg', 'trace', 'verbose'],
info: ['info', 'inf', 'information', 'notice'],
};
/**
* Prefer the declared level (`level=debug`, `"level":"debug"`, a leading
* `INFO`) over keywords, so `INFO ... 0 error events` or `level=debug err=EOF`
* is not shown as an error.
*/
export function getLogLevel(content) {
for (const pattern of [LEVEL_FIELD, LEADING_LEVEL]) {
const declared = content.match(pattern)?.[1];
for (const [level, names] of Object.entries(LEVEL_NAMES)) {
if (names.includes(declared)) return level;
}
}
if (/\b(error|err|failed|failure|exception|fatal|panic|critical)\b/.test(content)) return 'error';
if (/\b(warn|warning|wrn|caution)\b/.test(content)) return 'warning';
if (/\b(debug|dbg|trace|verbose)\b/.test(content)) return 'debug';
+8
View File
@@ -43,6 +43,14 @@ test('detects log levels by keyword', () => {
assert.equal(getLogLevel('warning: disk'), 'warning');
assert.equal(getLogLevel('trace id=1'), 'debug');
assert.equal(getLogLevel('ready'), 'info');
assert.equal(getLogLevel('time=2026-10-30t16:35:49z level=debug msg="read failed" err=eof'), 'debug');
assert.equal(getLogLevel('{"level":"info","msg":"conn closed","err":"eof"}'), 'info');
assert.equal(getLogLevel('level=error msg="db down"'), 'error');
assert.equal(getLogLevel('{"level":50,"msg":"failed"}'), 'error');
assert.equal(getLogLevel('2026-10-29t03:17:00.849341z info logs: pruned 840 requests, 0 error events'), 'info');
assert.equal(getLogLevel('2026/10/30 16:35:49 [debug] read failed'), 'debug');
assert.equal(getLogLevel('warn: retry failed'), 'warning');
assert.equal(getLogLevel('connection failed'), 'error');
});
test('filters by level and case-insensitive query', () => {