mirror of
https://github.com/serverless/serverless.git
synced 2026-01-18 14:58:43 +00:00
266 lines
8.9 KiB
Markdown
266 lines
8.9 KiB
Markdown
<!--
|
|
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
|
|
|
|
**Warning:** the API documented on this page is available in Serverless Framework v3, which is not released yet. This documentation is published to help plugins upgrade to v3.
|
|
|
|
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');
|
|
```
|