UNPKG

insta-meme-bot-cli

Version:

Production-ready Instagram meme bot that scrapes memes from Pinterest and posts them automatically with CLI and programmatic API support

278 lines (241 loc) 7.16 kB
/** * Advanced logging system for Instagram Meme Bot * Ultra-scalable with multiple log levels and outputs */ const fs = require('fs'); const path = require('path'); class Logger { constructor(options = {}) { this.logLevel = options.logLevel || 'info'; this.logFile = options.logFile || 'bot.log'; this.maxFileSize = options.maxFileSize || 10 * 1024 * 1024; // 10MB this.maxFiles = options.maxFiles || 5; this.enableConsole = options.enableConsole !== false; this.enableFile = options.enableFile !== false; this.levels = { error: 0, warn: 1, info: 2, debug: 3, trace: 4 }; this.colors = { error: '\x1b[31m', // Red warn: '\x1b[33m', // Yellow info: '\x1b[36m', // Cyan debug: '\x1b[35m', // Magenta trace: '\x1b[37m', // White reset: '\x1b[0m' }; this.emojis = { error: '❌', warn: '⚠️', info: 'ℹ️', debug: '🐛', trace: '🔍' }; this.initializeLogFile(); } /** * Initialize log file and handle rotation */ initializeLogFile() { if (!this.enableFile) return; try { const logDir = path.dirname(this.logFile); if (!fs.existsSync(logDir)) { fs.mkdirSync(logDir, { recursive: true }); } // Check if log rotation is needed if (fs.existsSync(this.logFile)) { const stats = fs.statSync(this.logFile); if (stats.size > this.maxFileSize) { this.rotateLogFile(); } } } catch (error) { console.error('Failed to initialize log file:', error.message); } } /** * Rotate log files when they get too large */ rotateLogFile() { try { const logDir = path.dirname(this.logFile); const logName = path.basename(this.logFile, path.extname(this.logFile)); const logExt = path.extname(this.logFile); // Shift existing log files for (let i = this.maxFiles - 1; i > 0; i--) { const oldFile = path.join(logDir, `${logName}.${i}${logExt}`); const newFile = path.join(logDir, `${logName}.${i + 1}${logExt}`); if (fs.existsSync(oldFile)) { if (i === this.maxFiles - 1) { fs.unlinkSync(oldFile); // Delete oldest } else { fs.renameSync(oldFile, newFile); } } } // Move current log to .1 const firstRotated = path.join(logDir, `${logName}.1${logExt}`); if (fs.existsSync(this.logFile)) { fs.renameSync(this.logFile, firstRotated); } } catch (error) { console.error('Failed to rotate log file:', error.message); } } /** * Check if message should be logged based on level * @param {string} level - Log level * @returns {boolean} Should log */ shouldLog(level) { return this.levels[level] <= this.levels[this.logLevel]; } /** * Format log message * @param {string} level - Log level * @param {string} message - Message to log * @param {Object} meta - Additional metadata * @returns {string} Formatted message */ formatMessage(level, message, meta = {}) { const timestamp = new Date().toISOString(); const emoji = this.emojis[level] || ''; const metaStr = Object.keys(meta).length > 0 ? ` ${JSON.stringify(meta)}` : ''; return `[${timestamp}] ${emoji} ${level.toUpperCase()}: ${message}${metaStr}`; } /** * Write to console with colors * @param {string} level - Log level * @param {string} message - Formatted message */ writeToConsole(level, message) { if (!this.enableConsole) return; const color = this.colors[level] || this.colors.reset; const coloredMessage = `${color}${message}${this.colors.reset}`; if (level === 'error') { console.error(coloredMessage); } else if (level === 'warn') { console.warn(coloredMessage); } else { console.log(coloredMessage); } } /** * Write to file * @param {string} message - Formatted message */ writeToFile(message) { if (!this.enableFile) return; try { // Check if rotation is needed before writing if (fs.existsSync(this.logFile)) { const stats = fs.statSync(this.logFile); if (stats.size > this.maxFileSize) { this.rotateLogFile(); } } fs.appendFileSync(this.logFile, message + '\n'); } catch (error) { console.error('Failed to write to log file:', error.message); } } /** * Generic log method * @param {string} level - Log level * @param {string} message - Message to log * @param {Object} meta - Additional metadata */ log(level, message, meta = {}) { if (!this.shouldLog(level)) return; const formattedMessage = this.formatMessage(level, message, meta); this.writeToConsole(level, formattedMessage); this.writeToFile(formattedMessage); } /** * Error level logging * @param {string} message - Error message * @param {Object} meta - Additional metadata */ error(message, meta = {}) { this.log('error', message, meta); } /** * Warning level logging * @param {string} message - Warning message * @param {Object} meta - Additional metadata */ warn(message, meta = {}) { this.log('warn', message, meta); } /** * Info level logging * @param {string} message - Info message * @param {Object} meta - Additional metadata */ info(message, meta = {}) { this.log('info', message, meta); } /** * Debug level logging * @param {string} message - Debug message * @param {Object} meta - Additional metadata */ debug(message, meta = {}) { this.log('debug', message, meta); } /** * Trace level logging * @param {string} message - Trace message * @param {Object} meta - Additional metadata */ trace(message, meta = {}) { this.log('trace', message, meta); } /** * Set log level * @param {string} level - New log level */ setLevel(level) { if (this.levels[level] !== undefined) { this.logLevel = level; } } /** * Create child logger with additional context * @param {Object} context - Additional context for all logs * @returns {Logger} Child logger */ child(context = {}) { const childLogger = new Logger({ logLevel: this.logLevel, logFile: this.logFile, enableConsole: this.enableConsole, enableFile: this.enableFile }); // Override log method to include context const originalLog = childLogger.log.bind(childLogger); childLogger.log = (level, message, meta = {}) => { const mergedMeta = { ...context, ...meta }; originalLog(level, message, mergedMeta); }; return childLogger; } /** * Clear log file */ clear() { if (this.enableFile && fs.existsSync(this.logFile)) { try { fs.writeFileSync(this.logFile, ''); } catch (error) { console.error('Failed to clear log file:', error.message); } } } } // Export singleton instance module.exports = new Logger();