A logger for CLIs. Noop.
Contents
Features
- Highly Configurable
- Chalk-included
- Interactive Mode
- Timers
- Spinners
- Progress Bars
Install
$ npm i @darkobits/log
Basic Usage
This package's default export is a factory function that accepts an options object.
If the level
option is omitted, the log level will be set to process.env.LOG_LEVEL
if set. Otherwise, it will be set to info
.
Example:
import LogFactory from '@darkobits/log';
const log = LogFactory({heading: 'myApp'});
log.info(`Now you're thinking with ${log.chalk.bold('Portals')}!`);
API
This section documents the properties and methods of each logger instance. The examples below assume a logger (referred to as log
) has already been created.
.chalk
To afford a convenient way to style log messages, the logger creates a custom Chalk instance for each logger, the options for which are configurable. By creating a custom Chalk instance, the logger avoids conflicting with the default/global Chalk instance that may be in use by other parts of an application.
Example:
log.info(`Have a ${log.chalk.bold.pink('fabulous')} day!`);
configure(config: Partial<LogOptions>): void
Configure/re-configure the logger instance. The provided object will be deep-merged with the logger's existing configuration. This method can therefore be used to accomplish things like:
- Adding log levels.
- Changing the styling for existing log levels and other tokens.
- Changing the log level.
Example:
// Change the log level.
log.configure({level: 'verbose'});
// Add a new log level.
log.configure({
levels: {
foo: {
level: 5000,
label: 'FOO!',
style: (token, chalk) => chalk.keyword('blue')(token)
}
}
});
getLevel(): LevelDescriptor
Returns a LevelDescriptor
object for the current log level.
Example:
Assuming the current level is info
:
const curLevel = log.getLevel();
curLevel.label //=> 'info'
curLevel.level //=> 6000
curLevel.style //=> StyleFunction
getLevels(): {[key: string]: LevelDescriptor}
Returns an object mapping log level names (ex: info
) to LevelDescriptor
objects for each configured level. Note: The logger does not implement a method for the silent
level.
isLevelAtLeast(name: string): boolean
Returns true
if a message written at the provided log level would be written to the output stream based on the current log level.
Example:
log.configure({level: 'info'});
log.isLevelAtLeast('error') //=> true
log.isLevelAtLeast('silly') //=> false
prefix(prefix: Primitive): Prefix
Applies a prefix, styled according to the logger's style.prefix
options, to each line written to the output stream for the current call.
Example:
log.info(log.prefix('someFunction'), 'Hello\nworld.');
addSecret(secret: Primitive | RegExp, maskChar = '*'): void
This method may be used to ensure passwords and other sensitive information are not inadvertently written to the output stream. It accepts either a string literal or a regular expression and an optional mask character. By default, secrets are masked using *
.
Example:
const user = {
name: 'Frodo',
password: 'shire'
};
log.addSecret(user.password);
log.info('User data:', user);
createPipe(level: string): NodeJS.WritableStream
Creates a writable stream that will output any data written to it as log messages at the indicated level. Useful for displaying the output of a child process, for example.
In the following example, all output written to process.stderr
by the child process will be written as log messages at the verbose
level.
Example:
import execa from 'execa';
log.info('Starting child process...');
const childProcess = execa('echo', ['"I am a banana!"'], {stdout: 'pipe'});
childProcess.stdout.pipe(log.createPipe('verbose'));
await childProcess;
log.info('Done.');
beginInteractive(MessageFn | BeginInteractiveOptions): EndInteractiveFn
Begins a new interactive session and returns a function that may be invoked to end the interactive session. This method accepts either an options object or a function that will be invoked to render each update during the interactive session. If a custom interval is not being used, the shorthand (message function only) form is recommended.
The function returned by beginInteractive
accepts a function that will be invoked to produce the final content written to the interactive line(s).
Example:
const spinner = log.createSpinner();
const time = log.createTimer();
const endInteractive = log.beginInteractive(() => log.info(`${spinner} Please stand by...`));
// Some time later...
endInteractive(() => log.info(`Done in ${time}.`));
screenshot(s) here
createTimer(options?: TimerOptions): Timer
Creates a timer (chronograph) that starts immediately. The timer object may be placed directly into an interpolated string literal and will render its current value. Formatting is facilitated using pretty-ms
, and this method's options are identical to those of pretty-ms
.
Additionally, the timer object has a #reset()
method that may be invoked to reset the timer to zero.
Example:
const timer = log.createTimer();
// Some time later...
log.info(`Done in ${timer}.`);
screenshot(s) here.
createProgressBar(options: ProgressBarOptions): ProgressBar
Creates a progress bar. The progress bar object may be placed directly into an interpolated string literal and will render its current value. The only required option for this method is getProgress
, a function that will be invoked every time the progress bar is rendered, and should return a number between 0
and 1
indicating how full the bar should be.
Example:
import axios from 'axios';
let completed = 0;
const progressBar = log.createProgressBar({
// `getProgress` can simply return the value of `completed`.
getProgress: () => completed
});
// Begin an interactive session that will continuously render our progress bar
// while the download is outstanding.
const endInteractive = log.beginInteractive(() => log.info(`Downloading file: ${progressBar}`));
// Download a file with Axios.
const download = await axios({
url: 'https://my.domain.com/some-file.zip',
onDownloadProgress: (progressEvent) => {
// On each progress event, update our `completed` variable.
completed = progressEvent.loaded / progressEvent.total;
}
});
// Finally, end our interactive session.
endInteractive(() => log.info('Download complete.'));
createSpinner(options?: SpinnerOptions): Spinner
Creates a spinner. The spinner object may be placed directly into an interpolated string literal and will render its current value. The only option this method accepts is name
, which should be a valid cli-spinners
spinner name. If no options are provided, the dots
spinner will be used.
Example:
const spinner = log.createSpinner();
const endInteractive = log.beginInteractive(() => log.info(`${spinner} Reticulating splines...`));
// Once all splines have been reticulated...
endInteractive(() => log.info(`Done.`));
Debug Support
...
Caveats
...