@electron-forge/multi-logger

中文文档
8.0.1发布于 2026/09/30MITTypeScriptES Module构建来源已验证近 30 天下载 109.9万

Display multiple streams of logs as tabs in the terminal

原始使用文档
文档目录7 节
  1. Usage
  2. API
  3. new Logger(options?)
  4. Sharing one logger across packages
  5. Tab
  6. Interactive UI
  7. Plain mode

Multi Logger

Display multiple streams of logs as tabs in the terminal

This module collects multiple logically separated streams of logs ("tabs") and renders them in the terminal the process is already running in. When attached to an interactive terminal it draws a full-screen tabbed UI with ink; otherwise (CI, piped output) it falls back to append-only lines prefixed with a colored tag. Lines may contain ANSI styling, so anything that would render in a terminal can be piped in.

Usage

import Logger from '@electron-forge/multi-logger';

const logger = new Logger({ title: 'My tool' });

const serverTab = logger.createTab('Server');
const frontEndTab = logger.createTab('Front End');

// Everything logged before start() is buffered and shown once rendering begins.
serverTab.setStatus({ state: 'building' });
serverTab.log('listening on :3000');
serverTab.setStatus({ state: 'success', durationMs: 1200 });

// Pipe a child process's stdout/stderr into a tab of its own.
logger.attachProcess(spawn('node', ['server.js']), 'Server');

await logger.start();

// Later: restore the terminal. Safe to call more than once, and it also runs
// on process exit so a crash never leaves the terminal in the alternate screen.
logger.stop();

API

new Logger(options?)

OptionDescription
stdoutStream to render to. Defaults to process.stdout.
stdinStream to read keys from. Defaults to process.stdin.
interactiveWhether the tabbed UI may be used. Defaults to stdout.isTTY && stdin.isTTY && !process.env.CI.
forceMode'ink' or 'plain' to bypass detection.
titleText shown at the left of the tab bar.
keysExtra hotkeys, { key, label, onPress }, listed in the footer.
maxLinesLines kept per tab (and for the merged view). Defaults to 5000.
initialTabName of the tab shown first, or 'all' for the merged view. Defaults to the first tab.
errorSwitchDebounceMsMinimum gap between automatic switches to a tab that failed. Defaults to 15000.
  • logger.createTab(name) returns a Tab.
  • logger.getTab(name) / logger.getTabs().
  • logger.attachProcess(child, name = 'App') pipes the child's stdout and stderr into a tab line by line and marks the tab as exited when it ends. A tab with the same name is reused, so a restarted process keeps its tab. When the child exits, stdin is put back into raw mode in case the child inherited the terminal and reset it on the way out (Node embedders such as Electron do); logger.reassertRawMode() does the same on demand.
  • logger.start() begins rendering. Before this everything is buffered.
  • logger.stop() unmounts the UI and restores the terminal. If the UI never rendered, the buffered lines are written out as plain text instead so nothing is lost.
  • logger.extendOptions({ keys?, title?, initialTab? }) adds to the options the logger was created with: extra keys (already bound keys are left alone) and title / initialTab when none is set yet. It has no effect on the UI once start() has been called.

Sharing one logger across packages

A process can only sensibly draw one UI, so a host and the plugins running inside it should share a logger instead of each creating their own:

import { ensureSharedLogger } from '@electron-forge/multi-logger';

// The host creates it. The first caller decides how it is set up…
const logger = ensureSharedLogger({ title: 'My tool', initialTab: 'App' });

// …and a plugin, later, just adds a tab to it.
const tab = ensureSharedLogger().createTab('Bundler');
  • ensureSharedLogger(options?) returns the process-wide logger, creating it on the first call. On later calls options are merged additively via extendOptions(): extra keys are added, title / initialTab are taken only if none is set yet, and everything else is ignored.
  • getSharedLogger() returns it, or undefined if none has been created.
  • resetSharedLogger() forgets it without stopping it; meant for tests.

The instance is registered under Symbol.for('@electron-forge/multi-logger') on globalThis, so even two copies of this package in one node_modules tree end up sharing it.

Tab

  • tab.log(text) appends text, which may contain many lines and ANSI colors.
  • tab.setStatus({ state, errors?, warnings?, durationMs?, detail? }) where state is one of idle, building, success, warning, error or exited. The status is shown next to the tab name.
  • tab.clear() empties the tab's buffer.

Interactive UI

Electron Forge · webpack  1 Main Process ✔ 1.2s  2 Renderer (web) ⠹  3 Preload (electron-preload) ✖ 2  4 App ●  a All
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
… the active tab's most recent lines, wrapped to the terminal width …
←/→ 1-9 tabs · a all · r restart electron · c clear · f follow · ↑/↓ scroll · q quit
KeyAction
← / →, Tab / Shift+TabCycle through the tabs
1 to 9Jump to a tab
aMerged view of every tab, in arrival order, tagged by tab
↑ / ↓, PgUp / PgDnScroll back through the buffer (stops following)
Home / EndJump to the oldest line / back to the tail
fToggle following the tail
cClear the current tab (or every tab in the merged view)
q, Ctrl+CRestore the terminal and re-raise SIGINT
custom keysWhatever the host registered, e.g. r to restart

The tab bar always fits the terminal: when it gets tight the durations and counts go first, then the title, and finally the chips wrap onto more rows. When a tab's status turns to error the view switches to it, at most once per errorSwitchDebounceMs, so a cascade of failures does not fight the user.

The UI uses the terminal's alternate screen, so whatever was printed before start() is still in the scrollback when it exits, and it prints a one-line status per tab (plus the tail of any tab that ended in an error) on the way out.

Because ink puts stdin into raw mode, line-based input such as typing rs is not seen by the host process while the UI is up; hosts should register an equivalent hotkey via keys instead.

Plain mode

When stdout or stdin is not a TTY, or CI is set, lines are written as they arrive:

[Main Process]   asset index.js 1.2 KiB [emitted] (name: main)
[Main Process]   ✔ compiled in 1.2s
[Renderer (web)] ✖ failed with 1 error in 2.1s

版本与发布记录

@electron-forge/multi-logger 共发布 3 个版本,首次发布于 2026/09/29;过去 12 个月发布了 3 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
8.0.1latest2026/09/301 个月前
8.0.02026/09/301 个月前
0.0.02026/09/291 个月前

关于 @electron-forge/multi-logger 的常见问题

@electron-forge/multi-logger 是什么?

Display multiple streams of logs as tabs in the terminal

如何安装 @electron-forge/multi-logger?

在项目目录运行 npm install @electron-forge/multi-logger,或使用 pnpm add / yarn add / bun add。

@electron-forge/multi-logger 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@electron-forge/multi-logger 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@electron-forge/multi-logger 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >= 22.13.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@electron-forge/multi-logger 可以商用吗?

@electron-forge/multi-logger 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@electron-forge/multi-logger 的下载量有多少?

根据 npm 官方下载统计,@electron-forge/multi-logger 在 2026/10/14 – 2026/11/12 这 30 天内被下载 1,098,895 次,日均约 36,630 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@electron-forge/multi-logger 还在维护吗?

最新版本 8.0.1 发布于 2026/09/30(1 个月前);至今共发布 3 个版本,其中过去 12 个月发布了 3 个;首次发布于 2026/09/29。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。