feat: Add production logging gating (Phase 1, Task 1.1)

- Created logger utility with environment-based gating (lib/utils/logger.ts)
- Replaced 517 console.log statements with logger.log (71% reduction)
- Fixed import paths in 15 files (resolved comment-trapped imports)
- Added DEBUG_LOGS=false to .env
- Achieves 71% immediate log reduction (517/731 statements)
- Expected 90% reduction in production when deployed

Impact: Reduced I/O blocking, lower log volume in production
Risk: LOW (easy rollback, non-invasive)
Phase: Phase 1, Task 1.1 (Quick Wins - Console.log Production Gating)

Files changed:
- NEW: lib/utils/logger.ts (production-safe logging)
- NEW: scripts/replace-console-logs.js (automation tool)
- Modified: 15 lib/*.ts files (console.log → logger.log)
- Modified: .env (DEBUG_LOGS=false)

Next: Task 1.2 (Image Size Optimization)
This commit is contained in:
mindesbunister
2025-12-05 00:32:41 +01:00
parent cc3a0a85a0
commit 302511293c
20 changed files with 2223 additions and 518 deletions

View File

@@ -6,6 +6,7 @@
*/
import { getInitializedPositionManager } from '../trading/position-manager'
import { logger } from '../utils/logger'
import { initializeDriftService } from '../drift/client'
import { getPrismaClient, createTrade } from '../database/trades'
import { getMarketConfig, getMergedConfig } from '../../config/trading'
@@ -25,16 +26,16 @@ export async function initializePositionManagerOnStartup() {
initStarted = true
console.log('🚀 Initializing Position Manager on startup...')
logger.log('🚀 Initializing Position Manager on startup...')
try {
// CRITICAL: Run database sync validator FIRST to clean up duplicates
const { validateAllOpenTrades } = await import('../database/sync-validator')
console.log('🔍 Running database sync validation before Position Manager init...')
logger.log('🔍 Running database sync validation before Position Manager init...')
const validationResult = await validateAllOpenTrades()
if (validationResult.ghosts > 0) {
console.log(`✅ Cleaned up ${validationResult.ghosts} ghost/duplicate trades`)
logger.log(`✅ Cleaned up ${validationResult.ghosts} ghost/duplicate trades`)
}
// Then validate open trades against Drift positions
@@ -46,28 +47,28 @@ export async function initializePositionManagerOnStartup() {
const manager = await getInitializedPositionManager()
const status = manager.getStatus()
console.log(`✅ Position Manager ready - ${status.activeTradesCount} active trades`)
logger.log(`✅ Position Manager ready - ${status.activeTradesCount} active trades`)
if (status.activeTradesCount > 0) {
console.log(`📊 Monitoring: ${status.symbols.join(', ')}`)
logger.log(`📊 Monitoring: ${status.symbols.join(', ')}`)
}
// CRITICAL (Dec 2, 2025): Start data cleanup service for 4-week retention
// User directive: "we want to store the data for 4 weeks"
// Runs daily at 3 AM to delete MarketData records older than 28 days
console.log('🧹 Starting data cleanup service...')
logger.log('🧹 Starting data cleanup service...')
startDataCleanup()
// Start blocked signal price tracking
console.log('🔬 Starting blocked signal price tracker...')
logger.log('🔬 Starting blocked signal price tracker...')
startBlockedSignalTracking()
// Start stop hunt revenge tracker
console.log('🎯 Starting stop hunt revenge tracker...')
logger.log('🎯 Starting stop hunt revenge tracker...')
await startStopHuntTracking()
// Start smart entry validation queue (Nov 30, 2025)
console.log('🧠 Starting smart entry validation system...')
logger.log('🧠 Starting smart entry validation system...')
await startSmartValidation()
} catch (error) {
console.error('❌ Failed to initialize Position Manager on startup:', error)
@@ -107,11 +108,11 @@ async function validateOpenTrades() {
const allTradesToCheck = [...openTrades, ...recentlyClosedTrades]
if (allTradesToCheck.length === 0) {
console.log('✅ No open trades to validate')
logger.log('✅ No open trades to validate')
return
}
console.log(`🔍 Validating ${openTrades.length} open + ${recentlyClosedTrades.length} recently closed trades against Drift...`)
logger.log(`🔍 Validating ${openTrades.length} open + ${recentlyClosedTrades.length} recently closed trades against Drift...`)
// CRITICAL: Group trades by symbol to handle multiple DB entries for same Drift position
// This prevents reopening old closed trades when only the most recent should be restored
@@ -135,7 +136,7 @@ async function validateOpenTrades() {
// Close any older trades BEFORE validating the most recent
for (const oldTrade of olderTrades) {
if (oldTrade.exitReason === null) {
console.log(`🗑️ Closing duplicate old trade: ${oldTrade.id} (${symbol}, created ${oldTrade.createdAt.toISOString()})`)
logger.log(`🗑️ Closing duplicate old trade: ${oldTrade.id} (${symbol}, created ${oldTrade.createdAt.toISOString()})`)
await prisma.trade.update({
where: { id: oldTrade.id },
data: {
@@ -161,8 +162,8 @@ async function validateOpenTrades() {
if (!position || position.size === 0) {
// No position on Drift
if (trade.status === 'open') {
console.log(`⚠️ PHANTOM TRADE: ${trade.symbol} marked open in DB but not found on Drift`)
console.log(` 🗑️ Auto-closing phantom trade...`)
logger.log(`⚠️ PHANTOM TRADE: ${trade.symbol} marked open in DB but not found on Drift`)
logger.log(` 🗑️ Auto-closing phantom trade...`)
await prisma.trade.update({
where: { id: trade.id },
@@ -184,17 +185,17 @@ async function validateOpenTrades() {
const driftDirection = position.side.toLowerCase() as 'long' | 'short'
if (driftDirection !== trade.direction) {
console.log(`⚠️ DIRECTION MISMATCH: ${trade.symbol} DB=${trade.direction} Drift=${driftDirection}`)
logger.log(`⚠️ DIRECTION MISMATCH: ${trade.symbol} DB=${trade.direction} Drift=${driftDirection}`)
continue
}
// CRITICAL: If DB says closed but Drift shows open, restore tracking!
if (trade.exitReason !== null) {
console.log(`🔴 CRITICAL: ${trade.symbol} marked as CLOSED in DB but still OPEN on Drift!`)
console.log(` DB entry: $${trade.entryPrice.toFixed(2)} | Drift entry: $${position.entryPrice.toFixed(2)}`)
console.log(` DB exit: ${trade.exitReason} at ${trade.exitTime?.toISOString()}`)
console.log(` Drift: ${position.size} ${trade.symbol} ${driftDirection} @ $${position.entryPrice.toFixed(2)}`)
console.log(` 🔄 Reopening trade and correcting entry price to match Drift...`)
logger.log(`🔴 CRITICAL: ${trade.symbol} marked as CLOSED in DB but still OPEN on Drift!`)
logger.log(` DB entry: $${trade.entryPrice.toFixed(2)} | Drift entry: $${position.entryPrice.toFixed(2)}`)
logger.log(` DB exit: ${trade.exitReason} at ${trade.exitTime?.toISOString()}`)
logger.log(` Drift: ${position.size} ${trade.symbol} ${driftDirection} @ $${position.entryPrice.toFixed(2)}`)
logger.log(` 🔄 Reopening trade and correcting entry price to match Drift...`)
// Calculate position size in USD using Drift's entry price
const currentPrice = await driftService.getOraclePrice(marketConfig.driftMarketIndex)
@@ -213,9 +214,9 @@ async function validateOpenTrades() {
}
})
console.log(` ✅ Trade restored with corrected entry: $${position.entryPrice.toFixed(2)} (was $${trade.entryPrice.toFixed(2)})`)
logger.log(` ✅ Trade restored with corrected entry: $${position.entryPrice.toFixed(2)} (was $${trade.entryPrice.toFixed(2)})`)
} else {
console.log(`${trade.symbol} ${trade.direction}: Position verified on Drift`)
logger.log(`${trade.symbol} ${trade.direction}: Position verified on Drift`)
}
// CRITICAL FIX (Nov 16, 2025): Restore missing on-chain orders
@@ -255,11 +256,11 @@ async function restoreOrdersIfMissing(
const hasOrders = position.orders && position.orders.length > 0
if (hasOrders) {
console.log(`${trade.symbol} has ${position.orders.length} on-chain orders - protection active`)
logger.log(`${trade.symbol} has ${position.orders.length} on-chain orders - protection active`)
return // Orders exist, nothing to do
}
console.log(`⚠️ ${trade.symbol} has NO on-chain orders - restoring TP/SL protection...`)
logger.log(`⚠️ ${trade.symbol} has NO on-chain orders - restoring TP/SL protection...`)
// Import order placement function
const { placeExitOrders } = await import('../drift/orders')
@@ -278,11 +279,11 @@ async function restoreOrdersIfMissing(
})
if (result.success) {
console.log(`✅ Orders restored for ${trade.symbol}:`)
console.log(` TP1: $${trade.takeProfit1Price.toFixed(4)} (75%)`)
console.log(` TP2: $${trade.takeProfit2Price.toFixed(4)} (runner trigger)`)
console.log(` SL: $${trade.stopLossPrice.toFixed(4)}`)
console.log(` TX: ${result.signatures?.[0]?.slice(0, 8)}...`)
logger.log(`✅ Orders restored for ${trade.symbol}:`)
logger.log(` TP1: $${trade.takeProfit1Price.toFixed(4)} (75%)`)
logger.log(` TP2: $${trade.takeProfit2Price.toFixed(4)} (runner trigger)`)
logger.log(` SL: $${trade.stopLossPrice.toFixed(4)}`)
logger.log(` TX: ${result.signatures?.[0]?.slice(0, 8)}...`)
// Update database with order transaction signatures
await prisma.trade.update({
@@ -324,17 +325,17 @@ async function detectOrphanedPositions() {
const prisma = getPrismaClient()
const driftService = await initializeDriftService()
console.log('🔍 Checking for orphaned positions on Drift...')
logger.log('🔍 Checking for orphaned positions on Drift...')
// Get all open positions from Drift
const driftPositions = await driftService.getAllPositions()
if (driftPositions.length === 0) {
console.log('✅ No positions on Drift')
logger.log('✅ No positions on Drift')
return
}
console.log(`🔍 Found ${driftPositions.length} positions on Drift, checking database...`)
logger.log(`🔍 Found ${driftPositions.length} positions on Drift, checking database...`)
// Get all open trades from database
const openTrades = await prisma.trade.findMany({
@@ -349,7 +350,7 @@ async function detectOrphanedPositions() {
const positionKey = `${position.symbol}-${position.side.toLowerCase()}`
if (trackedSymbols.has(positionKey)) {
console.log(`${position.symbol} ${position.side} tracked in database`)
logger.log(`${position.symbol} ${position.side} tracked in database`)
continue
}
@@ -359,12 +360,12 @@ async function detectOrphanedPositions() {
const currentPrice = await driftService.getOraclePrice(marketConfig.driftMarketIndex)
const positionSizeUSD = Math.abs(position.size) * currentPrice
console.log(`🚨 ORPHAN POSITION DETECTED!`)
console.log(` Symbol: ${position.symbol}`)
console.log(` Direction: ${position.side}`)
console.log(` Size: ${Math.abs(position.size)} (notional: $${positionSizeUSD.toFixed(2)})`)
console.log(` Entry: $${position.entryPrice.toFixed(4)}`)
console.log(` Current: $${currentPrice.toFixed(4)}`)
logger.log(`🚨 ORPHAN POSITION DETECTED!`)
logger.log(` Symbol: ${position.symbol}`)
logger.log(` Direction: ${position.side}`)
logger.log(` Size: ${Math.abs(position.size)} (notional: $${positionSizeUSD.toFixed(2)})`)
logger.log(` Entry: $${position.entryPrice.toFixed(4)}`)
logger.log(` Current: $${currentPrice.toFixed(4)}`)
// Log to persistent file
logCriticalError('ORPHAN POSITION DETECTED - Creating retroactive database record', {
@@ -398,7 +399,7 @@ async function detectOrphanedPositions() {
: entryPrice * (1 - config.takeProfit2Percent / 100)
// Create retroactive database record
console.log(`🔄 Creating retroactive database record...`)
logger.log(`🔄 Creating retroactive database record...`)
const trade = await createTrade({
positionId: `ORPHAN-${Date.now()}`, // Fake position ID since we don't have transaction
symbol: position.symbol,
@@ -418,7 +419,7 @@ async function detectOrphanedPositions() {
status: 'open',
})
console.log(`✅ Retroactive database record created: ${trade.id}`)
logger.log(`✅ Retroactive database record created: ${trade.id}`)
// Send Telegram notification
try {
@@ -438,7 +439,7 @@ async function detectOrphanedPositions() {
console.error('Failed to send orphan notification:', telegramError)
}
console.log(`🎯 Orphan position now tracked and monitored`)
logger.log(`🎯 Orphan position now tracked and monitored`)
} catch (recoveryError) {
console.error(`❌ Failed to recover orphan position ${position.symbol}:`, recoveryError)
@@ -449,7 +450,7 @@ async function detectOrphanedPositions() {
}
}
console.log('✅ Orphan position detection complete')
logger.log('✅ Orphan position detection complete')
} catch (error) {
console.error('❌ Error detecting orphaned positions:', error)