UNPKG

serverless

Version:

Serverless Framework - Build web, mobile and IoT applications with serverless architectures using AWS Lambda, Azure Functions, Google CloudFunctions & more

264 lines (189 loc) 8.92 kB
<!-- title: Serverless Framework - Plugins - CLI output menuText: CLI output menuOrder: 2 description: How to write to the CLI output in Serverless Framework plugins. layout: Doc --> <!-- DOCS-SITE-LINK:START automatically generated --> ### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/guides/plugins/cli-output) <!-- DOCS-SITE-LINK:END --> # CLI output in plugins Plugins can integrate and extend the CLI output of the Serverless Framework in different ways. ## Writing to the output In Serverless Framework v2, plugins could write to the CLI output via `serverless.cli.log()`: ```js // This approach is deprecated: serverless.cli.log('Message'); ``` The method above is deprecated. It should no longer be used in Serverless Framework v3. Instead, plugins can log messages to the CLI output via a standard `log` interface: ```js class MyPlugin { constructor(serverless, cliOptions, { log }) { log.error('Error'); log.warning('Warning'); log.notice('Message'); log.info('Verbose message'); // --verbose log log.debug('Debug message'); // --debug log } } ``` Some aliases exist to make log levels more explicit: ```js log('Here is a message'); // is an alias to: log.notice('Here is a message'); log.verbose('Here is a verbose message'); // displayed with --verbose // is an alias to: log.info('Here is a verbose message'); // displayed with --verbose ``` To write a formatted "success" message, use the following helper: ```js log.success('The task executed with success'); ``` Log methods also support the printf format: ```js log.warning('Here is a %s log', 'formatted'); ``` **Best practices:** - **Keep the default CLI output minimal.** - Log most information to the `--verbose` output. - Warnings should be used exceptionally. Consider whether the plugin should instead throw an exception, log a `--verbose` message or trigger a deprecation (see below). - Before using `log.error()`, consider [throwing an exception](#errors): exceptions are automatically caught by the Serverless Framework and formatted with details. - Debugging logs should be logged to the `--debug` level. Debug logs can be namespaced following the [`debug` convention](https://github.com/visionmedia/debug#usage) via `log.get('my-namespace').debug('Debug message')`. Such logs can then be filtered in the CLI output via `--debug=plugin-name:my-namespace`. **By default, logs are written to `stderr`**, which displays in terminals (humans cannot tell the difference). This is intentional: plugins can safely log extra messages to any command, even commands meant to be piped or parsed by another program. Read the next section to learn more. ### Writing command output to `stdout` By default, plugins should write messages to `stderr` via the `log` object. To write command output to `stdout` instead, use `writeText()`: ```js class MyPlugin { constructor(serverless, cliOptions, { writeText }) { writeText('Command output'); writeText(['Here is a', 'multi-line output']); } } ``` **Best practices:** - `stdout` output is usually meant to be piped to/parsed by another program. - Plugins should only write to `stdout` in commands they define (to avoid breaking the output of other commands). - The only content written to `stdout` should be the main output of the command. Take, for example, the `serverless invoke` command: - Its output is the result of the Lambda invocation: by writing that result (and only that) to `stdout`, it allows any script to parse the result of the Lambda invocation. - All other messages should be written to `stderr`: such logs are useful to humans, for example configuration warnings, upgrade notifications, Lambda logs… Since they are written to `stderr`, they do not break the parsable output of `stdout`. **If unsure, write to `stderr`** (with the `log` object) instead of `stdout`. Why: human users will not see any difference, but the door will stay open to write a parsable output later in the future. ### Colors and formatting To format and color text output, use the [chalk](https://github.com/chalk/chalk) package. For example: ```js log.notice(chalk.gray('Here is a message')); ``` **Best practices:** - Write primary information in **white**, secondary information in **gray**. - Primary information is the direct outcome of a command (e.g. deployment result of the `deploy` command, or result of the `invoke` command). Secondary information is everything else. - Plugins should generally not use any other color, nor introduce any other custom formatting. Output formatting is meant to be minimalistic. - Plugins should use built-in formats documented in this page: success messages (`log.success()`), interactive progress… The "Serverless red" color (`#fd5750`) is used to grab the user's attention: - It should be used minimally, and maximum once per command. - It should be used only to grab attention to the command's most important information. ## Errors The Serverless Framework differentiates between 2 errors: - user errors (wrong input, invalid configuration, etc.) - programmer errors (aka bugs) To throw a **user error** and have it properly formatted, use Serverless' error class: ```js throw new serverless.classes.Error('Invalid configuration in X'); ``` All other errors are considered programmer errors by default (and are properly formatted in the CLI output as well). **Best practices:** - If an error should stop the execution of the command, use `throw`. - If an error should _not_ stop the execution of the command (which should be exceptional), log it via `log.error()`. - For example any execution error in `serverless-offline` should not stop the local server. ## Interactive progress Plugins can create an interactive progress: ```js class MyPlugin { constructor(serverless, cliOptions, { progress }) { const myProgress = progress.create({ message: 'Doing extra work in my-plugin', }); // ... myProgress.update('Almost finished'); // ... myProgress.remove(); } } ``` In case of parallel processing (for example compiling multiple files in parallel), it is possible to create multiple progress items if that is useful to users. **Best practices:** - Create a progress for tasks that usually take **more than 2 seconds**. Below that threshold, plugins can operate silently and log to `--verbose` only. - Users should know which plugin is working from the progress message: - Bad: "Compiling" - Bad: "[Webpack] Compiling" (avoid prefixes) - Good: "Compiling with webpack" - Displaying multiple progresses should be exceptional, and limited to 3-4 progresses at a time. It is better to keep the output minimal than too noisy. Note that it is possible to give a unique name to a progress. That name can be used to retrieve the progress without having to pass the instance around: ```js // Progress without any name: const myProgress = progress.create({ message: 'Doing extra work in my-plugin', }); // Progress with a unique name progress.create({ message: 'Doing extra work in my-plugin', name: 'my-plugin-progress', // Try to make the name unique across all plugins }); // elsewhere... progress.get('my-plugin-progress').update('Almost finished'); // elsewhere... progress.get('my-plugin-progress').remove(); ``` ## Service information Plugins can add their own sections to the "Service information", i.e. the information displayed after `serverless deploy` or in `serverless info`. To add a single item: ```js serverless.addServiceOutputSection('my section', 'content'); ``` The example above will be displayed as: ``` $ serverless info functions: ... my section: content ``` To add a multi-line section: ```js serverless.addServiceOutputSection('my section', ['line 1', 'line 2']); ``` The example above will be displayed as: ``` $ serverless info functions: ... my section: line 1 line 2 ``` ## Deprecations Plugins can signal deprecated features to users via `logDeprecation()`: ```js serverless.logDeprecation( 'DEPRECATION_CODE', 'Feature X of my-plugin is deprecated. Please use Y instead.' ); ``` These deprecations will integrate with the deprecation system of the Serverless Framework. **Best practices:** - Prefix the deprecation code with the plugin name, for example: `OFFLINE_XXX`. - Make the message actionable for users: if a feature is deprecated, what should users use instead? Feel free to add links if necessary. ## Retrieving the I/O API As shown in the examples above, the I/O API is injected in the constructor of plugins: ```js class MyPlugin { constructor(serverless, cliOptions, { writeText, log, progress }) { // ... } } ``` However, it is also possible to retrieve it from any JavaScript file by requiring the `@serverless/utils` package: ```js const { writeText, log, progress } = require('@serverless/utils/log'); ```