To capture a page’s browser output, attach Puppeteer listeners immediately after creating the Page and before goto, clicks, or other actions. Use console for JavaScript console calls, pageerror for uncaught exceptions, error for page crashes, requestfailed for transport failures, and response for HTTP status errors such as 404 and 503.
Complete Puppeteer logger
This runnable Node.js example records structured JSON to standard output. It preserves console type, text, source location and serializable arguments, while also recording uncaught exceptions, crashes, failed network requests and HTTP responses with status 400 or higher.
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
const page = await browser.newPage();
page.on('console', async msg => {
const values = [];
for (const arg of msg.args()) {
try {
values.push(await arg.jsonValue());
} catch {
values.push('[unserializable remote value]');
}
}
console.log(JSON.stringify({
kind: 'console',
type: msg.type(),
text: msg.text(),
location: msg.location(),
args: values,
url: page.url(),
timestamp: new Date().toISOString()
}));
});
page.on('pageerror', error => {
console.error(JSON.stringify({
kind: 'pageerror',
message: error instanceof Error ? error.message : String(error),
stack: error instanceof Error ? error.stack : undefined,
url: page.url(),
timestamp: new Date().toISOString()
}));
});
page.on('error', error => {
console.error(JSON.stringify({
kind: 'page-crash',
message: error.message,
stack: error.stack,
url: page.url(),
timestamp: new Date().toISOString()
}));
});
page.on('requestfailed', request => {
const failure = request.failure();
console.error(JSON.stringify({
kind: 'requestfailed',
url: request.url(),
errorText: failure?.errorText ?? null,
timestamp: new Date().toISOString()
}));
});
page.on('response', response => {
if (response.status() >= 400) {
console.error(JSON.stringify({
kind: 'http-error',
status: response.status(),
url: response.url(),
timestamp: new Date().toISOString()
}));
}
});
await page.goto('https://example.com');
await browser.close();
The handlers are registered before navigation, so messages emitted during document parsing, script execution and resource loading are not missed. The optional timestamp is generated in Node.js; add a test or page identifier when several pages write to the same log.
What each event captures
console: page console APIs
Puppeteer emits a console event when page JavaScript calls console.log, console.info, console.warn, console.error, console.debug and related APIs. The callback receives a ConsoleMessage.
#1 Best Overall
msg.text() is convenient for readable output. Use msg.type() to retain severity and msg.location() for the source URL, line and column when available. msg.args() exposes the original remote values, which is important for objects, arrays and multiple arguments:
page.on('console', async msg => {
console.log(msg.type(), msg.text(), msg.location());
for (const handle of msg.args()) {
try {
console.log(await handle.jsonValue());
} catch {
console.log('[unserializable remote value]');
}
}
});
Do not assume every argument can be converted to JSON. DOM nodes, cyclic objects, symbols and other remote values can make jsonValue() reject. Catching the conversion error lets the event stream continue.
pageerror: uncaught page exceptions
pageerror is for an exception that escapes page JavaScript without being caught. Record both message and stack; the stack usually identifies the failing script and line. This event is distinct from a deliberate console.error(), which is only a console message.
error: a page crash
The Page-level error event signals that the page crashed. Treat it as a high-severity lifecycle failure rather than an ordinary JavaScript exception. A crash can terminate scripts and prevent later events, so write the record synchronously enough to survive process cleanup and include the current URL.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
requestfailed: no HTTP response was completed
requestfailed covers transport-level failures such as connection errors, DNS problems, aborted requests and timeouts. Call request.failure() for the browser’s error text, but guard the result: the API permits null.
Rank #2
response: HTTP status failures, including 404
A server can return a valid HTTP response with status 404, 500 or 503. That request did not fail at the transport layer, so it does not produce requestfailed. Inspect every response’s status explicitly when you need HTTP error reporting. Filtering at 400 keeps normal traffic out of the log; remove the filter if you need a complete response audit.
Workers and other signals
For pages that rely on dedicated WebWorkers, listen for workercreated and workerdestroyed and record each worker’s URL and lifecycle. Worker activity can explain behavior that is absent from the main page’s console stream.
Attach listeners before anything that can emit
Install listeners directly after newPage(), before goto, clicks, form submissions, evaluate, waits that trigger application work or any custom interaction. Registering after navigation creates a race: early parser errors and startup logs are already gone.
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteFor multi-page tests, apply the same setup whenever a new Page is created. Keep a stable correlation ID in each record so logs from tabs, popups and parallel tests can be separated later:
function addDiagnostics(page, testId) {
page.on('console', msg => {
console.log(JSON.stringify({ testId, kind: 'console', type: msg.type(), text: msg.text(), url: page.url() }));
});
page.on('pageerror', error => {
console.error(JSON.stringify({ testId, kind: 'pageerror', message: String(error), stack: error.stack, url: page.url() }));
});
}
Choosing a logging design
| Design | Coverage | Payload | Best use |
|---|---|---|---|
| Minimal forwarding | Console only | msg.text() |
Interactive debugging and quick local checks |
| Structured page logger | Console and uncaught exceptions | Types, text, arguments, locations and stacks | CI diagnostics and searchable logs |
| Full diagnostic logger | Console, exceptions, crashes and network | Structured records with URLs, statuses and failure text | End-to-end triage and regression investigation |
Minimal output is easier to read and produces less volume. Structured output costs more CPU and storage, but it preserves the context needed to diagnose intermittent failures. If many pages run concurrently, write newline-delimited JSON to a stream or logging service rather than retaining records in an ever-growing array.
Making console arguments safe and useful
- Always catch failures from
jsonValue(); one unusual argument should not break the listener. - Keep handlers lightweight. Avoid expensive formatting, screenshots or synchronous disk operations inside every event callback.
- Use
msg.text()as a fallback even when argument serialization fails. - Include a Node-side timestamp and correlation ID when combining logs from several pages or test workers.
- Redact credentials, authorization headers and personal data before shipping logs outside the test environment.
Common problems and fixes
“The console listener missed startup messages”
The listener was attached after goto or after an interaction. Move all handlers immediately after newPage(). Also ensure the page is not created by a helper that navigates before returning it.
“A 404 never appears in requestfailed”
This is expected. A 404 is an HTTP response, not a transport failure. Use the response listener and test response.status() >= 400.
“request.failure() is null”
Failure details are optional. Use request.failure()?.errorText ?? null and retain the request URL even when no text is supplied.
“Structured console values cannot be serialized”
Remote objects can contain unsupported or cyclic values. Catch jsonValue() errors and store a marker, while preserving msg.text(). If you need a specific object, have the page emit a JSON-safe representation deliberately.
“The page crashed and later events disappeared”
Handle the Page error event separately and treat it as terminal for that page. Flush your logger, capture the crash record and create a fresh page if the test can safely continue.
Rank #4
“Logs are too noisy for CI”
Keep the full event stream in an artifact, but print a concise summary to the console. Filter known third-party URLs or console types only after confirming that the filtered signals are not part of the failure you are investigating.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Scan for outdated or missing drivers - takes under a minute3Repair Windows errors before they cause bigger problems“The test hangs after adding logging”
Do not await long-running work from an event handler that the page itself is waiting on. Handlers should enqueue data and return quickly. A bounded queue or asynchronous log writer prevents backpressure from blocking browser events.
Boundaries: what this does not capture
These Page events cover documented page-level console, exception, crash and network signals. They are not a promise to capture every browser diagnostic channel. Browser protocol messages, service-worker diagnostics and application-specific telemetry may require Chrome DevTools Protocol listeners or instrumentation inside the application. Add those channels only when the failure you need is outside the Page event model.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Or skip the browser setup
If your objective is a clean visual record rather than JavaScript diagnostics, ScreenshotNeo captures a URL through one HTTP request. It accepts cookie and consent banners as a visitor and removes more than 60 known consent platforms, newsletter popups and chat widgets before the shot; each cleanup step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers. Its MCP server provides take_screenshot, get_page_info and capture_pdf tools for Claude, Cursor and other MCP clients.
See the full parameter reference in the ScreenshotNeo documentation. The same endpoint can return PNG, JPEG, WebP or PDF:
Recommended Free Tools
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
The Free plan includes 1,000 shots per month with no card. Paid plans start at $5 for 3,000 shots; every feature is available on every plan, and yearly billing gives two months free. Create a free ScreenshotNeo account to get started.
Best Value
- Used Book in Good Condition
FAQ
Does page.on('console') include browser-level warnings?
It reports console APIs and related page console messages delivered through Puppeteer’s ConsoleMessage. Browser-internal diagnostics outside the Page event model require additional protocol-level instrumentation.
Should HTTP 3xx responses be logged as errors?
Only if your test defines redirects as failures. The example logs statuses 400 and above; change the predicate to match your policy.
Can I use this logger with TypeScript?
Yes. The event names and methods are the same; add types for Puppeteer’s event objects and for your structured log record, then compile the file before running it.
Why add both pageerror and console?
An uncaught exception and an explicit console.error() represent different failure modes. Keeping both lets CI distinguish a thrown bug from an application-generated diagnostic.
Frequently Asked Questions
Does page.on('console') include browser-level warnings?
It reports console APIs and related page console messages delivered through Puppeteer’s ConsoleMessage. Browser-internal diagnostics outside the Page event model require additional protocol-level instrumentation.
Should HTTP 3xx responses be logged as errors?
Only if your test defines redirects as failures. The example logs statuses 400 and above; change the predicate to match your policy.
Can I use this logger with TypeScript?
Yes. The event names and methods are the same; add types for Puppeteer’s event objects and for your structured log record, then compile the file before running it.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




