fix: Improve logging - fix LOG_LEVEL filter, reduce HTTP noise

- 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 <noreply@anthropic.com>
This commit is contained in:
richardtekula
2025-12-17 09:54:07 +01:00
parent 095a3a5b03
commit 3cd2531f6b
5 changed files with 49 additions and 27 deletions

View File

@@ -30,9 +30,13 @@ import eventRoutes from './routes/event.routes.js';
const app = express(); const app = express();
// HTTP request logging via Morgan -> custom logger // HTTP request logging - only errors by default (LOG_LEVEL=debug shows all)
const morganStream = { write: (message) => logger.http(message.trim()) }; app.use(morgan((tokens, req, res) => {
app.use(morgan(':method :url :status :response-time ms', { stream: morganStream })); 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( app.use(
helmet({ helmet({
contentSecurityPolicy: { contentSecurityPolicy: {

View File

@@ -24,5 +24,12 @@ pool.on('error', (err) => {
// Initialize Drizzle ORM with schema // Initialize Drizzle ORM with schema
export const db = drizzle(pool, { 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 for direct access if needed
export { pool }; export { pool };

View File

@@ -6,18 +6,13 @@ import { startAuditCleanupCron, cleanupOldAuditLogs } from './cleanupAuditLogs.j
* Start all cron jobs * Start all cron jobs
*/ */
export const startAllCronJobs = () => { export const startAllCronJobs = () => {
const jobs = [];
// Calendar event notifications // Calendar event notifications
const calendarJob = startCalendarNotificationCron(); const calendarJob = startCalendarNotificationCron();
jobs.push(calendarJob); logger.info(`Cron: ${calendarJob.name}`);
// Audit logs cleanup // Audit logs cleanup
const auditJob = startAuditCleanupCron(); const auditJob = startAuditCleanupCron();
jobs.push(auditJob); logger.info(`Cron: ${auditJob.name}`);
// Log summary
logger.info(`Cron jobs initialized: ${jobs.map(j => j.name).join(', ')}`);
}; };
// Export individual functions for testing/manual triggers // Export individual functions for testing/manual triggers

View File

@@ -1,11 +1,25 @@
import app from './app.js'; import app from './app.js';
import { startAllCronJobs } from './cron/index.js'; import { startAllCronJobs } from './cron/index.js';
import { logger } from './utils/logger.js'; import { logger } from './utils/logger.js';
import { testConnection } from './config/database.js';
const port = process.env.PORT || 5000; const port = process.env.PORT || 5000;
app.listen(port, () => {
logger.info(`Server running on http://localhost:${port}`);
// Start cron jobs after server is running 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(); startAllCronJobs();
}); });
} catch (error) {
logger.error('Failed to start server', error);
process.exit(1);
}
};
start();

View File

@@ -1,13 +1,11 @@
/** /**
* Logger utility with LOG_LEVEL filtering * Logger utility with LOG_LEVEL filtering
* *
* LOG_LEVEL options (from lowest to highest): * LOG_LEVEL options (minimum level to show):
* - debug: Show all logs * - debug: Show all logs
* - info: Show info, success, warn, error, audit * - info: Show info, success, warn, error, audit (default)
* - warn: Show warn, error, audit * - warn: Show warn, error, audit
* - error: Show only errors and audit * - error: Show only errors and audit
*
* Default: 'info' (production-ready default)
*/ */
const LOG_LEVELS = { const LOG_LEVELS = {
@@ -32,13 +30,13 @@ const getTimestamp = () => {
return new Date().toISOString(); return new Date().toISOString();
}; };
const getCurrentLevel = () => { const getMinLevel = () => {
const level = process.env.LOG_LEVEL?.toLowerCase() || 'info'; const level = process.env.LOG_LEVEL?.toLowerCase() || 'info';
return LOG_LEVELS[level] ?? LOG_LEVELS.info; return LOG_LEVELS[level] ?? LOG_LEVELS.info;
}; };
const shouldLog = (level) => { const shouldLog = (level) => {
return LOG_LEVELS[level] <= getCurrentLevel(); return LOG_LEVELS[level] >= getMinLevel();
}; };
export const logger = { export const logger = {
@@ -93,11 +91,15 @@ export const logger = {
); );
}, },
// HTTP request logger (for Morgan integration) // HTTP request logger - only logs errors (4xx, 5xx) by default
http: (message) => { http: (message, statusCode) => {
if (!shouldLog('info')) return; // 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( console.log(
`${colors.gray}[HTTP]${colors.reset} ${colors.gray}${getTimestamp()}${colors.reset} ${message}` `${color}[HTTP]${colors.reset} ${colors.gray}${getTimestamp()}${colors.reset} ${message}`
); );
}
}, },
}; };