Serverless Framework 插件开发指南:掌握 CLI 输出、日志、进度与错误处理
发布时间:2026/9/9 15:29:46来源:尧图网络
Serverless Framework 插件开发指南掌握 CLI 输出、日志、进度与错误处理【免费下载链接】serverless⚡ Serverless Framework – Effortlessly build apps that auto-scale, incur zero costs when idle, and require minimal maintenance using AWS Lambda and other managed cloud services.项目地址: https://gitcode.com/GitHub_Trending/se/serverless本文档面向通过插件扩展 Serverless Framework 的开发者系统讲解插件如何接入框架的 CLI 输出体系从分级日志、stdout/stderr分流、颜色与格式规范到错误抛出、交互式进度、Service information 区块与弃用deprecation提醒。读完本文你将能基于框架内置的 I/O API 编写出输出风格与官方命令一致、可被脚本可靠解析、并深度融入框架日志/弃用/服务信息生态的自定义插件。本文是 CLI output in plugins 一文的仓库本地化深度解析版。文中的所有源码依据均来自本仓库路径以仓库根目录为准。插件的 I/O API 从何而来在 Serverless Framework v3 中插件不再通过全局console或serverless.cli.log()直接向终端写内容而是由框架在实例化插件时把一份“I/O API”作为第三个构造参数注入进来class MyPlugin { constructor(serverless, cliOptions, { writeText, log, progress }) { // ... } }这份注入过程由插件管理器实现。在 plugin-manager.js 中可以看到框架通过getPluginWriters(...)拿到{ log, progress, writeText, style }之类的工具对象后再new Plugin(this.serverless, this.cliOptions, pluginUtils)把三者一并传给插件构造函数。因此插件的构造函数签名严格保持为(serverless, cliOptions, { log, progress, writeText })三者缺一不可具体接口的解析细节见下文“检索 I/O API”一节。分级日志默认保持克制verbose/debug 留给细节文档明确指出Serverless Framework v2 时代通过serverless.cli.log()写输出的做法已废弃v3 不应再使用。替代方案是构造参数中注入的log对象所提供的标准分级接口class MyPlugin { constructor(serverless, cliOptions, { log }) { log.error(Error) log.warning(Warning) log.notice(Message) log.info(Verbose message) // --verbose 时显示 log.debug(Debug message) // --debug 时显示 } }这些等级并非随意设计。查看日志渲染器源码 logger/index.js 可以印证等级体系的底层语义renderer.levels { compose: 0, // compose 专用最低级用于对 CLI 的完全控制 error: 1, // 错误消息 warning: 2, // 警告消息 notice: 3, // 默认日志级别对应默认 CLI 体验中简短清晰的普通消息 info: 4, // 执行动作时有用的补充信息 debug: 5, // 系统级调试信息 }notice是默认显示级别。当用户带上--verbose/--debug时框架会提高全局可写级别从而把info/debug消息也渲染出来实际门控逻辑见 writeStdErr级别大于全局级别时直接静默返回。--verbose与--debug的语义区别正是通过这组编号体现的info属于“执行动作时值得了解”的信息debug则留给需要命名空间管理的系统级调试输出。别名让意图更直白log对象提供了一些别名使语义更明确log(Here is a message) // 等价于 log.notice(Here is a message) log.verbose(Here is a verbose message) // 配合 --verbose 显示 // 等价于 log.info(Here is a verbose message) // 配合 --verbose 显示注意log.verbose只是log.info的别名在源码中同为info等级没有独立的“verbose 级别”它只是为了让调用点读起来更明确。这些别名在 Logger 类中有完整定义logNotice、logWarning、logInfo、logDebug等主要服务于向后兼容。成功消息带勾号的 log.success()如果需要渲染一条带“对勾”的格式化成功消息使用专门的辅助方法log.success(The task executed with success)成功消息会像内置的 “Service deployed” 成功提示一样带一个✔勾号渲染出来。从 writeStdErr 的实现可以看到type success时渲染器会在消息前缀处追加renderer.colors.red(✔)error追加✖warning追加红色[!]。这些符号统一定义在 renderer.style 中。printf 风格格式化log的各个方法都支持 Node.js 的 printf 风格占位符log.warning(Here is a %s log, formatted)源码中 writeStdErr 对首个 token 用/ %[sd] /检测占位符并做替换如果传入的是对象则会用util.inspect(token, { colors: true, depth: null })做带颜色的格式化输出这对手工打印配置对象或请求结果尤其方便。最佳实践小结保持默认 CLI 输出尽可能精简绝大多数信息应写入--verbose级别的info。warning应极端克制地使用。写前先自问插件是否应当抛异常、改为--verbose日志或触发一次弃用提醒见下文。慎用log.error()。优先考虑直接throw见下文“Errors”一节因为框架会自动捕获异常并带上调用链详情格式化展示。调试日志进--debug级别。debug日志支持按命名空间管理log.get(my-namespace).debug(Debug message)渲染时 debug 行会带上namespace:前缀见 Logger.debug并且可用--debugplugin-name:my-namespace过滤 CLI 输出。命名空间的实际拼接逻辑在 Logger.get子命名空间会以父:子形式挂在全局注册表中例如默认根命名空间s之下。默认写向 stderr 的深层原因默认情况下所有log输出都写入stderr这在终端里人类看不出差异——这是有意的设计插件可以放心地向任意命令追加额外消息即便该命令本意是被管道pipe或其它程序解析的。原因在于stdout被保留给了命令的“主输出”而杂讯全在stderr互不干扰。将命令主输出写到 stdoutwriteText()当插件需要把“命令本身的输出”写到stdout供管道/其它程序消费时使用注入的writeTextclass MyPlugin { constructor(serverless, cliOptions, { writeText }) { writeText(Command output) writeText([Here is a, multi-line output]) } }从源码看writeText 直接写process.stdout与log的process.stderr严格分离多行输出通过数组传入joinTextTokens 会深展平数组并用\n连接自动补齐末尾换行。最佳实践stdout输出通常用于被管道解析/消费。插件只应在自己定义的命令里写stdout以免破坏其它命令的既有输出。stdout中只应包含该命令的主输出。以内置serverless invoke为例其主输出就是 Lambda 调用的结果——只把该结果且仅该结果写入stdout任何脚本都能稳定解析而配置警告、升级提示、Lambda 日志等对人有用但会破坏可解析性的消息则一律写stderr。拿不准就写stderr。人类用户看不出差别但未来若要增加可解析输出门始终是敞开的。颜色与格式极简主义是规范文档推荐用chalk进行着色与格式化log.notice(chalk.gray(Here is a message))但更进一步的约束是——插件不应自由发挥颜色体系。内置chalk的直接依赖方其实是框架自身的渲染层仓库中 renderer.colors 定义了 white/red/yellow/green/gray/grey 等语义化颜色其中 “Serverless red”#fd5750正是通过chalk.rgb(253, 87, 80)实现的在 256 色能力不足的终端上降级为chalk.redBright。最佳实践主信息用白色次级信息用灰色主信息指命令的直接结果如deploy的部署结果、invoke的调用结果其它一切均属次级信息。插件一般不应使用其它颜色或引入自定义格式输出格式化以极简为美。优先使用本页文档化的内置格式log.success()成功消息、交互式进度等。“Serverless 红”仅用于抓取用户注意力应每命令至多一次、只给最关键的信息。Errors用户错误与程序错误的两种命运框架区分两类错误用户错误错误输入、非法配置等——用户修正输入即可解决。程序错误bug插件自身实现缺陷。要抛出一个用户错误并获得恰当的格式化使用框架的错误类throw new serverless.classes.Error(Invalid configuration in X)该错误类的仓库实现位于 serverless-error.js即ServerlessError由 serverless.js 挂载到this.classes上供插件访问。框架对其它默认视为“程序错误”的异常同样会格式化展示通常附带调用栈细节因此插件无需特判。最佳实践若错误应终止命令执行使用throw。若错误不应终止命令执行应当极其罕见用log.error()记录。例如serverless-offline中任何执行错误都不应中断本地服务器。交互式进度超过 2 秒再显示对耗时任务插件可以创建一个交互式进度class MyPlugin { constructor(serverless, cliOptions, { progress }) { const myProgress progress.create({ message: Doing extra work in my-plugin, }) // ... myProgress.update(Almost finished) // ... myProgress.remove() } }若确有并行处理如并行编译多个文件也可按需创建多个进度项。仓库的 Progress 类 负责维护所有进度状态底层是共享的单例 ora spinner多任务时 spinner 会显示类似 “{当前任务} (and N more tasks)” 的文本见 writeProgress。值得注意的是渲染器会自动判断是否交互环境computeIsInteractive要求 stdin/stdout 为 TTY 且无 CI 环境变量非交互如管道、CI时进度退化为按消息逐行输出避免清屏转义序列污染日志。最佳实践为通常超过 2 秒的任务创建进度低于该阈值应静默执行仅写--verbose日志。进度文案要让用户一眼知道是哪个插件在工作差Compiling差[Webpack] Compiling避免前缀式写法好Compiling with webpack同时显示多个进度应当是例外情况且不超过 34 个。宁可输出精简不可过噪。具名进度无需传递实例即可更新进度可以配唯一名称之后在任意代码处用progress.get(name)取回操作无需来回传递实例// 不带名称的进度 const myProgress progress.create({ message: Doing extra work in my-plugin, }) // 带唯一名称的进度 progress.create({ message: Doing extra work in my-plugin, name: my-plugin-progress, // 尽量保证跨插件唯一 }) // 别处... progress.get(my-plugin-progress).update(Almost finished) // 别处... progress.get(my-plugin-progress).remove()从源码看每个具名进度在 Progress.get 中会被加上s:前缀存入renderer.state.progressTasksMap因此不同插件只要使用全局唯一的name互不冲突。Service information把插件信息并入 deploy/info 输出serverless deploy或serverless info末尾展示的“服务信息”区块可以由插件扩展。添加单行项serverless.addServiceOutputSection(my section, content)展示效果$ serverless info functions: ... my section: content添加多行区块serverless.addServiceOutputSection(my section, [line 1, line 2])展示效果$ serverless info functions: ... my section: line 1 line 2底层机制Map 集合与去重校验addServiceOutputSection 是Serverless类的公开方法实现要点区块名与内容会做类型强校验sectionName必须为非空字符串内容必须是字符串或非空字符串数组空字符串内容会抛TypeError。内容存放在this.servicePluginOutputsMap与框架自身填充的this.serviceOutputsMap分离两块各自独立维护初始化见 serverless.js。区块名不允许重复若同名区块已存在于任一个 Map 中会抛TypeError(Section content for ... was already set)避免多个插件互相覆盖输出。内置的deploy/info命令正是把这两张 Map 合并渲染的deploy在部署完成后调用 write-service-outputs.js 依次写出每个[section, entries]info命令的渲染逻辑见 display.js——框架自己会填充functions、endpoints、layers、stage、region、api keys、Stack Outputs等标准区块而插件新增的区块正是追加在它们之后。对输出为纯字符串内容的数组/单行渲染时会以合理的缩进呈现单行key: value多行逐行缩进。Deprecations接入框架级弃用系统插件想向用户提示某个特性已弃用时使用logDeprecation()serverless.logDeprecation( DEPRECATION_CODE, Feature X of my-plugin is deprecated. Please use Y instead., )这些弃用提醒会接入 Serverless Framework 的弃用系统统一处理。底层机制serverless.logDeprecation在 serverless.js 中实现它会把插件代码自动加上EXT_前缀再委托给内部_logDeprecation并附带当前 service 配置实际逻辑在 log-deprecation.js。该模块的行为对插件作者理解弃用消息的“生命周期”很有价值去重同一code在一次运行中只会触发一次triggeredDeprecations。可关闭可通过环境变量SLS_DEPRECATION_DISABLE或在serverless.yml中配置disabledDeprecations列表禁用含通配*。通知模式可通过环境变量SLS_DEPRECATION_NOTIFICATION_MODE或配置项deprecationNotificationMode设为error把弃用升级为抛错、warn或默认的缓冲摘要模式。缓冲摘要默认模式下消息先缓冲在命令末尾统一打印摘要N deprecations triggered in the last command并写入 health-status 文件对应serverless doctor。插件消息无需逐条刷屏用户体验与框架内置弃用提醒完全一致。内置弃用消息会附带 “More Info” 链接指向官方 deprecations 文档带EXT_前缀的外部插件消息不会追加该链接因为官方文档没有对应页面。最佳实践弃用 code 要以插件名做前缀例如OFFLINE_XXX避免与其它插件及框架内置 code如SLS_*冲突。消息要对用户可操作特性弃用了应该明确指出用什么替代必要时附带链接。检索 I/O API不依赖构造注入前文所有示例都是在插件构造函数中通过解构第三参数获取 I/O API。若你想在任意 JS 文件中使用同一套接口可以直接 requireserverless/utils包const { writeText, log, progress } require(serverless/utils/log)在本仓库中serverless/utils即 packages/util 包其导出的log是带默认根命名空间s的全局 Logger 单例writeText是写入stdout的函数progress则是提供get(namespace)与cleanup()的进度工厂。无论从哪个模块获取它们最终都汇聚到同一渲染器状态因此写出的日志/进度会与框架其余输出保持一致的格式与换行行为写日志前渲染器会先停掉 spinner避免与转圈动画冲突见 writeStdErr。在默认插件骨架中落地要把上述 API 串起来一个符合框架规范的插件骨架大致如下插件的注册与生命周期编排见 creating-plugins.mdclass MyPlugin { constructor(serverless, cliOptions, { log, progress, writeText }) { this.serverless serverless this.hooks { before:deploy:deploy: async () { // 耗时操作先建进度 const p progress.create({ message: Running MyPlugin extra step }) try { log.info(Preparing extra artifacts…) // 仅 --verbose 可见 await doSlowWork() p.update(Almost finished) } finally { p.remove() } log.success(MyPlugin finished) }, after:deploy:deploy: () { // 并入 Service information this.serverless.addServiceOutputSection(my plugin, [line 1, line 2]) }, } } } module.exports MyPlugin关键规则回顾默认输出写stderrlog命令主输出才用writeText写stdout能用throw就不log.error2 秒的任务再显示进度区块名全局唯一弃用 code 带插件名前缀。遵循这套约定你的插件在视觉风格、脚本可解析性与错误处理体验上都将与框架官方命令保持一致。【免费下载链接】serverless⚡ Serverless Framework – Effortlessly build apps that auto-scale, incur zero costs when idle, and require minimal maintenance using AWS Lambda and other managed cloud services.项目地址: https://gitcode.com/GitHub_Trending/se/serverless创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网