CC 4.0 协议声明

本节内容派生于以下链接指向的内容 ,并遵守 CC BY 4.0 许可证的规定。

以下内容如果没有特殊声明,可以认为都是基于原内容的修改和删减后的结果。

Logger

使用 Logger 输出消息是一种向用户展示信息的有效方式。

Rspack logger 可以在 loadersplugins 中使用。 生成的日志将作为 Stats 的一部分并且可以在 rspack configuration 中配置。

使用 Rspack 的 Logger 有如下收益:

  • 可统一配置日志的展示级别。
  • 日志可作为 stats.json 的一部分。
  • Stats 的预设可控制日志的输出。
  • 使用 Plugin 可以影响日志的捕获和展示级别。
  • 档使用多个 Plugin 和 Loader 时,提供通用的日志方案。
  • Rspack 的 CLI、UI 工具可能会选择不同的方式来展示日志。
  • Rspack 核心也会输出日志,如一些时间统计数据

通过引入 Rspack 的 Logger API。我们希望能够统一 Plugin 和 Loader 输出日志的方式,以更方便地排查问题和提升开发体验。同时为非 CLI 的集成 Rspack 提供解决方案,如 dashboard 和其他 UI 工具。

避免输出过多日志

避免在输出过多日志!

由于构建过程中使用多个 Plugin 和 Loader,一个 Loader 通常也会处理许多文件。尽量选择一个低等级的日志展示等级来保证日志的信息有效性。

示例

在 Plugin 中使用

插件中有两种方式来获取 Logger:

  1. compilation.getLogger: 日志内容会被存储到 Stats 中,适合输出与一次 Compilation 关联的日志。
  2. compiler.getInfrastructureLogger: 日志内容不会被存储,适合输出不与 Compilation 关联的全局信息。

可以在插件中通过如下代码获得 Logger:

MyPlugin.js
const PLUGIN_NAME = 'my-plugin';
export class MyRspackPlugin {
  apply(compiler) {
    // 获得全局 Logger
    const logger = compiler.getInfrastructureLogger(PLUGIN_NAME);
    logger.log('log from compiler');

    compiler.hooks.compilation.tap(PLUGIN_NAME, compilation => {
      // 获得关联 Compilation 的 Logger
      const logger = compilation.getLogger(PLUGIN_NAME);
      logger.info('log from compilation');
    });
  }
}

在 Loader 中使用

可以在 Loader Context 上获取 Logger:

MyLoader.js
module.exports = function (source) {
  // access Logger from loader
  const logger = this.getLogger('my-loader');
  logger.info('hello Logger');
  return source;
};

Logger API

基础 API

类型: (...args: any[]): void;

按照展示等级从高到低依次为:

  • error: 用于输出错误信息。
  • warn: 用于输出警告信息。
  • info: 用于输出重要的信息,默认会被展示,确保这些信息需要被用户看到。
  • log: 用于输出不重要的信息,默认不会被展示
  • debug: 用于输出调试信息,需要通过特定的开关开启。

使用 compilation.getLogger 时,可通过 stats.loggingstats.loggingDebug 来控制日志的展示等级:

rspack.config.js
module.exports = {
  plugins: [{
    apply(compiler) {
      compiler.hooks.thisCompilation.tap("test plugin", compilation => {
        const logger = compilation.getLogger("TEST");
        logger.error("I am an error");
        logger.warn("I am a warning");
        logger.info("I am an information");
        logger.log("I am a log");
        logger.debug("I am a debug log");
      });
    }
  }],
  stats: {
    logging: "verbose",
    loggingDebug: true
  },
};
输出
asset main.js 264 bytes [emitted] (name: main)
runtime modules 124 bytes 2 modules
./index.js 15 bytes [built] [code generated]

DEBUG LOG from TEST
<e> I am an error
<w> I am a warning
<i> I am an information
    I am a log
    I am a debug log

当使用 compiler.getInfrastructureLogger 时,可通过 infrastructureLogging.levelinfrastructureLogging.debug 来控制日志的展示等级:

