UNPKG

@contentstack/cli-utilities

Version:

Utilities for contentstack projects

197 lines (196 loc) 8.41 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const tslib_1 = require("tslib"); const traverse_1 = tslib_1.__importDefault(require("traverse")); const full_1 = require("klona/full"); const path_1 = require("path"); const winston = tslib_1.__importStar(require("winston")); const logging_1 = require("../constants/logging"); const session_path_1 = require("./session-path"); class Logger { constructor(config) { this.consoleSensitiveKeys = [ /authtoken/i, /^password$/i, /secret/i, /token/i, /api[-._]?key/i, /management[-._]?token/i, /sessionid/i, /orgid/i, /stack/i, ]; this.logSensitiveKeys = [/authtoken/i, /secret/i, /token/i, /management[-._]?token/i, /delivery[-._]?token/i]; this.config = config; winston.addColors(logging_1.levelColors); this.loggers = { error: this.getLoggerInstance('error'), warn: this.getLoggerInstance('warn'), info: this.getLoggerInstance('info'), debug: this.getLoggerInstance('debug'), success: this.getLoggerInstance('info'), // Map success to info }; } getLoggerInstance(level = 'info') { // Use session-based path for date-organized logging const sessionPath = (0, session_path_1.getSessionLogPath)(); const filePath = (0, path_1.normalize)(sessionPath).replace(/^(\.\.(\/|\\|$))+/, ''); return this.createLogger(level === 'hidden' ? 'error' : level, filePath); } get loggerOptions() { return { filename: '', maxFiles: 50, tailable: true, maxsize: 5000000, // 5MB }; } createLogger(level, filePath) { return winston.createLogger({ levels: logging_1.logLevels, level, transports: [ new winston.transports.File(Object.assign(Object.assign({}, this.loggerOptions), { filename: `${filePath}/${level}.log`, format: winston.format.combine(winston.format.timestamp(), winston.format.printf((info) => { // Apply minimal redaction for files (debugging info preserved) const redactedInfo = this.redact(info, false); return JSON.stringify(redactedInfo); })) })), new winston.transports.Console({ format: winston.format.combine(winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), winston.format.printf((info) => { // Apply full redaction for console (user-facing) const redactedInfo = this.redact(info, true); const colorizer = winston.format.colorize(); const levelText = redactedInfo.level.toUpperCase(); const { timestamp, message } = redactedInfo; return colorizer.colorize(redactedInfo.level, `[${timestamp}] ${levelText}: ${message}`); })), }), ], }); } isSensitiveKey(keyStr, consoleMode = false) { if (keyStr && typeof keyStr === 'string') { const keysToCheck = consoleMode ? this.consoleSensitiveKeys : this.logSensitiveKeys; return keysToCheck.some((regex) => regex.test(keyStr)); } return false; } redactObject(obj, consoleMode = false) { const self = this; (0, traverse_1.default)(obj).forEach(function redactor() { if (this.key && self.isSensitiveKey(this.key, consoleMode)) { this.update('[REDACTED]'); } }); return obj; } redact(info, consoleMode = false) { try { const copy = (0, full_1.klona)(info); this.redactObject(copy, consoleMode); const splat = copy[Symbol.for('splat')]; if (splat) this.redactObject(splat, consoleMode); return copy; } catch (_a) { return info; } } shouldLog(level, target) { // If console logging is disabled, don't log to console if (target === 'console' && this.config.consoleLoggingEnabled === false) { return false; } const configLevel = target === 'console' ? this.config.consoleLogLevel : this.config.logLevel; const minLevel = configLevel ? logging_1.logLevels[configLevel] : 2; return logging_1.logLevels[level] <= minLevel; } /* === Public Log Methods === */ error(message, meta) { if (this.shouldLog('error', 'console') || this.shouldLog('error', 'file')) { this.loggers.error.error(message, Object.assign(Object.assign({}, meta), { level: 'error' })); } } warn(message, meta) { if (this.shouldLog('warn', 'console') || this.shouldLog('warn', 'file')) { this.loggers.warn.warn(message, Object.assign(Object.assign({}, meta), { level: 'warn' })); } } info(message, meta) { if (this.shouldLog('info', 'console') || this.shouldLog('info', 'file')) { this.loggers.info.info(message, Object.assign(Object.assign({}, meta), { level: 'info' })); } } success(message, meta) { if (this.shouldLog('success', 'console') || this.shouldLog('success', 'file')) { this.loggers.success.log('success', message, Object.assign({}, meta)); } } debug(message, meta) { if (this.shouldLog('debug', 'console') || this.shouldLog('debug', 'file')) { this.loggers.debug.debug(message, Object.assign(Object.assign({}, meta), { level: 'debug' })); } } /* === Structured Logging === */ logError(params) { const logPayload = { level: logging_1.logLevels.error, message: params.message, meta: Object.assign(Object.assign({ type: params.type, error: params.error }, (params.context || {})), (params.meta || {})), }; // Always log to error file, but respect hidden parameter for console if (this.shouldLog('error', 'file')) { this.loggers.error.error(logPayload); } // For console, use debug level if hidden, otherwise error level const consoleLevel = params.hidden ? 'debug' : 'error'; if (this.shouldLog(consoleLevel, 'console')) { this.loggers[consoleLevel].error(logPayload); } } logWarn(params) { const logPayload = { level: logging_1.logLevels.warn, message: params.message, timestamp: new Date(), meta: Object.assign(Object.assign({ type: params.type, warn: params.warn }, (params.context || {})), (params.meta || {})), }; if (this.shouldLog('warn', 'console') || this.shouldLog('warn', 'file')) { this.loggers.warn.warn(logPayload); } } logInfo(params) { const logPayload = { level: logging_1.logLevels.info, message: params.message, timestamp: new Date(), meta: Object.assign(Object.assign({ type: params.type, info: params.info }, (params.context || {})), (params.meta || {})), }; if (this.shouldLog('info', 'console') || this.shouldLog('info', 'file')) { this.loggers.info.info(logPayload); } } logSuccess(params) { const logPayload = { level: 'success', message: params.message, timestamp: new Date(), meta: Object.assign(Object.assign({ type: params.type, data: params.data }, (params.context || {})), (params.meta || {})), }; if (this.shouldLog('success', 'console') || this.shouldLog('success', 'file')) { this.loggers.success.log(logPayload); } } logDebug(params) { const logPayload = { level: logging_1.logLevels.debug, message: params.message, meta: Object.assign(Object.assign({ type: params.type, debug: params.debug }, (params.context || {})), (params.meta || {})), }; if (this.shouldLog('debug', 'console') || this.shouldLog('debug', 'file')) { this.loggers.debug.debug(logPayload); } } } exports.default = Logger;