logs is a command of the Extension.js CLI. Extension.js builds and runs browser extensions from one manifest.json for Chrome, Edge, Firefox, and Safari, and npx extension@latest dev starts the session that this command reads.
logs reads the log records that a dev session collects from your extension: background, content scripts, popup, options, and the rest. One command shows them all, merged in order, without opening a single DevTools window.
When to use logs
- You want console output from the background worker and a content script in one stream.
- An agent or script needs machine-readable log records instead of screen-scraped terminal text.
- You want to check what an extension logged earlier without re-triggering the behavior.
Usage
logs prints the records already on disk and exits. Add --follow to stay attached and stream new records live.
Arguments and flags
Choosing a
--level includes that level plus everything more severe. --level warn shows warn and error. Plain console.log records count as info.
From 4.1.20, a Safari session is read the same way. extension logs --browser safari prints the background and content rows of a dev --browser safari session, and --follow streams them. See Building Safari extensions.
One-shot mode
Without--follow, the command reads dist/extension-js/<browser>/logs.ndjson directly and needs no live connection. The dev session appends every record to that file, so a one-shot read works even after you close the browser.
If the file does not exist, logs prints a hint to start extension dev first and exits with code 1. Machine formats also emit a failure envelope with code E_LOGS_NOT_FOUND (see Result envelope).
When the session dropped records, the file holds a gap record in their place. An unfiltered ndjson or json read prints that record on stdout as it is. Once any filter narrows the read (--context, --level, --since, --url, --tab), stdout carries log records only, and the loss is reported as one line on stderr with the drop count and reason, in every output format.
Follow mode
--follow looks up the session’s readiness contract, connects to the control channel as a log consumer, and streams records as they happen. It needs a running dev session, but no unlock flag: log consumption is always allowed.
logs prints a one-line gap notice on stderr with the drop count and reason.
When you stop the stream with Ctrl+C, machine formats print one terminating success envelope with status interrupted, so a consumer can tell a clean stop from a crash. The exit code is 0.
If no session is found for the chosen browser, the command fails with E_SESSION_NOT_FOUND and names the extension dev command to run.
Output formats
Pretty mode prints one line per record:ndjson prints one raw JSON record per line, ideal for jq and log shippers. json pretty-prints each record over multiple lines. When stdout is not a TTY, ndjson is already the default, so piping needs no extra flag:
Tell content script rows from service worker rows
Withoutlogs, the two outputs live in two DevTools windows: the service worker console behind the extensions page, and the content script console inside the page that it runs in. logs merges them into one stream and labels every record with the context that produced it, so you can split them again on your terms.
The label is the context field, printed in parentheses in pretty mode. The dev session assigns it per compiled entry. Output under content_scripts/ is content, and the background service worker (or the Firefox background script) is background. action/index.js is popup, options/index.js is options, and so on through the list that --context accepts.
console.log call prints as LOG and is filtered as info, so --level info keeps it and --level warn drops it.
Two things differ between the rows beyond the label. A content script record also carries the page that it ran in (url, tabId, frameId). The content script relays its output to the service worker, and the session records the sender. A background record has no page, so it carries none of those fields. That gives you two ways to separate the streams:
--contextchooses by label.--context backgroundshows the service worker alone,--context contentthe content scripts alone, and a comma-separated list combines them. A name outside the list is refused.--urland--tabchoose by page. A background row has no URL or tab, so either filter drops it and leaves content rows only.
error rows with the stack appended to the message, so --level error across both contexts is the fastest way to see which side threw.
To split the stream in a script, read ndjson and branch on the context field. The url field is absent on background rows:
dev prints in its own terminal through --logs and --log-context. See dev.
Examples
Show only errors and warnings from content scripts on a specific site:Next steps
- Diagnose a session that
logs --followcannot reach withdoctor. - Grab a DOM snapshot with the recent console tail in one call with
inspect. - Read the wider debugging workflow in Debugging.
- Review the machine failure format in Result envelope.

