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

@@ -14,6 +14,7 @@
*/
import { getPrismaClient } from '../database/trades'
import { logger } from '../utils/logger'
import { initializeDriftService } from '../drift/client'
import { getPythPriceMonitor } from '../pyth/price-monitor'
@@ -70,7 +71,7 @@ export class StopHuntTracker {
}): Promise<void> {
// Only track quality 85+ stop-outs (high-confidence trades)
if (params.originalQualityScore < 85) {
console.log(`⚠️ Stop hunt not tracked: Quality ${params.originalQualityScore} < 85 threshold`)
logger.log(`⚠️ Stop hunt not tracked: Quality ${params.originalQualityScore} < 85 threshold`)
return
}
@@ -93,9 +94,9 @@ export class StopHuntTracker {
}
})
console.log(`🎯 STOP HUNT RECORDED: ${params.symbol} ${params.direction.toUpperCase()}`)
console.log(` Quality: ${params.originalQualityScore}, Loss: $${params.stopLossAmount.toFixed(2)}`)
console.log(` Revenge window: 4 hours (expires ${revengeExpiresAt.toLocaleTimeString()})`)
logger.log(`🎯 STOP HUNT RECORDED: ${params.symbol} ${params.direction.toUpperCase()}`)
logger.log(` Quality: ${params.originalQualityScore}, Loss: $${params.stopLossAmount.toFixed(2)}`)
logger.log(` Revenge window: 4 hours (expires ${revengeExpiresAt.toLocaleTimeString()})`)
// Start monitoring if not already running
if (!this.isMonitoring) {
@@ -113,7 +114,7 @@ export class StopHuntTracker {
if (this.isMonitoring) return
this.isMonitoring = true
console.log('🔍 Stop Hunt Revenge Tracker: Monitoring started')
logger.log('🔍 Stop Hunt Revenge Tracker: Monitoring started')
// Check every 30 seconds
monitoringInterval = setInterval(async () => {
@@ -130,7 +131,7 @@ export class StopHuntTracker {
monitoringInterval = null
}
this.isMonitoring = false
console.log('🛑 Stop Hunt Revenge Tracker: Monitoring stopped')
logger.log('🛑 Stop Hunt Revenge Tracker: Monitoring stopped')
}
/**
@@ -152,13 +153,13 @@ export class StopHuntTracker {
if (activeStopHunts.length === 0) {
// No active stop hunts, stop monitoring to save resources
if (this.isMonitoring) {
console.log('📊 No active stop hunts - pausing monitoring')
logger.log('📊 No active stop hunts - pausing monitoring')
this.stopMonitoring()
}
return
}
console.log(`🔍 Checking ${activeStopHunts.length} active stop hunt(s)...`)
logger.log(`🔍 Checking ${activeStopHunts.length} active stop hunt(s)...`)
for (const stopHunt of activeStopHunts) {
await this.checkStopHunt(stopHunt as StopHuntRecord)
@@ -211,7 +212,7 @@ export class StopHuntTracker {
const shouldRevenge = await this.shouldExecuteRevenge(stopHunt, currentPrice)
if (shouldRevenge) {
console.log(`🔥 REVENGE CONDITIONS MET: ${stopHunt.symbol} ${stopHunt.direction.toUpperCase()}`)
logger.log(`🔥 REVENGE CONDITIONS MET: ${stopHunt.symbol} ${stopHunt.direction.toUpperCase()}`)
await this.executeRevengeTrade(stopHunt, currentPrice)
}
@@ -260,7 +261,7 @@ export class StopHuntTracker {
lowestInZone: currentPrice,
}
})
console.log(` ⏱️ LONG revenge zone entered at ${currentPrice.toFixed(2)}, waiting for 90s confirmation...`)
logger.log(` ⏱️ LONG revenge zone entered at ${currentPrice.toFixed(2)}, waiting for 90s confirmation...`)
return false
}
@@ -274,17 +275,17 @@ export class StopHuntTracker {
// Check if we've been in zone for 90+ seconds (1.5 minutes)
const timeInZone = now - stopHunt.firstCrossTime.getTime()
if (timeInZone >= 90000) { // 90 seconds = 1.5 minutes
console.log(` ✅ LONG revenge: Price held below entry for ${(timeInZone/60000).toFixed(1)}min, confirmed!`)
console.log(` Entry ${originalEntryPrice.toFixed(2)} → Current ${currentPrice.toFixed(2)}`)
logger.log(` ✅ LONG revenge: Price held below entry for ${(timeInZone/60000).toFixed(1)}min, confirmed!`)
logger.log(` Entry ${originalEntryPrice.toFixed(2)} → Current ${currentPrice.toFixed(2)}`)
return true
} else {
console.log(` ⏱️ LONG revenge: ${(timeInZone/60000).toFixed(1)}min in zone (need 1.5min)`)
logger.log(` ⏱️ LONG revenge: ${(timeInZone/60000).toFixed(1)}min in zone (need 1.5min)`)
return false
}
} else {
// Price left revenge zone - reset timer and increment counter
if (stopHunt.firstCrossTime) {
console.log(` ❌ LONG revenge: Price bounced back up to ${currentPrice.toFixed(2)}, resetting timer`)
logger.log(` ❌ LONG revenge: Price bounced back up to ${currentPrice.toFixed(2)}, resetting timer`)
await this.prisma.stopHunt.update({
where: { id: stopHunt.id },
data: {
@@ -310,7 +311,7 @@ export class StopHuntTracker {
highestInZone: currentPrice,
}
})
console.log(` ⏱️ SHORT revenge zone entered at ${currentPrice.toFixed(2)}, waiting for 90s confirmation...`)
logger.log(` ⏱️ SHORT revenge zone entered at ${currentPrice.toFixed(2)}, waiting for 90s confirmation...`)
return false
}
@@ -324,17 +325,17 @@ export class StopHuntTracker {
// Check if we've been in zone for 90+ seconds (1.5 minutes)
const timeInZone = now - stopHunt.firstCrossTime.getTime()
if (timeInZone >= 90000) { // 90 seconds = 1.5 minutes
console.log(` ✅ SHORT revenge: Price held above entry for ${(timeInZone/60000).toFixed(1)}min, confirmed!`)
console.log(` Entry ${originalEntryPrice.toFixed(2)} → Current ${currentPrice.toFixed(2)}`)
logger.log(` ✅ SHORT revenge: Price held above entry for ${(timeInZone/60000).toFixed(1)}min, confirmed!`)
logger.log(` Entry ${originalEntryPrice.toFixed(2)} → Current ${currentPrice.toFixed(2)}`)
return true
} else {
console.log(` ⏱️ SHORT revenge: ${(timeInZone/60000).toFixed(1)}min in zone (need 1.5min)`)
logger.log(` ⏱️ SHORT revenge: ${(timeInZone/60000).toFixed(1)}min in zone (need 1.5min)`)
return false
}
} else {
// Price left revenge zone - reset timer and increment counter
if (stopHunt.firstCrossTime) {
console.log(` ❌ SHORT revenge: Price dropped back to ${currentPrice.toFixed(2)}, resetting timer`)
logger.log(` ❌ SHORT revenge: Price dropped back to ${currentPrice.toFixed(2)}, resetting timer`)
await this.prisma.stopHunt.update({
where: { id: stopHunt.id },
data: {
@@ -354,9 +355,9 @@ export class StopHuntTracker {
*/
private async executeRevengeTrade(stopHunt: StopHuntRecord, currentPrice: number): Promise<void> {
try {
console.log(`🔥 EXECUTING REVENGE TRADE: ${stopHunt.symbol} ${stopHunt.direction.toUpperCase()}`)
console.log(` Original loss: $${stopHunt.stopLossAmount.toFixed(2)}`)
console.log(` Revenge size: 1.2x (getting our money back!)`)
logger.log(`🔥 EXECUTING REVENGE TRADE: ${stopHunt.symbol} ${stopHunt.direction.toUpperCase()}`)
logger.log(` Original loss: $${stopHunt.stopLossAmount.toFixed(2)}`)
logger.log(` Revenge size: 1.2x (getting our money back!)`)
// CRITICAL: Validate current ADX from 1-minute data cache
// Block revenge if trend has weakened (ADX < 20)
@@ -368,10 +369,10 @@ export class StopHuntTracker {
const currentADX = cachedData.adx
const dataAge = Date.now() - cachedData.timestamp
console.log(` 📊 Fresh ADX check: ${currentADX.toFixed(1)} (${(dataAge/1000).toFixed(0)}s old)`)
logger.log(` 📊 Fresh ADX check: ${currentADX.toFixed(1)} (${(dataAge/1000).toFixed(0)}s old)`)
if (currentADX < 20) {
console.log(` ❌ REVENGE BLOCKED: ADX ${currentADX.toFixed(1)} < 20 (weak trend, not worth re-entry)`)
logger.log(` ❌ REVENGE BLOCKED: ADX ${currentADX.toFixed(1)} < 20 (weak trend, not worth re-entry)`)
// Update database with failed reason
await this.prisma.stopHunt.update({
@@ -397,10 +398,10 @@ export class StopHuntTracker {
return
}
console.log(` ✅ ADX validation passed: ${currentADX.toFixed(1)} ≥ 20 (strong trend)`)
logger.log(` ✅ ADX validation passed: ${currentADX.toFixed(1)} ≥ 20 (strong trend)`)
} else {
console.log(` ⚠️ No fresh ADX data (cache age: ${cachedData ? (Date.now() - cachedData.timestamp)/1000 : 'N/A'}s)`)
console.log(` ⚠️ Proceeding with revenge but using original ADX ${stopHunt.originalADX}`)
logger.log(` ⚠️ No fresh ADX data (cache age: ${cachedData ? (Date.now() - cachedData.timestamp)/1000 : 'N/A'}s)`)
logger.log(` ⚠️ Proceeding with revenge but using original ADX ${stopHunt.originalADX}`)
}
// Call execute endpoint with revenge parameters
@@ -454,9 +455,9 @@ export class StopHuntTracker {
}
})
console.log(`✅ REVENGE TRADE EXECUTED: ${result.trade?.id}`)
console.log(`📊 SL Distance: $${Math.abs(slDistance).toFixed(2)} (${stopHunt.originalATR ? `${(Math.abs(slDistance) / stopHunt.originalATR).toFixed(2)}× ATR` : 'no ATR'})`)
console.log(`🔥 LET'S GET OUR MONEY BACK!`)
logger.log(`✅ REVENGE TRADE EXECUTED: ${result.trade?.id}`)
logger.log(`📊 SL Distance: $${Math.abs(slDistance).toFixed(2)} (${stopHunt.originalATR ? `${(Math.abs(slDistance) / stopHunt.originalATR).toFixed(2)}× ATR` : 'no ATR'})`)
logger.log(`🔥 LET'S GET OUR MONEY BACK!`)
// Send special Telegram notification
await this.sendRevengeNotification(stopHunt, result.trade)
@@ -524,7 +525,7 @@ Reversal Confirmed: Price crossed back through entry
})
if (!stopHunt) {
console.log(`⚠️ No stop hunt found for revenge trade ${params.revengeTradeId}`)
logger.log(`⚠️ No stop hunt found for revenge trade ${params.revengeTradeId}`)
return
}
@@ -538,10 +539,10 @@ Reversal Confirmed: Price crossed back through entry
})
const emoji = params.outcome.includes('TP') ? '✅' : '❌'
console.log(`${emoji} REVENGE OUTCOME: ${params.outcome} (${params.pnl >= 0 ? '+' : ''}$${params.pnl.toFixed(2)})`)
logger.log(`${emoji} REVENGE OUTCOME: ${params.outcome} (${params.pnl >= 0 ? '+' : ''}$${params.pnl.toFixed(2)})`)
if (params.failedReason) {
console.log(` Reason: ${params.failedReason}`)
logger.log(` Reason: ${params.failedReason}`)
}
} catch (error) {
@@ -568,7 +569,7 @@ Reversal Confirmed: Price crossed back through entry
})
if (expired.count > 0) {
console.log(`⏰ Expired ${expired.count} stop hunt revenge window(s)`)
logger.log(`⏰ Expired ${expired.count} stop hunt revenge window(s)`)
}
} catch (error) {
console.error('❌ Error expiring stop hunts:', error)
@@ -605,10 +606,10 @@ export async function startStopHuntTracking(): Promise<void> {
})
if (activeCount > 0) {
console.log(`🎯 Found ${activeCount} active stop hunt(s) - starting revenge tracker`)
logger.log(`🎯 Found ${activeCount} active stop hunt(s) - starting revenge tracker`)
tracker.startMonitoring()
} else {
console.log('📊 No active stop hunts - tracker will start when needed')
logger.log('📊 No active stop hunts - tracker will start when needed')
}
} catch (error) {
console.error('❌ Error starting stop hunt tracker:', error)