From 0010afbbf938c2516dcf9efb0d63b229d18fd57c Mon Sep 17 00:00:00 2001 From: accius Date: Mon, 2 Feb 2026 11:34:24 -0500 Subject: [PATCH] error logging silencing --- iturhfprop-service/server.js | 4 +-- server.js | 50 ++++++++++++++++++++++++++---------- 2 files changed, 38 insertions(+), 16 deletions(-) diff --git a/iturhfprop-service/server.js b/iturhfprop-service/server.js index f298012..24cbfce 100644 --- a/iturhfprop-service/server.js +++ b/iturhfprop-service/server.js @@ -38,8 +38,8 @@ let consecutiveFailures = 0; let lastFailureTime = 0; let lastLogTime = 0; const FAILURE_THRESHOLD = 3; // Open after 3 failures -const CIRCUIT_RESET_TIME = 2 * 60 * 1000; // 2 minutes before retry -const LOG_INTERVAL = 30000; // Only log every 30 seconds +const CIRCUIT_RESET_TIME = 5 * 60 * 1000; // 5 minutes before retry +const LOG_INTERVAL = 60000; // Only log every 60 seconds function getCacheKey(params) { // Round coordinates to 1 decimal place for better cache hits diff --git a/server.js b/server.js index e854cdc..ff43b9d 100644 --- a/server.js +++ b/server.js @@ -41,6 +41,26 @@ if (ITURHFPROP_URL) { app.use(cors()); app.use(express.json()); +// ============================================ +// RATE-LIMITED LOGGING +// ============================================ +// Prevents log spam when services are down +const errorLogState = {}; +const ERROR_LOG_INTERVAL = 60000; // Only log same error once per minute + +function logErrorOnce(category, message) { + const key = `${category}:${message}`; + const now = Date.now(); + const lastLogged = errorLogState[key] || 0; + + if (now - lastLogged >= ERROR_LOG_INTERVAL) { + errorLogState[key] = now; + console.error(`[${category}] ${message}`); + return true; + } + return false; +} + // Serve static files - use 'dist' in production (Vite build), 'public' in development const staticDir = process.env.NODE_ENV === 'production' ? path.join(__dirname, 'dist') @@ -526,7 +546,7 @@ app.get('/api/dxcluster/spots', async (req, res) => { } catch (error) { clearTimeout(timeout); if (error.name !== 'AbortError') { - console.error('[DX Cluster] HamQTH error:', error.message); + logErrorOnce('DX Cluster', `HamQTH: ${error.message}`); } } return null; @@ -554,7 +574,7 @@ app.get('/api/dxcluster/spots', async (req, res) => { } catch (error) { clearTimeout(timeout); if (error.name !== 'AbortError') { - console.error('[DX Cluster] DX Spider Proxy error:', error.message); + logErrorOnce('DX Cluster', `Proxy: ${error.message}`); } } return null; @@ -679,9 +699,9 @@ app.get('/api/dxcluster/spots', async (req, res) => { }); client.on('error', (err) => { - // Only log unexpected errors, not connection resets (they're common) - if (!err.message.includes('ECONNRESET') && !err.message.includes('ETIMEDOUT') && !err.message.includes('ENOTFOUND')) { - console.error(`[DX Cluster] DX Spider ${node.host} error:`, err.message); + // Only log unexpected errors, not connection issues (they're common) + if (!err.message.includes('ECONNRESET') && !err.message.includes('ETIMEDOUT') && !err.message.includes('ENOTFOUND') && !err.message.includes('ECONNREFUSED')) { + logErrorOnce('DX Cluster', `DX Spider ${node.host}: ${err.message}`); } cleanup(); }); @@ -1542,7 +1562,7 @@ app.get('/api/myspots/:callsign', async (req, res) => { res.json(spotsWithLocations); } catch (error) { - console.error('[My Spots] Error:', error.message); + logErrorOnce('My Spots', error.message); res.json([]); } }); @@ -1770,7 +1790,7 @@ async function fetchIonosondeData() { return validStations; } catch (error) { - console.error('[Ionosonde] Fetch error:', error.message); + logErrorOnce('Ionosonde', `Fetch error: ${error.message}`); return ionosondeCache.data || []; } } @@ -1785,7 +1805,7 @@ app.get('/api/ionosonde', async (req, res) => { stations: stations }); } catch (error) { - console.error('[Ionosonde] API error:', error.message); + logErrorOnce('Ionosonde', `API: ${error.message}`); res.status(500).json({ error: 'Failed to fetch ionosonde data' }); } }); @@ -1923,12 +1943,12 @@ async function fetchITURHFPropPrediction(txLat, txLon, rxLat, rxLon, ssn, month, clearTimeout(timeoutId); if (!response.ok) { - console.log('[Hybrid] ITURHFProp returned error:', response.status, response.statusText); + logErrorOnce('Hybrid', `ITURHFProp returned ${response.status}`); return null; } const data = await response.json(); - console.log('[Hybrid] ITURHFProp prediction received, MUF:', data.muf); + // Only log success occasionally to reduce noise // Cache the result iturhfpropCache = { @@ -1940,7 +1960,9 @@ async function fetchITURHFPropPrediction(txLat, txLon, rxLat, rxLon, ssn, month, return data; } catch (err) { - console.log('[Hybrid] ITURHFProp service error:', err.name, err.message); + if (err.name !== 'AbortError') { + logErrorOnce('Hybrid', `ITURHFProp: ${err.message}`); + } return null; } } @@ -1952,17 +1974,17 @@ async function fetchITURHFPropHourly(txLat, txLon, rxLat, rxLon, ssn, month) { if (!ITURHFPROP_URL) return null; try { - console.log('[Hybrid] Fetching 24-hour prediction from ITURHFProp...'); const url = `${ITURHFPROP_URL}/api/predict/hourly?txLat=${txLat}&txLon=${txLon}&rxLat=${rxLat}&rxLon=${rxLon}&ssn=${ssn}&month=${month}`; const response = await fetch(url, { timeout: 60000 }); // 60s timeout for 24-hour calc if (!response.ok) return null; const data = await response.json(); - console.log('[Hybrid] Received 24-hour prediction'); return data; } catch (err) { - console.log('[Hybrid] ITURHFProp hourly unavailable:', err.message); + if (err.name !== 'AbortError') { + logErrorOnce('Hybrid', `ITURHFProp hourly: ${err.message}`); + } return null; } }