@pryv/boiler
v1.2.4
Published
Logging and config boilerplate library for Node.js apps and services at Pryv
Readme
Pryv config and logging boilerplate for Node.js
Usage
Initialization
The "boiler" must be initialized with the application name and configuration files settings, before invoking getConfig() or getLogger():
require('@pryv/boiler').init({
appName: 'my-app', // This will will be prefixed to any log messages
baseFilesDir: path.resolve(__dirname, '..'), // use for file:// relative path if not give cwd() will be used
baseConfigDir: path.resolve(__dirname, '../config'),
extraConfigs: [{
scope: 'extra-config',
file: path.resolve(__dirname, '../config/extras.js')
}]
});Configuration
We use nconf.
Base config directory structure
During initialization default-config.yml and ${NODE_ENV}-config.yml will be loaded if present in baseConfigDir.
Loading order and precedence
The order configuration sources are loaded is important. Each source is loaded with a "scope" name, which can be used to replace the source's contents at any point of time.
The configuration sources are loaded in the following order, the first taking precedence:
- 'test': Empty slot reserved for tests to override any other config parameter
- 'argv': Command line arguments
- 'env': Environment variables
- 'base': File
${NODE_ENV}-config.yml(if present) or specified by the--configcommand line argument - 'extra-config': Additional source(s) as specified by the
extraConfigsoption (see below) - Defaults:
- 'default-file':
${baseConfigDir}/default-config.yml - 'defaults': Hard-coded defaults for logger
- 'default-file':
Extra configurations
Additional configuration sources can be set or reserved at initialization with the extraConfigs option, which expects an array of objects with one of the following structures:
- File:
{scope: <name>, file: <path to file> }. Accepts.yml,.jsonand.jsfiles. Note.jscontent is loaded withrequire(<path to file>). - Data:
{scope: <name>, key: <optional key>, data: <object> }. Ifkeyis provided, the content ofdatawill be accessible by this key, otherwise it is loaded at the root of the configuration. - Remote URL:
{scope: <name>, key: <optional key>, url: <URL to json content> }. The JSON contents of this URL will be loaded asynchronously. - URL from key:
{scope: <name>, key: <optional key>, urlFromKey: <key> }. Similar to remove URL, with the URL obtained from an existing configuration key.
Working with the configuration
Retrieving the configuration object:
// synchronous loading
const { getConfigUnsafe } = require('@pryv/boiler'); // Until all asynchronous sources such as URL are loaded, items might not be available
const config = await getConfigUnsafe();
// asynchronous loading
const { getConfig } = require('@pryv/boiler');
const config = await getConfig(); // Here we can be sure all items are fully loadedRetrieving settings:
// configuration content is {foo: { bar: 'hello'}};
const foo = config.get('foo'); // {bar: 'hello'}
const bar = config.get('foo:bar'); // 'hello'
const barExists = config.has('bar'); // trueAssigning settings:
// configuration content is {foo: { bar: 'hello'}};
config.set('foo', 'bye bye'); // {bar: 'hello'}
const foo = config.get('foo'); // 'bye bye'Changing a scope's contents:
// configuration content is {foo: { bar: 'hello'}};
config.get('foo'); // {bar: 'hello'}
// replace 'test' existing content (test is always present as the topmost configuration source)
config.replaceScopeConfig('test', {foo: 'test'});
config.get('foo'); // 'test'
// reset content of 'test' scope
config.replaceScopeConfig('test', {});
config.get('foo'); // {bar: 'hello'}
// Note: for 'test' scope there is a "sugar" function with config.injectTestConfig(object)Finding out from which scope a key applies: As nconf is hierachical sometimes you migth want to search from which scope the value of a key is issued.
config.getScopeAndValue('foo');
// returns {value: 'bar', scope: 'scopeName'; info: 'From <file> or Type <env, '}"Learn" mode
To help detect unused configuration settings, a "learn" mode can be activated to track all calls to config.get() in files.
Example when running tests:
export CONFIG_LEARN_DIR="{absolute path}/service-core/learn-config"
yarn testNote, if CONFIG_LEARN_DIR is not given {process.cwd()}/learn-config will be used
Logging
All messages are prefixed by the appName value provided at initialization (see above)). appName can be postfixed with a string by setting the environment variable PRYV_BOILER_SUFFIX, which is useful when spawning several concurrent processes of the same application.
Using the logger
const {getLogger} = require('@pryv/boiler');
logger.info('Message', item); // standard log
logger.warn('Message', item); // warning
logger.error('Message', item); // warning
logger.debug('Message', item); // debug
logger.getLogger('sub'); // new logger name spaced with parent, here '{appName}:sub'logger.info(), logger.warn() and logger.error() use Winston logger.debug() is based on debug.
Outputting debug messages
Set the DEBUG environment variable. For example: DEBUG="*" node app.js will output all debug messages.
As "debug" is a widely used package, you might get way more debug lines than expected, so you can use the appName property to only output messages from your application code: DEBUG="<appName>*" node app.js
Using a custom logger
A custom logger can be used by providing logs:custom information to the configuration. A working sample of custom Logger is provided in ./examples/customLogger.
The module must implement async init(settings) and log(level, key, message, meta)
Log configuration sample
logs: {
console: {
active: true,
level: 'info',
format: {
color: true,
time: true,
aligned: true
}
},
file: {
active: true,
path: 'application.log'
},
custom: {
active: true,
path 'path/to/node/package',
settings: { /* settings passed to the custom logger */}
}
}TODO
- Make config an eventEmitter ? // to track when read or if config changes
- FIX realtive PATH logic for config.loadFromFile()
Contributing
npm run lint lints the code with Semi-Standard.
npm run license updates license information.
