A resource-aware CloudWatch log tailer. resolog = resolve + log.
Most tools answer "how do I tail a log group?" (aws logs tail,
lucagrulla/cw, StartLiveTail). resolog
answers the question that comes first: given a resource, what should I even
tail? — then interleaves every stream it finds, docker-compose style.
Main use: hand it a Step Functions execution ARN and it tails the state machine plus every Lambda, Batch, and ECS task that execution ran, all together.
go install github.com/tawAsh1/resolog/cmd/resolog@latestOr download a binary from Releases. They carry build-provenance attestations:
gh attestation verify resolog_*.tar.gz --repo tawAsh1/resologThe real backends use the standard AWS credential chain. The default backend is
live (StartLiveTail).
resolog log-group:/aws/lambda/my-fn # real-time tail
resolog --backend poll -f log-group:/my/group # historical, then follow
resolog --backend poll --since 1h --sort -t sfn-execution:<execution-arn> # a finished run, in time order
resolog --backend poll --since "2026-07-05 09:00" --until "2026-07-05 10:00" --filter '?ERROR ?WARN' log-group:/my/group # a window, server-side filtered
resolog --output json log-group:/my/group | jq .message # machine-readable, one JSON object per line
resolog --fields level,msg log-group:/my/group # structured logs: show just these fields
resolog arn:aws:ecs:us-east-1:123:task/prod/abc123 # paste a raw ARN, no scheme needed
resolog ls sfn-execution <state-machine-arn> # list executions, pick one
resolog ls batch-job <queue>
resolog ls ecs-task <cluster>
resolog ls log-group /aws/lambda/
resolog pick --backend poll --sort sfn-execution <state-machine-arn> # list, choose, tail in one steppick shows the same listing as ls, lets you choose one entry interactively
(through fzf when it is installed, else a numbered prompt), and tails it —
tail flags come first, then <scheme> [filter].
References are a raw resource ARN, <scheme>:<rest>, or a bare log group name.
A raw ARN dispatches by its service, so you can paste one as-is. Schemes:
log-group, sfn-execution, sfn-state-machine (tails its most recent
execution), batch-job, batch-queue (every active job in a queue), lambda,
ecs-task (one stream per container), ecs-service (every task in a
service), codebuild (a build id/ARN or a project name), cfn-stack
(everything a CloudFormation stack logs).
| Flag | Description |
|---|---|
--backend live|poll |
live (StartLiveTail, default) or poll (FilterLogEvents, historical) |
-f |
keep polling for new events (poll backend) |
--since |
only fetch events at/after this time: a duration ago (10m, 1h30m) or an absolute time (RFC3339, 2026-07-05 09:00, 15:04) |
--until |
only fetch events at/before this time; pair with --since for a window. Same forms as --since |
--filter |
CloudWatch Logs filter pattern, applied server-side (both backends) |
--grep |
client-side regexp; only events whose message matches are shown |
--output text|json |
json writes one compact JSON object per line ({"time","group","stream","label","message"}); -t/--no-color are ignored there |
--fields |
comma-separated JSON fields (dot paths like log.level) to show instead of the full message; text output only. Non-JSON lines pass through unchanged |
--sort |
poll only; buffer and print in time order across streams (see Ordering) |
-t |
show timestamps |
--no-color |
disable colored output |
--filter and --grep compose: --filter narrows what CloudWatch sends over
the wire, --grep further narrows what gets printed. With --fields, --grep
matches the extracted view — what you see is what it greps.
By default lines print in arrival order — interleaved across sources, like
docker compose logs. Across different streams this is not time order.
--sort (poll, finished resources only) buffers everything and prints in time
order by each resource's own reported clock. Honest caveats:
- Clocks differ across resources; resolog never claims causal order between them.
- CloudWatch ingestion lags, so a finished task's last lines (e.g. a failure stack trace) can land late and fall outside the window.
- A whole-log-group source (e.g. a Lambda tailed by group) can include lines from other invocations within the window.
--sortwaits for the fetch to finish before printing; on Ctrl-C it flushes the ordered prefix it has so far.--sortholds everything in memory; it errors past--sort-maxevents (default 1,000,000) rather than risk running out of memory. Narrow with--since/--until.
Live output — the newest lines as they arrive — is intentionally never
reordered. Without --sort, streaming uses a fixed amount of memory (a page at
a time) no matter how much you tail.
resolog is also a Go library; the CLI is just one consumer. See the package docs.
res, _ := sfn.New(sfnClient, sfn.WithBatchResolver(batch.New(batchClient))).
Resolve(ctx, executionARN)
sink := resolog.NewRenderer(os.Stdout, true, false)
resolog.Tail(ctx, res, livetail.New(logsClient), sink)Unit tests use fakes; integration/ (build tag integration) exercises the
loggroup + poll backend, the sfn resolver, the sfn-state-machine resolver, and
the cfn-stack resolver against a real-ish AWS API via
LocalStack (community edition).
Run them with:
make integration-test # spins up LocalStack in Docker, runs the tests, tears downThey're skipped by plain go test ./... and require Docker; see
integration/*_test.go for what's covered, and
integration/unsupported_test.go for the resolvers LocalStack community can't
back (Batch, ECS, CodeBuild are Pro-only there).
v0, still young, but now exercised against real production AWS workloads, not just unit tests. Extracted from batchkoi's log tailer.
MIT