logs 是 Extension.js CLI 的一条命令。Extension.js 从一个 manifest.json 出发,为 Chrome、Edge、Firefox 和 Safari 构建并运行浏览器扩展,npx extension@latest dev 会启动这条命令所读取的会话。
logs 读取 dev 会话从你的扩展里收集到的日志记录:background、content script、popup、options 以及其余上下文。一条命令就能按顺序合并展示全部内容,不用打开任何一个 DevTools 窗口。
什么时候使用 logs
- 你想在同一个流里同时看到 background worker 和某个 content script 的控制台输出。
- agent 或脚本需要机器可读的日志记录,而不是从终端文本里刮取。
- 你想查看扩展早些时候记录了什么,而不必重新触发一遍那个行为。
用法
logs 打印磁盘上已有的记录然后退出。加上 --follow 可以保持连接,实时流式读取新记录。
参数与 flag
选择某个
--level 会包含该级别以及所有更严重的级别。--level warn 会显示 warn 和 error。普通的 console.log 记录算作 info。
从 4.1.20 起,Safari 会话的读取方式完全相同。extension logs --browser safari 会打印 dev --browser safari 会话中 background 与 content 的日志行,加上 --follow 就是实时流。详见 构建 Safari 扩展。
一次性模式
不加--follow 时,命令直接读取 dist/extension-js/<browser>/logs.ndjson,不需要任何实时连接。开发会话会把每一条记录追加到那个文件,所以即使你已经关掉浏览器,一次性读取依然有效。
如果该文件不存在,logs 会提示你先启动 extension dev,并以退出码 1 退出。机器可读格式还会输出一个错误码为 E_LOGS_NOT_FOUND 的失败信封(参见 结果信封)。
当会话丢弃过记录时,文件里会在对应位置保留一条 gap 记录。不带任何过滤的 ndjson 或 json 读取会把这条记录原样打印到 stdout。一旦有任何过滤条件收窄了读取范围(--context、--level、--since、--url、--tab),stdout 上就只有日志记录,丢失的情况会在 stderr 上以一行报告,带上丢弃条数和原因,所有输出格式都如此。
跟随模式
--follow 会查找会话的就绪契约,以日志消费方的身份连接控制通道,并在记录产生时把它们流式输出。它需要一个正在运行的开发会话,但不需要任何解锁 flag:日志消费始终是允许的。
logs 会在 stderr 上打印一行提示,说明丢弃的条数和原因。
当你用 Ctrl+C 停止这个流时,机器可读格式会打印一个状态为 interrupted 的终止成功信封,这样消费方就能区分正常停止与崩溃。退出码为 0。
如果找不到所选浏览器对应的会话,命令会以 E_SESSION_NOT_FOUND 失败,并指名应当运行的 extension dev 命令。
输出格式
pretty 模式每条记录打印一行:ndjson 每行打印一条原始 JSON 记录,非常适合 jq 和日志采集器。json 会把每条记录格式化成多行。当 stdout 不是 TTY 时,ndjson 本来就是默认值,因此走管道不需要额外的 flag:
区分 content script 的行与 service worker 的行
没有logs 时,这两种输出分别住在两个 DevTools 窗口里:service worker 的控制台藏在扩展管理页后面,content script 的控制台则在它所运行的页面里。logs 把它们合并成一条流,并给每条记录打上产生它的上下文标签,这样你就能按自己的方式再把它们分开。
这个标签就是 context 字段,pretty 模式下打印在括号里。开发会话按编译后的条目分配它。content_scripts/ 下的输出是 content,后台 service worker(或 Firefox 的后台脚本)是 background。action/index.js 是 popup,options/index.js 是 options,其余依此类推,覆盖 --context 接受的整个列表。
console.log 调用打印为 LOG,按 info 过滤,因此 --level info 会保留它,--level warn 会丢弃它。
除了标签之外,这两种行还有两处不同。content script 的记录还带有它所运行的页面(url、tabId、frameId)。content script 把输出转发给 service worker,而会话记录下了发送方。后台记录没有页面,所以这些字段一个都没有。这给了你两种分离两条流的方式:
--context按标签选择。--context background只显示 service worker,--context content只显示 content script,逗号分隔的列表则把它们组合起来。列表之外的名字会被拒绝。--url和--tab按页面选择。后台行没有 URL 也没有标签页,所以任一过滤器都会把它丢掉,只留下 content 行。
error 行到达,消息后面附有堆栈,因此跨两个上下文使用 --level error 是看清哪一侧抛错的最快方式。
要在脚本里拆分这条流,请读取 ndjson 并按 context 字段分支。后台行上没有 url 字段:
dev 通过 --logs 和 --log-context 在自己终端里打印的流。参见 dev。

