Add -n/--tail support to lstk logs#359
Conversation
cff248a to
b3e7ad8
Compare
80c342e to
52b13c3
Compare
a8809b2 to
a3a8697
Compare
There was a problem hiding this comment.
The tail value is computed before filtering, so the output is confusing in some scenarios. For example, when the latest log entries are of type INFO:
$ ./bin/lstk logs --verbose
emulator |
emulator | LocalStack version: 2026.6.3
emulator | LocalStack build date: 2026-07-15
emulator | LocalStack build git hash: 7bf63b7c4
emulator |
emulator | 2026-07-21T12:19:57.291 INFO --- [ MainThread] l.p.c.b.licensingv2 : Successfully activated cached license cc9b6903-7df5-4afb-9d50-f27d7c
9a4cf5:enterprise from /var/lib/localstack/cache/license.json 🔑✅
emulator | 2026-07-21T12:20:00.115 INFO --- [ MainThread] l.p.c.extensions.plugins : loaded 0 extensions
emulator | Ready.
emulator | 2026-07-21T12:20:02.016 INFO --- [et.reactor-2] localstack.request.http : GET /_localstack/pods/state/metamodel => 200
emulator | 2026-07-21T12:20:02.017 INFO --- [et.reactor-1] localstack.request.http : GET /_localstack/pods/state/metamodel => 200
emulator | 2026-07-21T12:20:02.017 INFO --- [et.reactor-0] localstack.request.http : GET /_localstack/pods/state/metamodel => 200
emulator | 2026-07-21T12:20:02.024 INFO --- [et.reactor-1] localstack.request.http : GET /_localstack/pods/state/metamodel => 200
emulator | 2026-07-21T12:20:02.025 INFO --- [et.reactor-2] localstack.request.http : GET /_localstack/pods/state/metamodel => 200
emulator | 2026-07-21T12:20:02.026 INFO --- [et.reactor-0] localstack.request.http : GET /_localstack/pods/state/metamodel => 200For reference, this is the $ ./bin/lstk logs output:
emulator |
emulator | LocalStack version: 2026.6.3
emulator | LocalStack build date: 2026-07-15
emulator | LocalStack build git hash: 7bf63b7c4
emulator |
emulator | 2026-07-21T12:19:57.291 INFO --- [ MainThread] l.p.c.b.licensingv2 : Successfully activated cached license cc9b6903-7df5-4afb-9d50-f27d7c
9a4cf5:enterprise from /var/lib/localstack/cache/license.json 🔑✅
emulator | 2026-07-21T12:20:00.115 INFO --- [ MainThread] l.p.c.extensions.plugins : loaded 0 extensions
emulator | Ready.
Now, if I run ./bin/lstk logs --tail 1 the output is empty as it's tailed on the full set first, and then filtered afterwards. What I expect, as a user, is the following:
$ ./bin/lstk logs --tail 1
emulator | Ready.
a3a8697 to
e535afa
Compare
|
250f218 is unrelated to the tail fix, so a quick note on why it's in here. While verifying the tail change, a test in this package wedged for the full 10 minute timeout instead of failing. The log stream goroutine called No behavior change, and I couldn't reproduce it against the real Docker path, so it isn't a user-facing bug. I'd still keep it here because this PR made the Happy to split it out if you'd rather keep this PR focused. What do you think? |
Motivation
DevRel parity report: the Python CLI supports
localstack logs -n 20to inspect the last N log lines, whilelstk logsonly offered all-or-follow — cumbersome on a busy emulator and a migration papercut.-n, --tail(defaultall) is also the exact convention ofdocker logs,docker compose logs, andnerdctl logs.Changes
-n, --tailtolstk logs(string, default"all", wording identical todocker logs); validated at the command boundary — non-negative integer orallcontainer.Logs/ui.RunLogsintoruntime.StreamLogs, which previously hardcodedTail: "all"MockRuntimefor theStreamLogssignature change--tail Ncounts the lines lstk printslstk logsfilters noisy loggers (request logs, internal handlers) before printing. Handing the limit straight to the runtime would count lines that the filter then drops, so--tail 1printed nothing whenever the newest raw line happened to be a filtered one — a burst of request logs was enough to swallow the whole limit.So the limit is now applied after filtering:
shouldFilteris a pure, order-preserving predicate, so the last N survivors of a long-enough suffix are the last N survivors overall.N × 8raw lines and grows geometrically, only when it turned out to be too heavily filtered (falling back to the full history past 100k).-v): every line is printed, so the runtime's own tail is already exact — delegated to it, which is also far cheaper.--follow: the filtered backlog is emitted first, then the live stream starts attail=0.One trade-off worth a look: the follow path now issues two runtime calls, so lines written in the sub-millisecond gap between them aren't displayed. Making that exact needs
Timestamps: trueplusSince, which means parsing and stripping a timestamp prefix off every line — happy to add it if we'd rather not have the gap. It's called out in a comment at the call site.Tests
--tail 0, and verbose delegating to the runtime--tail/-nlimit output,--tail 1survives a burst of filtered lines, default shows all lines, invalid values are rejected with a helpful error, and--follow --tailstarts from the tail--tail 0,--tail all, over-count values, and combined-fn 3Closes PRO-358