All files / src/utils consoleLogger.js

100% Statements 16/16
75% Branches 3/4
100% Functions 4/4
100% Lines 14/14

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69                              1x   1x               1x             6x 15x   4x       1x             1x 4x   6x     6x 7x   6x                       1x  
/**
 * Console override that funnels console.log/warn/error/info into
 * the structured logger while preserving stdout output.
 *
 * Must be called once during app startup, AFTER loggerInstance is created.
 *
 * Behaviour:
 *   - Keeps original console output (developers still see logs in PM2/terminal)
 *   - Also writes each console call as a structured log entry to Firestore
 *   - Source is detected from the message prefix (e.g. "[CRON]" → "cron")
 *   - Falls back to source "express-api"
 */
 
/* eslint-disable no-console */
 
const logger = require('./loggerInstance');
 
const LEVEL_MAP = {
  log: 'INFO',
  info: 'INFO',
  warn: 'WARN',
  error: 'ERROR',
};
 
// Detect source from common prefix patterns
const SOURCE_PATTERNS = [
  { regex: /^\[CRON\]/i, source: 'cron' },
  { regex: /^\[AUTO-BAN\]/i, source: 'admin' },
  { regex: /^(GET|POST|PUT|PATCH|DELETE) \//, source: 'http' },
];
 
function detectSource(message) {
  for (const { regex, source } of SOURCE_PATTERNS) {
    if (regex.test(message)) return source;
  }
  return 'express-api';
}
 
function patchConsole() {
  const originalConsole = {
    log: console.log.bind(console),
    info: console.info.bind(console),
    warn: console.warn.bind(console),
    error: console.error.bind(console),
  };
 
  for (const [method, level] of Object.entries(LEVEL_MAP)) {
    console[method] = (...args) => {
      // Always keep original console output
      originalConsole[method](...args);
 
      // Also send to structured logger
      try {
        const message = args.map((a) => (typeof a === 'string' ? a : JSON.stringify(a))).join(' ');
 
        logger.log({
          level,
          source: detectSource(message),
          message: message.slice(0, 2000), // cap length
        });
      } catch {
        // Intentionally swallowed — console patching must never throw to avoid breaking application output
      }
    };
  }
}
 
module.exports = { patchConsole };