305 words, 2 min read

When debugging an application in production, I often want a quick overview of requests that didn't result in a "normal" HTTP response. If your web server writes JSON logs, jq makes this incredibly easy.

Here's a command I regularly use:

cat access_*.log \
| jq -r '
select(
.status != 200 and
.status != 206 and
.status != 302 and
.status != 304 and
.status != 308 and
.status != 101
)
| [.status, .request.uri]
| @csv
' \
| uniq

The output looks something like this:

404,"/api/v1/users/..."
403,"/admin/..."
429,"/api/v1/search"
500,"/api/v1/orders/..."

What this does

The command performs three simple steps:

  • Reads all matching access log files.
  • Filters out the expected HTTP status codes (200, 206, 302, 304, 308, and 101).
  • Outputs the remaining status code together with the requested URI as CSV.

Finally, uniq removes duplicate entries so you get a concise overview instead of thousands of repeated requests.

Why this is useful

This is an easy way to spot things like:

  • Missing routes (404)
  • Authorization issues (401/403)
  • Rate limiting (429)
  • Unexpected server errors (500)
  • Any other response that deserves investigation

Instead of searching through millions of log lines, you immediately get a list of unique problem endpoints.

A small improvement

If your logs aren't already grouped, consider sorting before calling uniq:

cat access_*.log \
| jq -r '
select(
.status != 200 and
.status != 206 and
.status != 302 and
.status != 304 and
.status != 308 and
.status != 101
)
| [.status, .request.uri]
| @csv
' \
| sort \
| uniq

Or, even shorter:

...
| sort -u

It's a simple one-liner, but it's become one of my favourite ways to quickly identify unexpected responses in production logs.