next.js
94f8aecf - feat: forward browser errors/logs to terminal (#80909)

Commit
239 days ago
feat: forward browser errors/logs to terminal (#80909) Closes NEXT-4534 This PR introduces the ability for next.js to forward logs, errors, and unhandled rejections from the browser to the terminal the dev server is running in (behind an experimental flag) # Explanation The 2 main components of this pr are the client side error accumulation logic, and the ingest handling on the other side of the hmr socket. We listen on the existing hmr socket to send batched logs, errors, and uncaught rejections the frame after they were captured. All forwarded data is sent with metadata so we can have reconstruct the log with an equivalent level of information to the browser- since we expect AI agents that can't access the browsers to be consumers of this feature (and it's generally useful). All foreign data created by the user in the browser is serialized using `safe-stable-serializer`, a popular serializer [used by other logging libraries](https://www.npmjs.com/browse/depended/safe-stable-stringify), like [pino](https://github.com/search?q=repo%3Apinojs%2Fpino+safe-stable&type=code) (safety, determinism, fast). We also have a light shim on top of json serialization to handle displaying custom data representations that either wouldn't survive serialization (undefined) or we want to present to users in a custom format (throwing proxies, promises, ...) On the dev server server, we (bespoke) deserialize, source map, format, and log. I tried to share as much logic as I could with error dev overlay to avoid feature drift since they are very similar implementations other than the render target # Explicitly covered cases - console table - shows as `[browser]\n<table>\n(<source location>)` - console trace - shows as `[browser] arg1 arg2 ...\n<stack trace>\n([source mapped location of log]`)` - trace is source mapped - ignored frames are shown, incase people explicitly want the full trace - console dir - shows as `[browser] arg1 arg2 ([source mapped location of log])` - we need to explicitly capture stdout and rewrite it when we call nodes `console.dir` to prefix and postfix with [browser] and and (`<source mapped location of log>`) without adding newlines (we could do this for console.table but it makes sense to keep the prefix and postfix on new lines) - console error - `[browser] arg1 arg2 \n codeblock + source mapped stack of console.error ([source mapped location of console.error])` - if there are any `Error` values present we wont show the stack and code block of console.error since it's overwhelming (this is fine since we still tell the user where the `console.error` is with the appended location) - rejected promises that have `Error`'s - behave identical to console.error, but is prepended with `⨯ unhandledRejection` - the `Error`'s render with their source mapped stack + ignored frames - no error stack can be automatically appended where the promise rejected - rejected promises that have non `Error` values - prepended with `⨯ unhandledRejection: ${error.name}: ${error.message}` - everything is logged in red - no error stack can be automatically appended where the promise rejected - on caught error - prepended with `Uncaught ${errorName}: ${errorMessage}` - stack attached to error is source mapped + ignored frames are not shown - everything is logged in red but the code block of where the error orginated from - all other console cases - shows as `[browser] arg1 arg2 ([source mapped location of log])` - if an error is passed, we show the error name, message, source mapped stack (colored white, ignored frames not shown), and code block if available (syntax highlighted) - we apply util.format to handle formatted strings - logs captured during RSC rendering - not piped to server, ignored on client Closes NEXT-4534
Author
Parents
Loading