UNPKG

pot-logger

Version:

A powerful log system for node.js, with zero configuration

570 lines (485 loc) 14.6 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.logger = exports.setLoggers = exports.defaultAppenders = undefined; var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; exports.setConfig = setConfig; exports.overrideConsole = overrideConsole; exports.resetConsole = resetConsole; exports.overrideConsoleInRuntime = overrideConsoleInRuntime; exports.getLogger = getLogger; exports.hasLogger = hasLogger; exports.createLogger = createLogger; exports.ensureLogger = ensureLogger; exports.flush = flush; var _log4js = require('log4js'); var _log4js2 = _interopRequireDefault(_log4js); var _path = require('path'); var _chalk = require('chalk'); var _chalk2 = _interopRequireDefault(_chalk); var _fsExtra = require('fs-extra'); var _globby = require('globby'); var _globby2 = _interopRequireDefault(_globby); var _deasync = require('deasync'); var _deasync2 = _interopRequireDefault(_deasync); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; } const isFunction = src => typeof src === 'function'; const isObject = src => typeof src === 'object'; const isString = src => typeof src === 'string'; const noop = () => {}; const shutdown = (0, _deasync2.default)(_log4js2.default.shutdown); _log4js2.default.configure({ appenders: { out: { type: 'stdout' } }, categories: { default: { appenders: ['out'], level: 'info' } } }); const defaultCategory = 'out'; const defaultLogLevel = 'INFO'; const defaultFileAppender = { type: 'file', filename: defaultCategory, maxLogSize: 10485760, // 10MB backups: 5, compress: true }; const config = { enable: true, logLevel: defaultLogLevel, logsDir: (0, _path.resolve)('.logs'), daemon: false, overrideConsole: false }; const colored = function () { let color = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'dim'; const pattern = '[%c]'; const validatColor = color => { if (!isFunction(_chalk2.default[color])) { throw new Error(`Category with style "${color}" is NOT support.`); } }; const getColor = color.split('.').reduce((chalkChaining, color) => { validatColor(color); return chalkChaining[color]; }, _chalk2.default); return getColor(pattern); }; const createAppender = function createAppender(category, description, daemon) { const type = description.type, color = description.color, file = description.file, other = _objectWithoutProperties(description, ['type', 'color', 'file']); if (type) { return description; } if (daemon) { const categoryPattern = file ? ' ' : ' %c - '; return _extends({}, defaultFileAppender, { layout: { type: 'pattern', pattern: `[%d] [%p]${categoryPattern}%m` } }, other, { filename: file ? category : defaultCategory }); } else { return _extends({}, other, { type: 'console', layout: { type: 'pattern', pattern: `%[%p%] ${colored(color)} %m` } }); } }; const nativeConsole = {}; const SymbolEnsureLatest = Symbol('EnsureLatest'); const getLevel = (key, level, defaultLevel) => (isObject(level) ? level[key] : level) || defaultLevel; const logSystem = function () { let shouldReload = false; let shouldUpdateDaemon = false; let shouldUpdateAppenders = false; let shouldUpdateCategories = false; let shouldReloadConfigure = false; let shouldReloadEnableStatus = false; let enable = config.enable; let appenders = {}; let categories = {}; const loggers = new Map(); const getCategories = () => { const appenderKeys = Object.keys(appenders); const builtInLevelAppenders = []; if (appenders['$_all']) { builtInLevelAppenders.push('$_all'); } if (appenders['$_err']) { builtInLevelAppenders.push('$_err'); } return appenderKeys.reduce((categories, key) => { const isCustomKey = !/^[$_]/.test(key); const $key = '$' + key; if (isCustomKey && appenders[$key]) { categories[key] = { appenders: [$key].concat(builtInLevelAppenders), level: 'ALL' }; } return categories; }, { default: { appenders: [`$${defaultCategory}`].concat(builtInLevelAppenders), level: 'ALL' } }); }; const performUpdateAppenders = () => { if (!shouldUpdateAppenders) { return; } const logsDir = config.logsDir, logLevel = config.logLevel; const appenderKeys = Object.keys(appenders); const ensureFilename = appender => { let name = appender.filename; if (!name) { return appender; } if ((0, _path.extname)(name) !== '.log') { name += '.log'; } appender.filename = (0, _path.isAbsolute)(name) ? name : (0, _path.join)(logsDir, name); }; const ensureLevelAppender = (key, appender) => { if (key.charAt(0) === '$') return; const $key = '$' + key; if (~appenderKeys.indexOf($key)) return; appenders[$key] = { type: 'logLevelFilter', appender: key, level: function () { if (key === '_err') return 'ERROR'; if (key === '_all') return 'ALL'; if (appender.level) return appender.level; return getLevel(key, logLevel, defaultLogLevel); }(), maxLevel: appender.maxLevel || 'MARK' }; }; appenderKeys.forEach(key => { let appender = appenders[key]; // lazy appender if (isFunction(appender)) { appenders[key] = appender = appender(); } ensureFilename(appender); ensureLevelAppender(key, appender); }); shouldUpdateAppenders = false; }; const performUpdateCategories = () => { if (!shouldUpdateCategories) return; categories = getCategories(); shouldUpdateCategories = false; }; const performUpdateDaemon = () => { if (!shouldUpdateDaemon) { return; } if (enable) { if (config.daemon) { Object.assign(appenders, { _all: defaultAppenders.all, _err: defaultAppenders.err, [defaultCategory]: defaultAppenders.out }); } else { delete appenders._all; delete appenders._err; delete appenders.$_all; delete appenders.$_err; appenders[defaultCategory] = defaultAppenders.con; } } shouldUpdateDaemon = false; }; const performReloadConfigure = () => { if (shouldReloadConfigure) { shutdown(); _log4js2.default.configure({ appenders, categories }); } }; const performReloadEnableStatus = () => { if (!shouldReloadEnableStatus) { return; } enable = config.enable; shouldReloadEnableStatus = false; }; return { get shouldReload() { return shouldReload; }, get appenders() { return appenders; }, requestUpdateDaemon() { shouldUpdateDaemon = true; shouldUpdateAppenders = true; shouldUpdateCategories = true; shouldReloadConfigure = true; shouldReload = true; }, requestUpdateAppenders() { shouldUpdateAppenders = true; shouldReloadConfigure = true; shouldReload = true; }, requestUpdateCategories() { shouldUpdateCategories = true; shouldReloadConfigure = true; shouldReload = true; }, requestReloadConfigure() { shouldReloadConfigure = true; shouldReload = true; }, requestReloadEnableStatus() { shouldUpdateDaemon = true; shouldUpdateAppenders = true; shouldUpdateCategories = true; shouldReloadConfigure = true; shouldReloadEnableStatus = true; shouldReload = true; }, reload() { performReloadEnableStatus(); performUpdateDaemon(); performUpdateAppenders(); performUpdateCategories(); performReloadConfigure(); shouldReload = false; }, hasLogger(category) { return loggers.has(category); }, getLogger() { let category = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : defaultCategory; if (loggers.has(category)) { return loggers.get(category); } let origin = null; let cache = {}; const ensureLatest = () => { if (this.shouldReload) { this.reload(); cache = {}; } if (!origin) origin = _log4js2.default.getLogger(category); return origin; }; const reflect = name => { ensureLatest(); if (cache[name]) return cache[name]; return cache[name] = enable ? origin[name].bind(origin) : noop; }; // prettier-ignore const logger = { [SymbolEnsureLatest]: ensureLatest, get all() { return reflect('all'); }, get trace() { return reflect('trace'); }, get debug() { return reflect('debug'); }, get info() { return reflect('info'); }, get log() { return reflect('info'); }, get warn() { return reflect('warn'); }, get error() { return reflect('error'); }, get fatal() { return reflect('fatal'); }, get mark() { return reflect('mark'); } }; loggers.set(category, logger); return logger; } }; }(); const defaultAppenders = exports.defaultAppenders = { out: _extends({}, defaultFileAppender), err: _extends({}, defaultFileAppender, { filename: 'err' }), all: _extends({}, defaultFileAppender, { filename: 'all' }), con: { type: 'console', layout: { type: 'pattern', pattern: '%[%p%] %m' } } }; function setConfig(maybeKey, maybeVal) { const prev = _extends({}, config); Object.assign(config, isObject(maybeKey) ? maybeKey : { [maybeKey]: maybeVal }); if (prev.enable !== config.enable) { logSystem.requestReloadEnableStatus(); } if (prev.daemon !== config.daemon) { logSystem.requestUpdateDaemon(); } if (prev.logLevel !== config.logLevel) { Object.keys(logSystem.appenders).forEach(key => { const $key = '$' + key; const levelAppender = logSystem.appenders[$key]; if (levelAppender) { const newLevel = getLevel(key, config.logLevel); if (newLevel) levelAppender.level = newLevel; } }); logSystem.requestReloadConfigure(); } if (prev.logsDir !== config.logsDir) { logSystem.requestUpdateAppenders(); } if (prev.overrideConsole && !config.overrideConsole) { resetConsole(); } else if (!prev.overrideConsole && config.overrideConsole) { overrideConsole(); } } function overrideConsole() { let logger = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : logSystem.getLogger(); let filter = arguments[1]; logger = logger[SymbolEnsureLatest] ? logger[SymbolEnsureLatest]() : logger; const createReflection = method => { return function () { for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } if (isFunction(filter)) { args = filter(args, method, logger); } if (args.length) { logger[method].apply(logger, args); } }; }; nativeConsole.trace = console.trace; nativeConsole.debug = console.debug; nativeConsole.dir = console.dir; nativeConsole.info = console.info; nativeConsole.log = console.log; nativeConsole.warn = console.warn; nativeConsole.error = console.error; console.trace = createReflection('trace'); console.debug = createReflection('debug'); console.dir = createReflection('info'); console.log = createReflection('info'); console.warn = createReflection('warn'); console.error = createReflection('error'); } function resetConsole() { console.trace = nativeConsole.trace; console.debug = nativeConsole.debug; console.dir = nativeConsole.dir; console.log = nativeConsole.log; console.warn = nativeConsole.warn; console.error = nativeConsole.error; } function overrideConsoleInRuntime(start, logger, filter) { overrideConsole(logger, filter); return Promise.resolve(start()).then(() => { resetConsole(); config.overrideConsole && overrideConsole(); }); } function getLogger(category) { return logSystem.getLogger(category); } function hasLogger(category) { return logSystem.hasLogger(category); } function createLogger(category, description) { if (logSystem.hasLogger(category)) { throw new Error(`Failed to create logger: "${category}" has already exists.`); } let appender; if (isObject(description)) { appender = createAppender(category, description, config.daemon); } else { let color; if (isString(description)) { color = description; } const ref = { category, get daemon() { return config.daemon; }, get defaultDaemonAppender() { return _extends({}, defaultFileAppender); }, get defaultConsoleAppender() { return { type: 'console', layout: { type: 'pattern', pattern: `%[%p%] ${colored(color)} %m` } }; } }; appender = () => { if (isFunction(description)) { return createAppender(category, description(ref), config.daemon); } else { const finalDescription = { color: description }; return createAppender(category, finalDescription, config.daemon); } }; } logSystem.appenders[category] = appender; // TODO: set level appender logSystem.requestUpdateAppenders(); logSystem.requestUpdateCategories(); return logSystem.getLogger(category); } function ensureLogger(category, description) { return hasLogger(category) ? getLogger(category) : createLogger(category, description); } function flush() { let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var _options$mode = options.mode; const mode = _options$mode === undefined ? 0o644 : _options$mode; var _options$removeDir = options.removeDir; const removeDir = _options$removeDir === undefined ? false : _options$removeDir; var _options$logsDir = options.logsDir; const logsDir = _options$logsDir === undefined ? config.logsDir : _options$logsDir; if (removeDir) return (0, _fsExtra.remove)(logsDir); const glob = pattern => (0, _globby2.default)(pattern, { absolute: true }); const each = iterator => arr => Promise.all(arr.map(iterator)); const logFilesPattern = `${logsDir}/*.log`; const rotatedFilesPattern = `${logsDir}/*.log.[0-9]*`; const renewFile = file => (0, _fsExtra.open)(file, 'w', mode).then(_fsExtra.close); const removeFile = file => (0, _fsExtra.remove)(file); return Promise.all([glob(logFilesPattern).then(each(renewFile)), glob(rotatedFilesPattern).then(each(removeFile))]); } const setLoggers = exports.setLoggers = setConfig; const logger = exports.logger = createLogger(defaultCategory, defaultAppenders.con); exports.default = logger;