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:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user