@contentstack/cli-utilities
Version:
Utilities for contentstack projects
197 lines (196 loc) • 8.41 kB
JavaScript
"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;