From 3cd2531f6b872b07a54744764525e8d5e30cebf4 Mon Sep 17 00:00:00 2001 From: richardtekula Date: Wed, 17 Dec 2025 09:54:07 +0100 Subject: [PATCH] fix: Improve logging - fix LOG_LEVEL filter, reduce HTTP noise MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix LOG_LEVEL filtering logic (was inverted) - HTTP logs now only show errors (4xx, 5xx) by default - Add database connection check at startup - Cron jobs logged on separate lines - LOG_LEVEL=debug shows all HTTP requests 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- src/app.js | 10 +++++++--- src/config/database.js | 7 +++++++ src/cron/index.js | 9 ++------- src/index.js | 24 +++++++++++++++++++----- src/utils/logger.js | 26 ++++++++++++++------------ 5 files changed, 49 insertions(+), 27 deletions(-) diff --git a/src/app.js b/src/app.js index 13cea49..30c91f2 100644 --- a/src/app.js +++ b/src/app.js @@ -30,9 +30,13 @@ import eventRoutes from './routes/event.routes.js'; const app = express(); -// HTTP request logging via Morgan -> custom logger -const morganStream = { write: (message) => logger.http(message.trim()) }; -app.use(morgan(':method :url :status :response-time ms', { stream: morganStream })); +// HTTP request logging - only errors by default (LOG_LEVEL=debug shows all) +app.use(morgan((tokens, req, res) => { + const status = parseInt(tokens.status(req, res)) || 0; + const message = `${tokens.method(req, res)} ${tokens.url(req, res)} ${status} ${tokens['response-time'](req, res)} ms`; + logger.http(message, status); + return null; // Don't write to stdout, logger handles it +})); app.use( helmet({ contentSecurityPolicy: { diff --git a/src/config/database.js b/src/config/database.js index fc2b497..b4078fd 100644 --- a/src/config/database.js +++ b/src/config/database.js @@ -24,5 +24,12 @@ pool.on('error', (err) => { // Initialize Drizzle ORM with schema export const db = drizzle(pool, { schema }); +// Test database connection +export const testConnection = async () => { + const client = await pool.connect(); + client.release(); + return true; +}; + // Export pool for direct access if needed export { pool }; diff --git a/src/cron/index.js b/src/cron/index.js index ba5e7c1..f632e34 100644 --- a/src/cron/index.js +++ b/src/cron/index.js @@ -6,18 +6,13 @@ import { startAuditCleanupCron, cleanupOldAuditLogs } from './cleanupAuditLogs.j * Start all cron jobs */ export const startAllCronJobs = () => { - const jobs = []; - // Calendar event notifications const calendarJob = startCalendarNotificationCron(); - jobs.push(calendarJob); + logger.info(`Cron: ${calendarJob.name}`); // Audit logs cleanup const auditJob = startAuditCleanupCron(); - jobs.push(auditJob); - - // Log summary - logger.info(`Cron jobs initialized: ${jobs.map(j => j.name).join(', ')}`); + logger.info(`Cron: ${auditJob.name}`); }; // Export individual functions for testing/manual triggers diff --git a/src/index.js b/src/index.js index b908e6b..9c25529 100644 --- a/src/index.js +++ b/src/index.js @@ -1,11 +1,25 @@ import app from './app.js'; import { startAllCronJobs } from './cron/index.js'; import { logger } from './utils/logger.js'; +import { testConnection } from './config/database.js'; const port = process.env.PORT || 5000; -app.listen(port, () => { - logger.info(`Server running on http://localhost:${port}`); - // Start cron jobs after server is running - startAllCronJobs(); -}); +const start = async () => { + try { + // Test database connection + await testConnection(); + logger.success('Database connected'); + + // Start server + app.listen(port, () => { + logger.info(`Server running on http://localhost:${port}`); + startAllCronJobs(); + }); + } catch (error) { + logger.error('Failed to start server', error); + process.exit(1); + } +}; + +start(); diff --git a/src/utils/logger.js b/src/utils/logger.js index 93dbc7f..ce4b493 100644 --- a/src/utils/logger.js +++ b/src/utils/logger.js @@ -1,13 +1,11 @@ /** * Logger utility with LOG_LEVEL filtering * - * LOG_LEVEL options (from lowest to highest): + * LOG_LEVEL options (minimum level to show): * - debug: Show all logs - * - info: Show info, success, warn, error, audit + * - info: Show info, success, warn, error, audit (default) * - warn: Show warn, error, audit * - error: Show only errors and audit - * - * Default: 'info' (production-ready default) */ const LOG_LEVELS = { @@ -32,13 +30,13 @@ const getTimestamp = () => { return new Date().toISOString(); }; -const getCurrentLevel = () => { +const getMinLevel = () => { const level = process.env.LOG_LEVEL?.toLowerCase() || 'info'; return LOG_LEVELS[level] ?? LOG_LEVELS.info; }; const shouldLog = (level) => { - return LOG_LEVELS[level] <= getCurrentLevel(); + return LOG_LEVELS[level] >= getMinLevel(); }; export const logger = { @@ -93,11 +91,15 @@ export const logger = { ); }, - // HTTP request logger (for Morgan integration) - http: (message) => { - if (!shouldLog('info')) return; - console.log( - `${colors.gray}[HTTP]${colors.reset} ${colors.gray}${getTimestamp()}${colors.reset} ${message}` - ); + // HTTP request logger - only logs errors (4xx, 5xx) by default + http: (message, statusCode) => { + // Only log HTTP errors, or all if LOG_LEVEL=debug + const isError = statusCode >= 400; + if (isError || getMinLevel() === LOG_LEVELS.debug) { + const color = isError ? colors.red : colors.gray; + console.log( + `${color}[HTTP]${colors.reset} ${colors.gray}${getTimestamp()}${colors.reset} ${message}` + ); + } }, };