Free browser tool

Paste CloudWatch logs. Get invocations back.

CloudWatch scatters a single Lambda execution across dozens of unrelated lines. This stitches them back together by request ID — with duration, memory, cold starts, timeouts and stack traces where they belong. No signup, no upload, no limit.

Nothing is uploaded. Every line you paste is parsed by JavaScript running in this tab, on your machine. Your logs are never sent to a server, never written to the URL, never stored, and never included in analytics — there is no server here to send them to. Read the parser yourself at /stitch/parser/, or just view source.

Stitches as soon as you paste — there is no button to press.
No logs handy?

One paste vs. every function

This is one paste. LogStitch does it live.

The stitching above is the part LogStitch does in the first millisecond. The Mac app does it continuously, for every function in your account, and then builds on it.

Live tail

Watch invocations assemble as they happen, instead of pasting after the fact. Cards finalize the moment their REPORT line lands.

Cross-function stitching

Follow one request through every Lambda it touched, correlated on X-Ray trace IDs and your own correlation fields — laid out as swim lanes.

Cold-start analytics

Init duration over time, per runtime and per version, so you can see whether a change actually moved your cold starts.

Cost projection

GB-seconds and spend per function, with right-sizing based on the memory-versus-duration curve rather than headroom alone.

MCP server for Claude

A built-in MCP server so Claude and other AI tools can query your stored invocations directly while debugging.

Local-first, always

Same promise as this page, at a larger scale: logs are fetched straight from your AWS account to your Mac. Nothing transits a vendor.

Why CloudWatch splits one invocation into twenty lines

A Lambda invocation is a single unit of work: a request arrives, your handler runs, it finishes. CloudWatch does not store it that way. It stores a flat, timestamp-ordered stream of individual log events, and when your function runs concurrently — which is the whole point of Lambda — the events from every simultaneous execution are interleaved into the same river.

Each execution contributes at least four platform lines of its own: an optional INIT_START when a new container is initialized, a START, an END, and a REPORT carrying the duration, billed duration, memory size, peak memory used, and — only on a cold start — the init duration. In between sit however many lines your own code logged. Read top to bottom, they look like one confused program. They are actually five or fifty separate ones, spliced together by nothing more meaningful than when each line happened to be written.

Stitching is the act of undoing that. Every platform line carries the request ID, so START, END and REPORT can be matched into an invocation boundary. Your own lines are then attributed to whichever invocation was open on that log stream at that moment — or, better, matched directly when the line carries the request ID itself, which CloudWatch's tab-delimited format and most structured loggers both provide. Do that, and a wall of noise becomes a list of executions you can actually reason about: this one was slow, that one was cold, this one threw.

What this tool works out for you

Cold starts are reported with the evidence behind them. An Init Duration on the REPORT line is proof. An INIT_START immediately before the START is strong. Being the first invocation on a log stream is a guess — so it is labelled a guess, and it is switched off entirely when your paste carries no stream names, rather than inventing a cold start out of nothing.

Timeouts are read from the REPORT line's own status field where the runtime provides one, and otherwise from the Task timed out after N seconds line. Out-of-memory kills are flagged when peak memory reached the configured size, which is the signature of the kill you would otherwise only find by noticing the invocation simply stopped. Errors are detected from Node, Python, Java, Go and .NET exception shapes, from the Lambda runtime's own unhandled-fault envelope, and from structured error fields — with one deliberate rule: an error object logged at WARN or INFO is not treated as a failure, because logging it at that level was your decision.

Multi-line output stays whole. A Java stack trace, a Python traceback, a Go goroutine dump or a block of pretty-printed JSON arrives from CloudWatch as many separate events, and is reassembled here into one entry rather than being scattered across the timeline. And nothing is thrown away: any line that cannot be attributed to an invocation is listed separately, with the reason.

If you do this more than once

A paste is a fine way to read one incident. It is a poor way to work. If you find yourself copying out of the CloudWatch console regularly, the same stitching runs continuously in LogStitch for Mac against every function in your account — and it is worth comparing against the CloudWatch console, Logs Insights, and the hosted observability platforms before you decide what you actually need.

Questions

The obvious ones

Does this upload my logs?
No. Parsing happens entirely in your browser, in a Web Worker on your own machine. Your log text is never sent to a server, never written to the URL, never stored in localStorage, and never included in analytics. There is no server-side component to this page — it is a static HTML file and a JavaScript module. Read the parser at /stitch/parser/ if you would rather check than take our word for it.
What log formats are supported?
The format is detected automatically. Raw pasted CloudWatch text; the console's copy output and S3 export files with leading ISO 8601 timestamps; the console's CSV search-results download; Logs Insights CSV exports; the JSON from aws logs filter-log-events and aws logs get-log-events; newline-delimited JSON; and gzipped versions of any of them. Paste it, drop the file in, or use the file picker.
Does it work with structured JSON logs?
Yes. JSON lines are read for level, message, error, request ID and trace ID across the conventions used by AWS Lambda Powertools, Pino, Bunyan, Winston, Serilog and structlog. Lambda's Advanced Logging Controls JSON format is supported too, including the platform.start, platform.runtimeDone and platform.report records — so invocations stitch correctly even when your function emits no plain-text platform lines at all.
Is there a limit?
None imposed. Parsing runs off the main thread with a progress bar, and around 50,000 lines goes through without the tab locking up. The real ceiling is your own machine's memory, since nothing leaves it. Very large exports are usually easier to drop in as a file than to paste, and .gz files are decompressed here in the browser.
What happens to lines that don't belong to any invocation?
They appear in an explicit "unattributed" group, each with the reason it could not be placed — usually no request ID on the line and no timestamp falling inside any invocation's window. Nothing you paste is silently dropped; every line ends up either inside an invocation or in that group.
Is this the same parser as the Mac app?
It is a hand port of it, not a shared library — the app is written in Swift. The parsing rules, the grouping model (including the way Lambda's async retries reuse one request ID), the cold-start priority order and the error-detection precedence all mirror the app's, and the same test fixtures run against both. Where the two deliberately differ, the reasons are written down in the repo alongside the parser.
Every function, not one paste

Stop copying out of the console.

LogStitch runs this on every Lambda in your AWS account, continuously, on your Mac — with live tail, cross-function correlation, cold-start and cost analytics, and an MCP server so Claude can debug alongside you. One-time purchase, 14-day trial, logs never leave your machine.