Skip to content
Back to skills

Log Tail Method

ASecurity

Tail structured logs from the dev server, filtered by correlation id, level, or module. Use when a request failed and you need what the server actually recorded — a 500 with no body, a trace to follow end-to-end, an error whose stack never reached the client. Reads what was already logged; it cannot recover a field the code never emitted, does not tail production, and is not a metrics or APM query.

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 20, 2026
ai-agentsgobashdockerapibackend

Works with

  • cli
  • api

Security analysis

A100/100

Scanned October 20, 2026

npx -y skills add adnanmokhtar/refract --skill log-tail-method --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Log Tail Method?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Log Tail Method
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/adnanmokhtar-log-tail-method/badge)](https://www.skillsdirectory.com/skills/adnanmokhtar-log-tail-method)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: log-tail-method
description: Tail structured logs from the dev server, filtered by correlation id, level, or module. Use when a request failed and you need what the server actually recorded — a 500 with no body, a trace to follow end-to-end, an error whose stack never reached the client. Reads what was already logged; it cannot recover a field the code never emitted, does not tail production, and is not a metrics or APM query.
kind: skill
pack: backend
allowed-tools: [Read, Grep, Glob, Bash]
---

# log-tail-method

## Premise

Real artifacts only. Every claim cites the actual log line(s) observed + the timestamp + the correlation id used to filter. "Saw an error" without quoting the line is not a finding. The log source must be a real file, container, or process — never a paraphrase. PII (phones, emails, tokens) gets redacted before sharing; surfacing raw secrets is a halt-worthy mistake.

A "nothing in the logs" verdict without showing the exact `jq` filter run is a failed investigation.

Stream structured JSON logs (Pino / Winston / zerolog / structlog) with `jq` filters scoped to one request, level, module, or tenant.

**Scope boundary — this skill owns the mechanism, `/log-tail` owns the interpretation.** Everything here is how to *obtain* lines: source detection, the level-encoding probe, the field-name probe, the filters, the logger-specific gotchas. What the lines *mean* — the cluster halt, the no-match ladder, the reporting halts — lives in [`templates/packs/backend/commands/log-tail.md`](../../commands/log-tail.md) and is not restated here. One predicate, one owner: the level filter this skill emits shipping differently from the command's is exactly the defect that made a Pino project report a confident, false "nothing in the logs."

**Never hardcode the level predicate.** `.level` is a number in some loggers and a label in others, and a predicate built for the wrong encoding matches zero rows forever while looking correct. Probe first (Procedure step 3a), then build.

## When to use

- Endpoint returns 500 — find the stack trace + upstream calls in the same trace ID.
- Webhook receiver isn't firing — confirm payload arrived.
- Tenant reports "slow" — filter by their tenant ID and look for outliers.
- After enabling debug logs for one module without a global level change.

## Prerequisites

- Dev server logging in JSON (check the project's actual logger lib from extraction — Pino / Winston / zerolog / structlog / log15 / etc.).
- `jq` installed.
- One of: log file, container name, or running PID.

## Procedure

1. Identify log source from `CLAUDE.md` or running process:
   ```bash
   # File-based
   ls -1 logs/*.log 2>/dev/null
   # Container
   docker compose ps --services
   # Foreground process — pipe its stdout (redirect at start: `bun run start:dev | tee logs/app.log`)
   ```
2. Tail with the right command:
   ```bash
   tail -F logs/app.log                # file
   docker compose logs -f api          # container
   ```
3a. **Probe the encoding before building any predicate** — this step is not optional:
   ```bash
   tail -n 50 logs/app.log | jq -r '.level | type' | sort -u   # number | string | null
   tail -n 1  logs/app.log | jq -r 'keys | join(",")'          # which key carries the correlation id
   ```
   | Probe returns | Level predicate to use |
   |---|---|
   | `number` | `select(.level >= 50)` — Pino's ascending scale (`trace=10, debug=20, info=30, warn=40, error=50, fatal=60`); use `>= 40` to include warn. |
   | `string` | `select((.level \| ascii_downcase) as $l \| $l == "error" or $l == "fatal" or $l == "warn")` — downcased because some JSON handlers emit the label uppercase (`"level":"INFO"`). |
   | `number`, scale not Pino's | Establish the direction from that logger's own docs first. On a descending scale `>= 50` selects everything *except* errors. Direction unknown → escalate, do not guess. |
   | more than one type | Two log sources are interleaved in this file — separate them before filtering. |
   | `null` / absent | The level lives under another key (`severity`, `levelname`, `lvl`); read `keys` and re-probe. |

   The correlation key is probed the same way and passed in — `reqId`, `req.id`, `correlationId`, `request_id`, `traceId` and `trace_id` are all in live use; assuming one is how a filter returns nothing on a correctly-instrumented app.

3b. Apply the filter via `jq`, using the level predicate and correlation key 3a resolved:
   ```bash
   # Errors only — predicate from 3a, never hardcoded
   tail -F logs/app.log | jq -c 'select(.level >= 50)'                 # numeric-level logger
   tail -F logs/app.log | jq -c 'select((.level|ascii_downcase) as $l | $l=="error" or $l=="fatal")'   # label logger
   # Single request trace — probed key, both flat and nested forms
   tail -F logs/app.log | jq -c --arg id "abc-123" 'select(.reqId == $id or .req.id == $id or .correlationId == $id or .request_id == $id or .traceId == $id)'
   # One module
   tail -F logs/app.log | jq -c 'select(.context == "OrdersService")'
   # One tenant
   tail -F logs/app.log | jq -c --arg t "tenant_42" 'select(.tenantId == $t or .tenant_id == $t)'
   # Slow requests
   tail -F logs/app.log | jq -c 'select(.responseTime > 500)'
   ```
4. For unstructured fallback, swap `jq` for `grep -E`:
   ```bash
   tail -F logs/app.log | grep -E 'ERROR|FATAL|reqId=abc-123'
   ```
5. Pretty-print one event when investigating:
   ```bash
   tail -n 200 logs/app.log | jq 'select(.reqId=="abc-123")' | jq -s '.'
   ```

## Output

```
[2026-04-24T09:14:02.123Z] ERROR  OrdersService  req=a1b2c3  tenant=t_42  failed to create order: validation error
[2026-04-24T09:14:02.124Z] ERROR  ExceptionFilter req=a1b2c3 tenant=t_42 ValidationError: items[0].sku must not be empty
   at CreateOrderDto.validate (src/orders/dto/new-order.dto.ts:34)
   at OrdersController.create (src/orders/controllers/orders.controller.ts:58)
[2026-04-24T09:14:02.125Z] INFO   HttpLogger     req=a1b2c3 tenant=t_42 POST /orders 400 2ms
```

**Lines are the output of this skill, not the answer.** Hand them to `/log-tail`'s cluster halt (group by signature, enumerate every correlation id) — and when the filter returns **zero** rows, to its no-match ladder, which distinguishes "wrong source", "wrong predicate", "request never reached the handler" and "chain swallowed mid-flow". Reporting an empty result without walking that ladder is the failed investigation this skill's premise forbids.

## False positives / gotchas

- Multi-line stack traces break naive `tail | grep` — Pino keeps them as a single JSON line; some loggers don't.
- Time zones: log timestamps may be UTC while CLAUDE is in local time — convert when correlating with user-reported timestamps.
- PII: phone numbers, emails, tokens may be in logs — redact before sharing (`jq` with `.user.phone |= sub("(\\d{3})\\d+(\\d{3})";"\(.1)***\(.2)")`).
- `tail -f` (lowercase) drops the file on rotation; `tail -F` follows the rename — always use `-F` for long sessions.
- Dev only — never tail prod logs from this skill; use the central log aggregator.

## Related

- Used by `@bug-investigator` — the agent invokes `log-tail-method correlation:<id>` (or `log-tail-method error` + timestamp) as its first evidence-gathering step.
- `.claude/skills/debug-tenant/SKILL.md` — supplies log-tail-method the tenant id / correlation id to walk the tenant-resolution chain; log-tail-method produces the cited log lines that playbook requires.
- `.claude/skills/endpoint-test-method/SKILL.md` — when a test case returns 500 or a wrong shape, log-tail-method follows that request's trace into the logs.
- `ai/patterns/error-handling.md` — the `traceId` correlation id + structured-log discipline these filters key on.
- `.claude/rules/backend-principles.md` — the structured-logging + no-PII-in-logs MUSTs this skill enforces at read time.

## Halt conditions

- Halt on hand-waves: every finding must quote the actual log line + timestamp + correlation id.
- Halt if PII (phone, email, token) appears in the report unredacted — sanitize before surfacing.
- Halt if a "nothing found" verdict is returned without showing the exact `jq` (or `grep`) filter executed.
- Halt if this skill was pointed at production logs — refuse and redirect to the central aggregator.
- Halt if a level predicate was applied without the step-3a encoding probe — a hardcoded predicate on the wrong encoding returns zero rows and reads as an all-clear.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.