> ## Documentation Index
> Fetch the complete documentation index at: https://extension.js.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Logs command for reading dev session output

> Print or stream logs from every context of a running Extension.js dev session. Filter by context, level, URL, or tab, and pipe ndjson to tools.

Print or stream logs from every context of a running dev session.

`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`](/docs/commands/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.

<Frame>
  <iframe className="w-full aspect-video rounded-xl" src="https://www.youtube-nocookie.com/embed/R5RZ1vxLBTA?rel=0" title="Extension.js: extension logs --follow" loading="lazy" allow="encrypted-media; picture-in-picture; fullscreen" allowFullScreen />
</Frame>

## 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

<CodeGroup>
  ```bash npm theme={null}
  extension logs [project-path] [options]
  ```

  ```bash pnpm theme={null}
  extension logs [project-path] [options]
  ```

  ```bash yarn theme={null}
  extension logs [project-path] [options]
  ```

  ```bash bun theme={null}
  extension logs [project-path] [options]
  ```

  ```bash deno theme={null}
  extension logs [project-path] [options]
  ```
</CodeGroup>

By default, `logs` prints the records already on disk and exits. Add `--follow` to stay attached and stream new records live.

## Arguments and flags

| Flag | What it does | Default |
| - | - | - |
| `[project-path]` | Path to the extension project root. | `process.cwd()` |
| `--browser <browser>` | Which session to read (`chrome`, `chromium`, `edge`, `firefox`, `safari`). | `chromium` |
| `--follow` | Stream live over the control channel instead of printing and exiting. | off |
| `--context <list>` | Comma-separated contexts (`background`, `content`, `page`, `popup`, `options`, `sidebar`, `devtools`, `newtab`, `history`, `bookmarks`). A name outside this list is refused with `E_INVALID_OPTION`. | all contexts |
| `--level <level>` | Minimum severity (`off`, `error`, `warn`, `info`, `debug`, `trace`, `all`). | `all` |
| `--signals-only` | Experimental. Show only structured `dx.signal` diagnostics. No emitter ships yet, so this currently prints nothing. | off |
| `--since <seq\|iso>` | Only show events after this sequence number, or after this ISO timestamp by the event clock. | unset |
| `--url <glob\|substring>` | Only events whose URL or hostname matches (glob with `*`, or a plain substring). | unset |
| `--tab <id>` | Only events from this tab id. | unset |
| `--output <pretty\|json\|ndjson>` | Output format. | `pretty` on a TTY, `ndjson` when piped |

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](/docs/browsers/safari).

## 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](/docs/contracts/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.

```bash theme={null}
extension logs --follow --context background,content --level info
```

If the stream falls behind and the session drops records, `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:

```plaintext theme={null}
[142] INFO (background) message text from the worker
[143] ERROR (content) E_SOMETHING failed to reach the page
    ↳ remediation hint, when the record carries one
```

`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:

```bash theme={null}
extension logs --context content | jq -r '.messageParts | join(" ")'
```

## Tell content script rows from service worker rows

Without `logs`, 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.

```plaintext theme={null}
[142] LOG (background) [From the background context] Hello from the background worker/script!
[143] LOG (content) [From the page context] Hello from content_scripts!
```

A `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:

* `--context` chooses by label. `--context background` shows the service worker alone, `--context content` the content scripts alone, and a comma-separated list combines them. A name outside the list is refused.
* `--url` and `--tab` choose by page. A background row has no URL or tab, so either filter drops it and leaves content rows only.

```bash theme={null}
extension logs --context background --follow
extension logs --context content --tab 7 --follow
```

Run those in two terminals to watch each side live, or read them one shot from the same file. Uncaught errors and unhandled rejections from either context arrive as `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:

```bash theme={null}
extension logs --output ndjson | jq -r '[.context, (.url // "-"), (.messageParts | join(" "))] | @tsv'
```

The same labels and filters apply to the stream that `dev` prints in its own terminal through `--logs` and `--log-context`. See [`dev`](/docs/commands/dev).

## Examples

Show only errors and warnings from content scripts on a specific site:

```bash theme={null}
extension logs --context content --level warn --url "*.example.com"
```

Resume reading after a known record, useful for polling agents:

```bash theme={null}
extension logs --since 142 --output ndjson
```

## Next steps

* Diagnose a session that `logs --follow` cannot reach with [`doctor`](/docs/commands/doctor).
* Grab a DOM snapshot with the recent console tail in one call with [`inspect`](/docs/commands/inspect).
* Read the wider debugging workflow in [Debugging](/docs/debugging).
* Review the machine failure format in [Result envelope](/docs/contracts/result-envelope).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.