rspack.config.js
module.exports = {
  plugins: [{
    apply(compiler) {
      compiler.hooks.thisCompilation.tap("test plugin", compilation => {
        const logger = compiler.getInfrastructureLogger("TEST");
        logger.error("I am an error");
        logger.warn("I am a warning");
        logger.info("I am an information");
        logger.log("I am a log");
        logger.debug("I am a debug log");
      });
    }
  }],
  infrastructureLogging: {
    level:  "verbose",
    debug: true
  },
};
输出
<e> [TEST] I am an error
<w> [TEST] I am a warning
<i> [TEST] I am an information
    [TEST] I am a log
    [TEST] I am a debug log
Rspack compiled successfully in 49 ms

assert

当断言失败时展示错误信息。

  • 等级: error
  • 类型:: assert(assertion: any, ...args: any[]): void;
rspack.config.js
logger.assert(false, "I am an assert error");
logger.assert(true, "Never displayed");
输出
LOG from TEST
<e> I am an assert error

status

展示处理状态信息,默认会使用 console.status,如果不存在则降级到 console.info

  • 等级: info
  • 类型: status(...args: any[]): void
rspack.config.js
logger.status("status info");
Output
[TEST] status info

trace

展示当前堆栈,仅当使用 Compilation Logger 且开启 stats.loggingTrace 可用。

  • 等级: debug
  • 类型: trace(): void
rspack.config.js
logger.trace();
Output
DEBUG LOG from TEST
    Trace
|     at Object.fn
|     at SyncHook.callAsyncStageRange

clear

清除所有之前已打印的所有日志,等同于 console.clear()

  • 等级: log
  • 类型: clear(): void;
rspack.config.js
logger.debug("not displayed");
logger.clear();
logger.debug("will displayed");
Output
[TEST] will displayed

分组 API

包括如下方法:

  • group(...args: any[]): void: 创建一个日志分组,分组信息以 logger.log 展示。
  • groupEnd(...args: any[]): void: 结束一个日志分组。
  • groupCollapsed(...args: any[]): void: 将日志进行分组。默认显示为折叠 logger.log 日志,当日志记录级别设置为 'verbose' 或 'debug' 时,显示展开的日志。
rspack.config.js
logger.group("Group");
logger.info("Info");
logger.log("Log");
logger.debug("Debug");
logger.groupCollapsed("Collapsed group");
logger.log("Log inside collapsed group");
logger.group("Inner group");
logger.log("Inner inner message");
logger.groupEnd();
logger.groupEnd();
logger.log("Log");
logger.groupEnd();
logger.log("End");
Output
<-> [TEST] Group
  <i> [TEST] Info
      [TEST] Log
      [TEST] Debug
  <-> [TEST] Collapsed group
        [TEST] Log inside collapsed group
    <-> [TEST] Inner group
          [TEST] Inner inner message
      [TEST] Log
    [TEST] End

时间 API

包括如下方法:

  • time(label: any): void: 启动一个计时器。
  • timeLog(label: any): void: 记录时间差但不关闭计时器。
  • timeEnd(label: any): void: 记录时间差并关闭计时器。
  • timeAggregate(label: any): void: 叠加记录时间差。
  • timeAggregateEnd(label: any): void: 结束叠加记录时间差,并获得时间差总和。
rspack.config.js
const wait = time => new Promise(resolve => setTimeout(resolve, time))
logger.time("normal");
await wait(100);
logger.timeLog("normal");
await wait(100);
logger.timeEnd("normal");

for (let i = 10;i--;) {
logger.time("aggregate")
await wait(i \* 10);
logger.timeAggregate("aggregate")
}
logger.timeAggregateEnd("aggregate")
Output
<t> [TEST] normal: 101.091167 ms
<t> [TEST] normal: 202.565 ms
<t> [TEST] aggregate: 460.416124 ms

Profile API

包括如下方法:

  • profile(label: any): void: 开启 Profile 捕获,如果支持会直接代理到 console.profile
  • profileEnd(label: any): void: 结束 Profile 捕获,如果支持会直接代理到 console.profileEnd

子 Logger

可以通过 logger.getChildLogger() 来创建子 Logger,其方法完全一致。

rspack.config.js
const logger = compiler.getInfrastructureLogger("TEST");
logger.info("logger info");
const childLogger = logger.getChildLogger("CHILD");
childLogger.info("child logger info");
Output
<i> [TEST] logger info
<i> [TEST/CHILD] child logger info