import { LogLevel } from './cb-logger'; import winston from 'winston'; /** * Configuration options for InternalLogger */ export interface InternalLoggerConfig { /** * Whether to use JSON format for logging (default: false) */ useJsonFormat?: boolean; } /** * Context options for child loggers */ export interface ChildLoggerContext { /** * Additional context to include in all log messages */ [key: string]: any; } /** * Winston-based InternalLogger provides logging capabilities for the Chargebee Apps CLI itself * This is separate from the user-facing cb-logger and uses Winston for structured logging * Shared implementation for both public and private CLI packages */ export class InternalLogger { private logger: winston.Logger; private config: InternalLoggerConfig; constructor(config: InternalLoggerConfig = {}) { this.config = { useJsonFormat: false, ...config }; this.logger = this.createWinstonLogger(); } /** * Creates a Winston logger instance with appropriate formatting * @returns {winston.Logger} The configured Winston logger */ private createWinstonLogger(): winston.Logger { const logLevel = process.env['CB_LOG_LEVEL'] || LogLevel.INFO; // Create custom format based on configuration const customFormat = winston.format.combine( winston.format.timestamp(), winston.format.errors({ stack: true }), this.config.useJsonFormat ? this.createJsonFormat() : this.createTextFormat() ); return winston.createLogger({ level: logLevel.toLowerCase(), format: customFormat, transports: [ new winston.transports.Console({}) ] }); } /** * Creates JSON format for structured logging * @returns {winston.Logform.Format} The JSON format */ protected createJsonFormat(): winston.Logform.Format { return winston.format.combine( winston.format.timestamp(), winston.format.printf((info: winston.Logform.TransformableInfo) => { const logEntry: any = { timestamp: info['timestamp'], level: info.level, service: 'Internal', message: info.message }; // Include all additional context from child loggers Object.keys(info).forEach(key => { if (!['timestamp', 'level', 'message', 'stack'].includes(key)) { logEntry[key] = info[key]; } }); if (info['stack']) { logEntry.stack = info['stack']; } return JSON.stringify(logEntry); }) ); } /** * Creates text format for human-readable logging * @returns {winston.Logform.Format} The text format */ protected createTextFormat(): winston.Logform.Format { return winston.format.combine( winston.format.timestamp(), winston.format.colorize(), winston.format.printf((info: winston.Logform.TransformableInfo) => { const timestamp = info['timestamp']; const level = info.level; const service = 'Internal'; const message = info.message; const stack = info['stack'] ? `\n${info['stack']}` : ''; // Build context string from additional properties const contextParts: string[] = []; Object.keys(info).forEach(key => { if (!['timestamp', 'level', 'message', 'stack'].includes(key)) { contextParts.push(`${key}=${info[key]}`); } }); const contextStr = contextParts.length > 0 ? ` [${contextParts.join(', ')}]` : ''; return `${timestamp} [${level}] [${service}]${contextStr} ${message}${stack}`; }) ); } /** * Logs an informational message * @param {...any} args - The message arguments to log */ info(...args: any[]): void { const message = args.join(' '); this.logger.info(message); } /** * Logs a warning message * @param {...any} args - The message arguments to log */ warn(...args: any[]): void { const message = args.join(' '); this.logger.warn(message); } /** * Logs an error message * @param {...any} args - The message arguments to log */ error(...args: any[]): void { const message = args.join(' '); this.logger.error(message); } /** * Logs a debug message * @param {...any} args - The message arguments to log */ debug(...args: any[]): void { const message = args.join(' '); this.logger.debug(message); } /** * Logs a success message (alias for info with success indicator) * @param {...any} args - The message arguments to log */ success(...args: any[]): void { const message = args.join(' '); this.logger.info(`✅ ${message}`); } /** * Logs a failure message (alias for error with failure indicator) * @param {...any} args - The message arguments to log */ failure(...args: any[]): void { const message = args.join(' '); this.logger.error(`❌ ${message}`); } /** * Creates a child logger with additional context * @param {ChildLoggerContext} context - Additional context to include in all log messages * @returns {InternalLogger} A new InternalLogger instance with the child context */ child(context: ChildLoggerContext): InternalLogger { const childLogger = new InternalLogger(this.config); // Create a Winston child logger with the provided context childLogger.logger = this.logger.child(context); return childLogger; } } // Create a singleton instance for default logger export const __logger = new InternalLogger();