$ logcrux /var/log/syslog
Or pipe a stream straight in:
$ tail -f /var/log/nginx/error.log | logcrux
$ journalctl -u nginx | logcrux
This page writes logcrux ... everywhere, which assumes
the executable is on your PATH. Whether it is depends on
how you installed it:
| installed with | invoke as |
|---|---|
uv tool install or pipx | logcrux ..., already on PATH |
pip into a venv | ~/.venvs/logcrux/bin/logcrux ..., or activate the venv first |
| no install, one-off run | uvx logcrux ... |
Everything else on this page, flags, stdin, exit codes, is identical
regardless of which form you use; substitute your invocation for the
literal logcrux. See install
for the full picture, including hosts where logcrux can't be installed
at all, analyze those from outside instead:
docker logs oldcontainer | logcrux, run from a host that
does have it installed.
--last DURATION | only analyze events from the trailing window, e.g. --last 1h, --last 30m, --last 2d |
|---|---|
--threshold FLOAT | minimum confidence to trust a classified category (default 0.35) |
--format NAME | force a parser instead of auto-detecting, e.g. --format nginx. Common aliases work: nginx, apache, k8s, dmesg, ssh, dnf, journal |
--no-baseline | skip comparison against historical baselines |
--json | machine-readable output instead of the terminal report |
--config PATH | use a specific config file (see config) |
--verbose | debug logging |
--version / -V | print the installed version |
--install-completion | install shell completion |
--show-completion | print the completion script |
--help | full flag reference from the CLI itself |
$ logcrux /var/log/syslog # analyze a file
$ logcrux /var/log/syslog --last 1h # only the last hour
$ logcrux /var/log/syslog --threshold 0.7 # raise confidence threshold
$ logcrux /var/log/syslog --format nginx # force a parser
$ logcrux /var/log/syslog --no-baseline # skip baseline comparison
$ logcrux /var/log/syslog --json # machine-readable output
$ logcrux /var/log/syslog --config my.yaml # custom config
$ logcrux /var/log/syslog --verbose # debug logging
An incident:
╭─────────────────────── WARNING Auth Brute Force ────────────────────────╮
│ ● Error burst detected (25 events in 5s window) │
│ Confidence: 96% │
│ │
│ Remediation: Investigate the root service throwing errors. Check upstream │
│ connectivity and review recent config changes. │
╰──────────────────────────────────────────────────────────────────────────────╯
Analyzed 117 events in 0.1s │ 1 incident(s) │ 29 line(s) unparsed
Header: severity (WARNING / CRITICAL) and the
incident category. Body: what clustered and over what window, the
classifier's confidence, and a suggested first action. Footer: events
analyzed, elapsed time, incident count, and any unparsed lines.
A clean result:
╭────────────────────── CLEAN No incidents detected ──────────────────────╮
│ Confidence: 100% │
╰──────────────────────────────────────────────────────────────────────────────╯
Analyzed 13 events in 0.0s │ 0 incident(s)
$ logcrux /var/log/postgres.log --json
{
"analysis_id": "11962dbc-1857-4a75-9a2a-48b0fd5f4bf1",
"level": "WARNING",
"title": "Auth Brute Force",
"findings": [
{ "headline": "Error burst detected", "detail": "25 events in 5s window" }
],
"confidence": 0.957,
"category": "auth_brute_force",
"remediation": "Investigate the root service throwing errors. Check upstream connectivity and review recent config changes.",
"log_path": "/var/log/postgres.log",
"parser_format": "postgresql",
"analyzed_at": "2024-01-15T10:02:38Z",
"parsed_count": 117,
"skipped_count": 29,
"elapsed_seconds": 0.084
}
A clean run reports "level": "CLEAN",
"findings": [], "confidence": 1.0,
"category": "unknown", and "remediation": null.
parser_format shows which parser was chosen;
skipped_count mirrors the unparsed-line count in the
footer, useful for spotting a wrong-format feed.
| category | triggered by | exit |
|---|---|---|
auth_brute_force | SSH/SASL/DB auth-failure clusters, firewall port-22 blocks | 3 |
http_overload | 5xx spikes, 502/503, upstream failures | 3 |
network_issue | timeouts, replication lag, CNI failures | 3 |
oom | OOM-killed processes, memory allocation failures | 4 |
service_crash | backend down, FATAL/PANIC, crash-loop restarts | 4 |
disk_full | "no space left on device" | 4 |
config_error | invalid parameters, malformed settings | 4 |
Severity follows the category, not just confidence: a
high-confidence auth_brute_force is still exit 3, while
oom and service_crash are always exit 4 once
clustered. Reaching that CRITICAL state also requires the failure to
actually be logged at error severity and in a parseable format, not
just to have happened. A database's own LOG-level recovery trace, or a
crash backtrace no parser recognizes, won't trip it: feed logcrux the
stream where the failure is recorded at error severity (kernel/syslog,
journalctl, orchestrator events).
Script-friendly by design:
0 | clean, nothing found |
|---|---|
3 | info or warning-level finding |
4 | critical finding |
Reading a file transparently decompresses it if it's gzip or bz2.
Detection is by magic number, not by trusting the
.gz/.bz2 extension, so a rotated
syslog.1.gz just works. A truncated or partially-corrupt
archive (say, a log rotated mid-write) is salvaged up to the point it
can still decompress, with a warning, rather than aborting outright.
If a line can't be parsed, it's counted and reported in the footer
("N line(s) unparsed"), never dropped without a trace. If the
auto-detected parser only covers a minority of the file, logcrux
re-parses with a generic fallback that never drops a non-blank line,
and keeps whichever result recovered more events. --format
disables this safety net if you want to force a specific parser
regardless.
logcrux is only as good as the stream you hand it.
One log format per invocation. docker logs
merges stdout and stderr. If a service writes an access log to stdout
and an error log to stderr, the mixed stream defeats format
auto-detection. Split them:
$ docker logs myapp 2>/dev/null > access.log # stdout only
$ logcrux access.log
Strip container syslog priority prefixes. Containers
logging via syslog can prepend a <PRI>-style tag
(e.g. <134>) that file-oriented parsers reject:
$ docker logs myapp 2>&1 | sed -E 's/^<[0-9]+>//' | logcrux
Strip ANSI color codes. Some services colorize their own log output; the escape sequences disrupt parsing:
$ docker logs myapp 2>&1 | sed -E 's/\x1b\[[0-9;]*m//g' | logcrux
Turn on the severity you want detected. Some services
only log auth failures at a raised verbosity level (MySQL's
log_error_verbosity, for example). If the source doesn't
log an event at error/warning severity, there's nothing to cluster.
Parsing and detection are separate: even when a dedicated parser
matches only a minority of lines, logcrux falls back to a generic
parser and can still catch incidents by content.
--verbose shows the fallback decision.