'use strict'; const util = require('util'); const utils = require('./utils'); const depd = require('depd')('egg-logger'); /** * Base class for all sub Logger class. * It extends Map, and can contains multi {@link Transport} * * @example * ```js * const logger = new Logger(); * logger.set('file', new FileTransport({ * file: '/path/to/log', * level: 'INFO', * })); * logger.set('console', new ConsoleTransport({ * level: 'INFO', * })); * logger.info('foo'); * ``` */ class Logger extends Map { /** * @param {Object} options - assign with `defaults` propery */ constructor(options) { super(); this.options = utils.assign(this.defaults, options); this.transports = {}; this.name = this.constructor.name; this.redirectLoggers = new Map(); this.duplicateLoggers = new Map(); } /** * disable a transport * @param {String} name - transport name */ disable(name) { const transport = this.get(name); if (transport) transport.disable(); } /** * enable a transport * @param {String} name - transport name */ enable(name) { const transport = this.get(name); if (transport) transport.enable(); } /** * Send log to all transports. * It's proxy to {@link Transport}'s log method. * @param {String} level - log level * @param {Array} args - log arguments * @param {Object} meta - log meta */ log(level, args, meta) { let excludes; let { logger, options } = this.duplicateLoggers.get(level) || {}; if (logger) { excludes = options.excludes; logger.log(level, args, meta); } else { logger = this.redirectLoggers.get(level); if (logger) { logger.log(level, args, meta); return; } } for (const [ key, transport ] of this.entries()) { if (transport.shouldLog(level) && !(excludes && excludes.includes(key))) { transport.log(level, args, meta); } } } /** * write raw log to all transports * @param {String} msg - log message */ write(msg) { // support util.format if (arguments.length > 1) msg = util.format.apply(util, arguments); // `NONE` is the top level this.log('NONE', [ msg ], { raw: true }); } /** * Redirect specify level log to the other logger * @param {String} level - log level * @param {Logger} logger - target logger instance */ redirect(level, logger) { level = level.toUpperCase(); if (!this.redirectLoggers.has(level) && logger instanceof Logger) { this.redirectLoggers.set(level, logger); } } /** * Un-redirect specify level log * @param {String} level - log level */ unredirect(level) { level = level.toUpperCase(); this.redirectLoggers.delete(level); } /** * Duplicate specify level log to the other logger * @param {String} level - log level * @param {Logger} logger - target logger instance * @param {Object} [options] - { excludes: [] } */ duplicate(level, logger, options = {}) { level = level.toUpperCase(); if (!this.duplicateLoggers.has(level) && logger instanceof Logger) { this.duplicateLoggers.set(level, { logger, options }); } } /** * Un-duplicate specify level log * @param {String} level - log level */ unduplicate(level) { level = level.toUpperCase(); this.duplicateLoggers.delete(level); } /** * Reload all transports */ reload() { for (const transport of this.values()) { transport.reload(); } } /** * End all transports */ close() { for (const transport of this.values()) { transport.close(); } } /** * @deprecated */ end() { depd('logger.end() is deprecated, use logger.close()'); this.close(); } } [ 'error', 'warn', 'info', 'debug' ].forEach(level => { const LEVEL = level.toUpperCase(); Logger.prototype[level] = function() { this.log(LEVEL, arguments); }; }); module.exports = Logger;