UNPKG

lambda-log

Version:

Lightweight logging library for any Node 10+ applications

208 lines (183 loc) 8.97 kB
const EventEmitter = require('events'); const LogMessage = require('./LogMessage'); const symbols = { LEVELS: Symbol('levels') }; /** * @typedef {object} LambdaLogOptions - Configuration object for LambdaLog. * @property {object} [meta={}] Global metadata to be included in all logs. * @property {string[]|Function[]} [tags=[]] Global tags to be included in all logs. * @property {Function} [dynamicMeta=null] Function that runs for each log that returns additional metadata. See [Dynamic Metadata](#dynamic-metadata). * @property {boolean} [debug=false] Enables `log.debug()`. * @property {boolean} [dev=false] Enable development mode which pretty-prints JSON to the console. * @property {boolean} [silent=false] Disables logging to `console` but messages and events are still generated. * @property {Function} [replacer=null] Replacer function for `JSON.stringify()` to allow handling of sensitive data before logs are written. See [JSON.stringify](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#The_replacer_parameter). * @property {object} [logHandler=console] A console-like object containing all standard console functions. Allows logs to be written to any custom location. See [Log Handler](#loghandler). * @property {?string} [levelKey=_logLevel] Override the key name for the log level. Set to `null` to remove the key from the output. * @property {string} [messageKey=msg] Override the key name for the message. * @property {?string} [tagsKey=_tags] Override the key name for the tags. Set to `null` to remove the key from the output. */ /** * @augments EventEmitter */ class LambdaLog extends EventEmitter { /** * Constructor for the LambdaLog class. Provided to be utilized in more advanced cases to allow overriding and configuration. * By default, this module will export an instance of this class, but you may access the class and create your own instance * via `log.LambdaLog`. * @class * @param {LambdaLogOptions} [options={}] Options for configuring LambdaLog. * @param {object.<string, string|Function>} [levels={}] Allows adding and customizing log levels. DEPRECATED */ constructor(options = {}, levels = {}) { super(); /** * Access to the uninstantiated LambdaLog class. This allows more advanced functionality and customization. * @type {LambdaLog} */ this.LambdaLog = LambdaLog; /** * Access to the uninstantiated LogMessage class. You can override this property to use a custom logging class that * inherits the same methods. * @type {LogMessage} * @since 2.2.0 */ this.LogMessage = LogMessage; /** * @type {LambdaLogOptions} */ this.options = { meta: {}, tags: [], dynamicMeta: null, debug: false, dev: false, silent: ['true', 'yes', 'y', '1'].includes(process.env.LAMBDALOG_SILENT), replacer: null, logHandler: console, levelKey: '_logLevel', messageKey: 'msg', tagsKey: '_tags', ...options }; /** * Global configuration for log levels * @type {object} */ this[symbols.LEVELS] = { info: 'info', warn: 'warn', error: 'error', debug() { if(this.options.debug) return 'debug'; return false; }, ...levels }; /** * Console-like log handler to use for logging messages * @type {object} */ this.console = this.options.logHandler; const levelsConfig = this[symbols.LEVELS]; for(const lvl in levelsConfig) { if(Object.prototype.hasOwnProperty.call(levelsConfig, lvl)) { this.addLevel(lvl, levelsConfig[lvl]); } } } /** * Add a new log level to this instance of LambdaLog. * @since 3.0.0 * @deprecated * @param {string} name The name of the new log level. * @param {string|Function} handler The string name of the `console` method to call or a function that returns a string method name. * @returns {this} Instance of LambdaLog. */ addLevel(name, handler) { this[symbols.LEVELS][name] = handler; /** * Shortcut methods for `log.log()`. By default, the following methods are available: `log.info()`, `log.warn()`, `log.error()` and `log.debug()`. * Additional methods will be added for any [custom log levels](#custom-log-levels) provided.<br><br>The provided msg can be any type, although a string * or `Error` is recommended. If `Error` is provided the stack trace is added as metadata to the log as `stack`. * @param {*} msg Message to log. Can be any type, but string or `Error` reccommended. * @param {object} [meta={}] Optional meta data to attach to the log. * @param {string[]} [tags=[]] Additional tags to append to this log. * @returns {LogMessage} The LogMessage instance for the log. */ this[name] = (msg, meta = {}, tags = []) => this.log(name, msg, meta, tags); return this; } /** * Generates JSON log message based on the provided parameters and the global configuration. Once the JSON message is created, it is properly logged to the `console` * and emitted through an event. If an `Error` or `Error`-like object is provided for `msg`, it will parse out the message and include the stacktrace in the metadata. * @throws {Error} If improper log level is provided. * @param {string} level Log level (`info`, `debug`, `warn`, `error` or a [custom log level](#custom-log-levels)) * @param {*} msg Message to log. Can be any type, but string or `Error` reccommended. * @param {object} [meta={}] Optional meta data to attach to the log. * @param {string[]} [tags=[]] Additional tags to append to this log. * @returns {LogMessage|boolean} Returns instance of LogMessage or `false` if `level = "debug"` and `options.debug = false`. May also return `false` when a [custom log level](#custom-log-levels) handler function prevents the log from being logged. */ log(level, msg, meta = {}, tags = []) { if(!Object.prototype.hasOwnProperty.call(this[symbols.LEVELS], level)) { throw new Error(`"${level}" is not a valid log level`); } const message = new this.LogMessage({ level, msg, meta, tags }, this.options); let method = this[symbols.LEVELS][level]; if(typeof method === 'function') { method = method.call(this, message); } if(!method) return false; if(!this.options.silent) { this.console[method](message.toJSON(this.options.dev)); } /** * The log event is emitted (using EventEmitter) for every log generated. This allows for custom integrations, such as logging to a thrid-party service. * This event is emitted with the [LogMessage](#logmessage) instance for the log. You may control events using all the methods of EventEmitter. * @event LambdaLog#log * @type {LogMessage} */ this.emit('log', message); return message; } /** * Generates a log message if `test` is a falsy value. If `test` is truthy, the log message is skipped and returns `false`. Allows creating log messages without the need to * wrap them in an if statement. The log level will be `error`. * @since 1.4.0 * @param {*} test A value which is tested for a falsy value. * @param {*} msg Message to log if `test` is falsy. Can be any type, but string or `Error` reccommended. * @param {object} [meta={}] Optional meta data to attach to the log. * @param {string[]|Function[]} [tags=[]] Additional tags to append to this log. * @returns {LogMessage|boolean} The LogMessage instance for the log or `false` if test passed. */ assert(test, msg, meta = {}, tags = []) { if(test) return false; return this.log('error', msg, meta, tags); } /** * Generates a log message with the result or error provided by a promise. Useful for debugging and testing. * @since 2.3.0 * @param {Promise} promise A promise or promise-like object to retrieve a value from. * @param {object} [meta={}] Optional meta data to attach to the log. * @param {string[]|Function[]} [tags=[]] Additional tags to append to this log. * @returns {Promise<LogMessage>} A new Promise that resolves with the LogMessage object after the promise completes. */ result(promise, meta = {}, tags = []) { if(!promise || typeof promise.then !== 'function') { throw new Error('A promise must be provided as the first argument'); } const wrapper = new Promise(resolve => { promise .then(value => resolve(this.log('info', value, meta, tags))) .catch(err => resolve(this.log('error', err, meta, tags))); }); return wrapper; } } LambdaLog.symbols = symbols; module.exports = LambdaLog;