I got tired of scrolling journalctl at 2am trying to spot the one error that mattered. So I wired the model into the pipe. It helps — but it is not magic, and it will lie to you with total confidence if you let it.

TL;DR:

journalctl -u nginx --since "1 hour ago" -p err --no-pager \
  | sed -E 's/[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/REDACTED_IP/g; s/[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+/REDACTED_EMAIL/g' \
  | claude -p "Triage these nginx errors. Top 3 issues, severity each, and the exact next command to run."

Details below.

Extract the slice first — never dump the firehose

The single most important rule: pull the relevant window before the model sees anything. The model does not need 4GB of logs, and you do not want to pay for them or wait on them. journalctl and friends do the filtering; the model does the reasoning.

# last hour, errors and worse, one unit
journalctl -u nginx --since "1 hour ago" -p err --no-pager

# a specific incident window
journalctl --since "2026-05-30 01:50" --until "2026-05-30 02:10" -p warning

# plain file, grab the interesting block
grep -i -E "error|panic|oom|refused|timeout" /var/log/syslog | tail -n 200

# count first, so you know what you're sending
journalctl -u nginx --since "1 hour ago" -p err --no-pager | wc -l

Bound the window in time (--since/--until), severity (-p err), and unit (-u). If the slice is still thousands of lines, that is a signal to narrow further, not to widen the model's context. I aim for something a human could read but does not want to.

Redact before it leaves the box

This is the number one caveat, and it is not optional. Piping logs to the Claude CLI means the slice leaves your infrastructure and goes to an API. Logs are full of things that should not: client IPs, emails, session tokens, auth headers, internal hostnames.

Strip them with sed in the same pipe:

journalctl -u myapp --since "30 min ago" -p err --no-pager \
  | sed -E '
      s/[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/REDACTED_IP/g;
      s/[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+/REDACTED_EMAIL/g;
      s/(Bearer|token=|api[_-]?key=)[A-Za-z0-9._-]+/\1REDACTED/gI;
      s/([Pp]assword=)[^ &]+/\1REDACTED/g
    ' \
  | claude -p "..."

Two honest limits here. First, regex redaction is best-effort — it catches the obvious patterns, not every secret format you have. Audit what you actually pipe. Second, if your logs contain regulated data (PII under GDPR, payment data), think hard before sending any of it off-box at all. The exception: a host where you genuinely cannot send data outside — then this whole approach is off the table, and you run a local model or you don't do this.

A small wrapper script

Typing the pipe every time gets old. I keep a logtriage script on the box:

#!/usr/bin/env bash
# logtriage <unit> [since]
set -euo pipefail

UNIT="${1:?usage: logtriage <unit> [since]}"
SINCE="${2:-1 hour ago}"

REDACT='s/[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/REDACTED_IP/g;
        s/[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+/REDACTED_EMAIL/g;
        s/(Bearer|token=|api[_-]?key=)[A-Za-z0-9._-]+/\1REDACTED/gI'

PROMPT='You are a Linux log triage assistant. Summarise the errors below.
Group by likely root cause. For the top 3 issues give: a one-line summary,
a severity (critical/high/low), and the single exact shell command I should
run next to confirm or fix it. Be terse. If a cause is a guess, say so.'

journalctl -u "$UNIT" --since "$SINCE" -p err --no-pager \
  | sed -E "$REDACT" \
  | claude -p "$PROMPT"

Usage: logtriage nginx "2 hours ago". No CLI on the box? Same idea with curl to the Messages API — read the slice into a JSON payload, model set to claude-opus-4-8, system prompt as above, and x-api-key from an env var, not the script.

Structure the prompt for output you can act on

A vague "what's wrong here?" gets you a vague essay. Ask for a fixed shape and you get something you can scan in five seconds. The triage prompt I use asks for exactly three things per issue:

  • Top 3 issues, grouped by root cause — not a line-by-line restatement of the log.
  • Severity per issue, so I know what to look at first.
  • The exact next commandsystemctl status, a grep, a config check — not "you may wish to investigate."

The "exact next command" framing is the part that earns its keep. It turns the model from a summariser into a thing that hands me the next step, which I then verify myself. I also tell it to flag guesses, which it mostly respects.

Cost and latency reality

This is good for ad-hoc triage of a bounded window — an incident, a deploy that went sideways, a "why is this unit angry" moment. You run it, you read it, you move on.

It is not for real-time alerting and it is not for streaming the firehose through a model. Every run costs tokens and takes a few seconds round-trip. Piping a continuous log stream through it would be slow and expensive and pointless — that is what metrics and alert rules are for. Use the model when a human would otherwise be reading; do not put it on the hot path.

Where it falls down

Be clear-eyed about this:

  • Hallucinated root causes. It will confidently link a timeout to a cause that is plausible and wrong. It is pattern-matching on text, not reasoning about your actual system.
  • No access to your topology. It does not know that nginx fronts three upstreams, that one box is the flaky one, or what changed at 01:55. It sees the slice you gave it and nothing else.
  • It does not replace monitoring. This catches things after they are in the log. It will not page you, trend a metric, or notice slow degradation. For that you want real metrics and alerting — see my Netdata on Ubuntu writeup.

Treat every root cause as a hypothesis, run the command it suggests, and confirm with your own eyes.

Honest verdict

This is a faster less, not an oracle. It compresses ten minutes of squinting at journalctl into thirty seconds of reading a structured summary, and it points me at the next command. That is genuinely useful at 2am. It is not a diagnosis, it is a head start.

If this is your kind of thing, the same applies to the broader pattern — see my sysadmin AI prompt cheatsheet. And when the trigger was a full disk or runaway logs, the actual forensics live in Linux disk-full forensics and keeping logs sane is logrotate — the model can point at those, but it can't do them for you.


Related posts