/** * Persistent File Logger - Survives Container Restarts * Critical for debugging database save failures and system issues */ import * as fs from 'fs' import { logger } from '../utils/logger' import * as path from 'path' const LOG_DIR = '/app/logs' const ERROR_LOG = path.join(LOG_DIR, 'errors.log') const TRADE_LOG = path.join(LOG_DIR, 'trades.log') const MAX_LOG_SIZE = 10 * 1024 * 1024 // 10MB per log file // Ensure log directory exists function ensureLogDir() { try { if (!fs.existsSync(LOG_DIR)) { fs.mkdirSync(LOG_DIR, { recursive: true }) } } catch (error) { console.error('Failed to create log directory:', error) } } // Rotate log if too large function rotateLogIfNeeded(logPath: string) { try { if (fs.existsSync(logPath)) { const stats = fs.statSync(logPath) if (stats.size > MAX_LOG_SIZE) { const timestamp = new Date().toISOString().replace(/[:.]/g, '-') const rotatedPath = `${logPath}.${timestamp}` fs.renameSync(logPath, rotatedPath) logger.log(`📦 Rotated log: ${rotatedPath}`) } } } catch (error) { console.error('Failed to rotate log:', error) } } // Format log entry function formatLogEntry(level: string, message: string, details?: any): string { const timestamp = new Date().toISOString() const detailsStr = details ? `\n${JSON.stringify(details, null, 2)}` : '' return `[${timestamp}] ${level}: ${message}${detailsStr}\n` } // Append to log file function appendToLog(logPath: string, entry: string) { try { ensureLogDir() rotateLogIfNeeded(logPath) fs.appendFileSync(logPath, entry, 'utf8') } catch (error) { console.error(`Failed to write to ${logPath}:`, error) } } /** * Log critical error (database failures, position issues) */ export function logCriticalError(message: string, details?: any) { const entry = formatLogEntry('CRITICAL', message, details) console.error('🔴 CRITICAL:', message, details || '') appendToLog(ERROR_LOG, entry) } /** * Log trade execution (success or failure) */ export function logTradeExecution( success: boolean, tradeDetails: { symbol: string direction: string entryPrice: number positionSize: number transactionSignature?: string error?: string } ) { const level = success ? 'SUCCESS' : 'FAILURE' const message = success ? `Trade opened: ${tradeDetails.symbol} ${tradeDetails.direction} @ $${tradeDetails.entryPrice}` : `Trade failed: ${tradeDetails.symbol} ${tradeDetails.direction} - ${tradeDetails.error}` const entry = formatLogEntry(level, message, tradeDetails) logger.log(success ? '✅' : '❌', message) appendToLog(TRADE_LOG, entry) } /** * Log database operation (create, update, query) */ export function logDatabaseOperation( operation: string, success: boolean, details: { table?: string recordId?: string error?: any retryAttempt?: number } ) { const level = success ? 'DB_SUCCESS' : 'DB_FAILURE' const message = success ? `${operation} succeeded: ${details.table || 'unknown'}` : `${operation} failed: ${details.error?.message || 'Unknown error'}` const entry = formatLogEntry(level, message, details) logger.log(success ? '💾' : '❌', message) appendToLog(ERROR_LOG, entry) } /** * Read recent error logs (for debugging) */ export function getRecentErrors(lines: number = 50): string[] { try { if (!fs.existsSync(ERROR_LOG)) { return [] } const content = fs.readFileSync(ERROR_LOG, 'utf8') const allLines = content.split('\n').filter(line => line.trim()) return allLines.slice(-lines) } catch (error) { console.error('Failed to read error log:', error) return [] } } /** * Read recent trade logs */ export function getRecentTrades(lines: number = 50): string[] { try { if (!fs.existsSync(TRADE_LOG)) { return [] } const content = fs.readFileSync(TRADE_LOG, 'utf8') const allLines = content.split('\n').filter(line => line.trim()) return allLines.slice(-lines) } catch (error) { console.error('Failed to read trade log:', error) return [] } }