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 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 12x 12x 12x 12x 12x 2x 10x 1x 2x 2x 9x 2x 1x 1x 12x 12x 2x 2x 10x 10x 10x 6x 4x 2x 6x 6x 2x 1x 1x 12x 1x 1x 1x 1x 1x 2x 2x 2x 2x 2x 2x 12x 12x 2x 2x 12x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 1x 1x 1x 1x 10x 4x 1x 1x 4x 1x 2x 2x 2x 2x 4x 2x 2x 2x 2x 2x 2x 2x 6x 2x 2x 2x 1x 12x 1x 14x 1x 12x 5x 5x | // src/services/logger.server.ts /** * SERVER-SIDE LOGGER * This file configures and exports a singleton `pino` logger instance for * server-side use, adhering to ADR-004 for structured JSON logging. * * In production/test environments, logs are written to: * - stdout (for PM2 capture and real-time viewing) * - File: logs/app.log (for Logstash aggregation) * * Log files are stored in the application's logs/ directory: * - Production: /var/www/flyer-crawler.projectium.com/logs/ * - Test: /var/www/flyer-crawler-test.projectium.com/logs/ * - Dev container: /app/logs/ */ import pino from 'pino'; import fs from 'fs'; import path from 'path'; const isProduction = process.env.NODE_ENV === 'production'; const isTest = process.env.NODE_ENV === 'test'; const isStaging = process.env.NODE_ENV === 'staging'; const isDevelopment = !isProduction && !isTest && !isStaging; // Determine log directory based on environment // In production/test, use the application directory's logs folder // In development, use process.cwd()/logs const getLogDirectory = (): string => { // Allow override via environment variable if (process.env.LOG_DIR) { return process.env.LOG_DIR; } // Default to logs/ in current working directory return path.join(process.cwd(), 'logs'); }; // Ensure log directory exists (only in production/test where we write files) const ensureLogDirectory = (): string | null => { if (isDevelopment) { return null; // Don't create log files in development } const logDir = getLogDirectory(); try { if (!fs.existsSync(logDir)) { fs.mkdirSync(logDir, { recursive: true }); } return logDir; } catch (error) { // If we can't create the directory, fall back to stdout only console.error(`Failed to create log directory ${logDir}:`, error); return null; } }; // Common redaction configuration const redactConfig = { paths: [ 'req.headers.authorization', 'req.headers.cookie', '*.body.password', '*.body.newPassword', '*.body.currentPassword', '*.body.confirmPassword', '*.body.refreshToken', '*.body.token', ], censor: '[REDACTED]', }; // Create the logger based on environment const createLogger = (): pino.Logger => { const logDir = ensureLogDirectory(); // Development: Use pino-pretty for human-readable output if (isDevelopment) { return pino({ level: 'debug', transport: { target: 'pino-pretty', options: { colorize: true, translateTime: 'SYS:standard', ignore: 'pid,hostname', }, }, redact: redactConfig, }); } // Production/Test: Write to both stdout and file if (logDir) { const logFilePath = path.join(logDir, 'app.log'); // Create a multi-stream destination const streams: pino.StreamEntry[] = [ // Stream to stdout (for PM2 and real-time viewing) { stream: process.stdout }, // Stream to file (for Logstash aggregation) { stream: pino.destination({ dest: logFilePath, sync: false, // Async for better performance mkdir: true, // Create directory if needed }), }, ]; return pino( { level: isProduction ? 'info' : 'debug', redact: redactConfig, }, pino.multistream(streams), ); } // Fallback: stdout only (if log directory creation failed) return pino({ level: isProduction ? 'info' : 'debug', redact: redactConfig, }); }; export const logger = createLogger(); const debugModules = (process.env.DEBUG_MODULES || '').split(',').map((s) => s.trim()); export const createScopedLogger = (moduleName: string) => { // If DEBUG_MODULES contains "ai-service" or "*", force level to 'debug' const isDebugEnabled = debugModules.includes('*') || debugModules.includes(moduleName); return logger.child({ module: moduleName, level: isDebugEnabled ? 'debug' : logger.level, }); }; |