十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Rolldown 插件 onLog 钩子完全指南:日志拦截、级别改写与防循环机制

Rolldown 插件 onLog 钩子完全指南:日志拦截、级别改写与防循环机制 Rolldown 插件 onLog 钩子完全指南日志拦截、级别改写与防循环机制【免费下载链接】rolldownFast Rust bundler for JavaScript/TypeScript with Rollup-compatible API.项目地址: https://gitcode.com/GitHub_Trending/ro/rolldownRolldown 的onLog插件钩子用于在日志输出到控制台之前拦截所有构建日志允许开发者过滤、改写、升级或降级日志甚至把警告直接转化为抛出的错误。本文以官方插件文档为核心结合 logger.ts、logging.ts 等源码实现深入讲解onLog的调用语义、防无限循环机制、上下文方法与InputOptions.onLog配置读完即可在插件中精准控制日志输出。onLog 钩子是什么onLog是 Rolldown 插件中负责日志后处理的钩子。当构建过程中的任何环节产生日志info、debug、warn 三类见 logging.ts 中LogLevel info | debug | warnRolldown 都会先把日志依次交给所有注册了onLog的插件处理最后才决定是否打印到控制台或抛错。其核心语义定义在 plugin-hooks-onlog.md不自动装饰日志与其他会往日志里添加插件名的钩子不同onLog不会添加或改变日志的任何属性插件拿到的就是最原始的日志对象防循环隔离由某个onLog钩子自身产生重新发出的日志不会回传给同一个插件的onLog钩子如果另一个插件在自己的onLog钩子中响应此类日志又产生了新日志这条新日志同样不会传给最初产生日志的那个插件的onLog钩子。这两条隔离规则直接对应 logger.ts 中的skipped: ReadonlySetPlugin实现每当某个插件在钩子内通过上下文方法重新发出日志时getLogHandler都会构造new Set(skipped).add(plugin)把当前插件加入跳过集合从而保证日志不会再进入该插件的onLog从根上杜绝了无限循环。钩子签名与日志对象结构onLog的钩子函数签名如下onLog(level: LogLevel, log: RolldownLog): boolean | void其中level取值为info | debug | warnlog是RolldownLog日志对象。该对象在 logging.ts 中定义常用字段包括字段类型说明messagestring日志正文唯一必填字段codestring日志代码如PLUGIN_ERRORpluginCodeunknown插件自定义日志代码插件发出的日志会保留原始code于此处pluginstring产生日志的插件名metaany自由附加的元数据可在onLog中读写loc{ file?, line, column }日志对应的源码位置framestring源码上下文片段id/ids/namesstring/string[]关联的模块 id 与导出名urlstring文档链接stackstring堆栈信息关于pluginCode的生成规则可参考 log-handler.ts插件通过上下文方法this.info/this.warn等发日志时若日志已有code而无pluginCode则code会被改存为pluginCode随后code被统一赋值为PLUGIN_WARNINGplugin字段被赋值为当前插件名——这正是 plugin-context-warn.md 所描述的插件警告总是带有PLUGIN_WARNING代码的行为。官方示例逐行解读日志降级、改写与防循环原文档给出了两个插件协同工作的完整示例下面逐段展开。第一个插件负责产生日志并把自己的特殊日志降级回 infofunction plugin1() { return { name: plugin1, buildStart() { this.info({ message: Hey, pluginCode: SPECIAL_CODE }); }, onLog(level, log) { if (log.plugin plugin1 log.pluginCode SPECIAL_CODE) { // 根据日志代码把日志重新变成 warning。这条 warning 不会 // 再传回 plugin1 自己的 onLog避免无限循环 // 但其他插件仍然会收到它。 this.warn(log); return false; } }, }; }第二个插件负责处理其他插件产生的日志可以就地改写并决定最终级别function plugin2() { return { name: plugin2, onLog(level, log) { if (log.plugin plugin1 log.pluginCode SPECIAL_CODE) { // 可以在这个钩子里修改日志对象 log.meta processed by plugin 2; // 把日志重新打回 info 级别。如果这是对 plugin1 // 那次的响应它不会再传回任何一方以免无限循环。 // 若两个插件同时启用且 plugin2 排在 plugin1 之后 // 最终这条日志就是一条 info 日志。 this.info(log); return false; } }, }; }这段示例揭示了onLog的完整工作流可总结为三步产生this.info({ message, pluginCode })发出日志pluginCode用于携带插件自定义的日志代码与内置日志代码区分开改写onLog中直接修改log对象属性如log.meta修改会沿插件链继续传递决定去向调用this.warn(log)/this.info(log)会以新级别重新入队分发返回false则终止后续处理——原日志不再传给后续插件也不进入默认打印/抛错流程。return false的终止语义在 logger.ts 中有直接体现handler.call(...) false时立即return跳过剩余的插件与最终的onLog(level, log)输出环节。插件执行顺序对结果的影响示例注释特别强调如果两个插件都启用且 plugin2 排在 plugin1 之后日志最终会是 info 级别。这是因为onLog像options、outputOptions钩子一样由 plugin-driver.ts 中的getSortedPlugins(onLog, plugins)按order: pre | post排序后依次调用同一级别的日志先经过前面的插件再到达后面的插件。因此插件注册顺序直接决定日志改写链的先后排在后面的插件拿到的是前面插件修改后的log对象。上下文方法info / warn / debug / error在onLog钩子内this暴露四个与日志相关的上下文方法它们的行为差异如下方法行为受 logLevel 影响this.info(log)以info级别重新发出日志logLevel为warn或silent时不生效见 plugin-context-info.mdthis.warn(log)以warn级别重新发出日志silent时不生效见 plugin-context-warn.mdthis.debug(log)仅当logLevel显式设为debug时才处理否则静默跳过见 plugin-context-debug.md默认被吞掉this.error(log)立即抛出错误且保留日志的全部附加属性不受logLevel限制这四个方法均接受字符串、日志对象或惰性函数三种参数形式见 log-handler.ts。其中函数形式() RolldownLog | string会在日志真正被处理时才执行非常适合承载昂贵的计算例如 plugin-context-debug.md 中按需统计模块行数的示例this.debug( () transforming ${id},\n module contains, ${code.split(\n).length} lines, );把警告变成错误在onLog内使用this.error(log)是把警告升级为错误的最简洁方式这正是 plugin-context-error.md 给出的场景function myPlugin() { return { name: my-plugin, onLog(level, log) { if (level warn log.code THIS_IS_NOT_OK) { return this.error(log); } }, }; }在 logger.ts 的实现中this.error对应error(normalizeLog(log))即把日志对象整体转换为抛出的RolldownError因此log.code、log.loc、log.meta等属性都会被带到错误上。logLevel 如何过滤 onLoglogLevel选项默认info见 input-options.ts决定了日志是否会被分发到onLog钩子。在 logging.ts 中四个级别被映射为优先级数字const logLevelPriority { debug: 0, info: 1, warn: 2, silent: 3, };logger.ts 在入口处先比较优先级if (logPriority minimalPriority) return;——低于配置级别的日志根本不会进入任何插件的onLog也不会打印到控制台。因此默认配置下warn、info日志会被正常分发与输出debug日志被完全吞掉既不传给插件onLog也不传给InputOptions.onLog更不会打印见 log-level.md设置为silent时所有普通日志都被吞掉只剩错误可抛出。InputOptions.onLog用户级日志拦截除了插件钩子Rolldown 还在输入选项层面提供了onLog配置input-options.ts它在插件链全部处理完毕后接管日志签名多一个默认处理器参数onLog(level, log, defaultHandler): void不调用defaultHandler日志不会打印到控制台见 on-log.md以不同级别调用defaultHandler可以改写日志的输出级别其中额外支持的error级别会把日志变成携带全部日志属性的抛出错误与onwarn的关系onwarn已是废弃 API仅当onLog以warn级别调用默认处理器时才会被触发见 on-warn.md。典型的忽略指定警告 其余警告转错误配置可直接参考 input-options.ts 中的内联示例export default defineConfig({ onLog(level, log, defaultHandler) { if (log.code CIRCULAR_DEPENDENCY) { return; // 忽略循环依赖警告 } if (level warn) { defaultHandler(error, log); // 其他警告升级为错误 } else { defaultHandler(level, log); // 其余日志照常打印 } }, });仓库自带的测试用例也验证了这两条路径default-handler-error/_config.ts 断言defaultHandler(error, log)会让构建抛出Error同时校验日志 message 不带额外的Warning:前缀throw/_config.ts 验证在onLog中直接throw new Error(convert log to error)同样能让构建失败且onLog被恰好调用一次。用 getLogFilter 复用 CLI 过滤语法当需要按代码批量过滤时可以配合getLogFilter使用与 CLI 一致的过滤语法见 get-log-filter.ts支持code:FOO精确匹配、!前缀取反、连接多个子条件、*通配符并且可匹配log对象的嵌套路径。import { defineConfig } from rolldown; import { getLogFilter } from rolldown/getLogFilter; const logFilter getLogFilter([code:FOO, code:BAR]); export default defineConfig({ input: main.js, onLog(level, log, handler) { if (logFilter(log)) { handler(level, log); } }, });小结onLog钩子是 Rolldown 日志管线的最后一公里掌握以下几点即可熟练运用它是日志的观察与改写点不自动装饰日志一切属性操作由插件自行完成返回false吞掉日志this.warn/info/debug/error重新分发日志this.error保留属性直接抛错防循环由skipped集合保证logger.ts同一插件重新发出的日志不会回传给自己跨插件响应产生的新日志也不会回溯到源头插件插件顺序决定改写链order: pre | post与注册顺序共同影响最终日志内容与级别logLevel是第一道闸门被级别过滤掉的日志不会进入任何onLog面向用户的自定义输出优先使用InputOptions.onLog配合getLogFilter替代已废弃的onwarn。需要更完整的信息可继续阅读官方文档 plugin-hooks-onlog.md 以及上下文方法文档 plugin-context-info.md、plugin-context-warn.md、plugin-context-debug.md 和 plugin-context-error.md。【免费下载链接】rolldownFast Rust bundler for JavaScript/TypeScript with Rollup-compatible API.项目地址: https://gitcode.com/GitHub_Trending/ro/rolldown创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表