# ADR-031: Granular Debug Logging Strategy **Date**: 2026-02-10 **Status**: Accepted **Source**: Imported from flyer-crawler project (ADR-052) **Related**: [ADR-027](ADR-027-application-wide-structured-logging.md), [ADR-017](ADR-017-structured-logging-with-pino.md) ## Context Global log levels (INFO vs DEBUG) are too coarse. Developers need to inspect detailed debug information for specific subsystems (e.g., `ai-service`, `db-pool`, `auth-service`) without being flooded by logs from the entire application. When debugging a specific feature: - Setting `LOG_LEVEL=debug` globally produces too much noise - Manually adding/removing debug statements is error-prone - No standard way to enable targeted debugging in production ## Decision We will adopt a namespace-based debug filter pattern, similar to the `debug` npm package, but integrated into our Pino logger. 1. **Logger Namespaces**: Every service/module logger must be initialized with a `module` property (e.g., `logger.child({ module: 'ai-service' })`). 2. **Environment Filter**: We will support a `DEBUG_MODULES` environment variable that overrides the log level for matching modules. ## Implementation ### Core Implementation Implemented in `src/services/logger.server.ts`: ```typescript import pino from 'pino'; // Parse DEBUG_MODULES from environment const debugModules = (process.env.DEBUG_MODULES || '').split(',').map((s) => s.trim()); // Base logger configuration export const logger = pino({ level: process.env.LOG_LEVEL || (process.env.NODE_ENV === 'production' ? 'info' : 'debug'), // ... other configuration }); /** * Creates a scoped logger for a specific module. * If DEBUG_MODULES includes this module or '*', debug level is enabled. */ export const createScopedLogger = (moduleName: string) => { // If DEBUG_MODULES contains the module name or "*", force level to 'debug' const isDebugEnabled = debugModules.includes('*') || debugModules.includes(moduleName); return logger.child({ module: moduleName, level: isDebugEnabled ? 'debug' : logger.level, }); }; ``` ### Service Usage Examples ```typescript // src/services/aiService.server.ts import { createScopedLogger } from './logger.server'; const logger = createScopedLogger('ai-service'); export async function processWithAI(data: unknown) { logger.debug({ data }, 'Starting AI processing'); // ... implementation logger.info({ result }, 'AI processing completed'); } ``` ```typescript // src/services/authService.server.ts import { createScopedLogger } from './logger.server'; const logger = createScopedLogger('auth-service'); export async function validateToken(token: string) { logger.debug({ tokenLength: token.length }, 'Validating token'); // ... implementation } ``` ### Module Naming Convention Use kebab-case suffixed with `-service` or `-worker`: | Module Name | Purpose | File | | --------------- | -------------------------------- | ------------------------------------- | | `ai-service` | AI/external API interactions | `src/services/aiService.server.ts` | | `auth-service` | Authentication and authorization | `src/services/authService.server.ts` | | `db-pool` | Database connection pooling | `src/services/database.server.ts` | | `cache-service` | Redis/caching operations | `src/services/cacheService.server.ts` | | `queue-worker` | Background job processing | `src/workers/queueWorker.ts` | | `email-service` | Email sending | `src/services/emailService.server.ts` | ## Usage ### Enable Debug Logging for Specific Modules To debug only AI and authentication: ```bash DEBUG_MODULES=ai-service,auth-service npm run dev ``` ### Enable All Debug Logging Use wildcard to enable debug logging for all modules: ```bash DEBUG_MODULES=* npm run dev ``` ### Development Environment In `.env.development`: ```bash # Enable debug logging for specific modules during development DEBUG_MODULES=ai-service ``` ### Production Troubleshooting Temporarily enable debug logging for a specific subsystem: ```bash # SSH into production server ssh root@example.com # Set environment variable and restart DEBUG_MODULES=ai-service pm2 restart app-api # View logs pm2 logs app-api --lines 100 # Disable debug logging pm2 unset DEBUG_MODULES app-api pm2 restart app-api ``` ### With PM2 Configuration In `ecosystem.config.js`: ```javascript module.exports = { apps: [ { name: 'app-api', script: 'dist/server.js', env: { NODE_ENV: 'production', // DEBUG_MODULES is unset by default }, env_debug: { NODE_ENV: 'production', DEBUG_MODULES: 'ai-service,auth-service', }, }, ], }; ``` Start with debug logging: ```bash pm2 start ecosystem.config.js --env debug ``` ## Best Practices ### 1. Use Scoped Loggers for Long-Running Services Services with complex workflows or external API calls should use `createScopedLogger` to allow targeted debugging: ```typescript const logger = createScopedLogger('payment-service'); export async function processPayment(payment: Payment) { logger.debug({ paymentId: payment.id }, 'Starting payment processing'); try { const result = await externalPaymentAPI.process(payment); logger.debug({ result }, 'External API response'); return result; } catch (error) { logger.error({ error, paymentId: payment.id }, 'Payment processing failed'); throw error; } } ``` ### 2. Use Child Loggers for Contextual Data Even within scoped loggers, create child loggers with job/request-specific context: ```typescript const logger = createScopedLogger('queue-worker'); async function processJob(job: Job) { const jobLogger = logger.child({ jobId: job.id, jobName: job.name }); jobLogger.debug('Starting job processing'); // ... processing jobLogger.info('Job completed successfully'); } ``` ### 3. Consistent Debug Message Patterns Use consistent patterns for debug messages: ```typescript // Function entry logger.debug({ params: sanitizedParams }, 'Function entry: processOrder'); // External API calls logger.debug({ url, method }, 'External API request'); logger.debug({ statusCode, duration }, 'External API response'); // State changes logger.debug({ before, after }, 'State transition'); // Decision points logger.debug({ condition, result }, 'Branch decision'); ``` ### 4. Production Usage Guidelines - `DEBUG_MODULES` can be set in production for temporary debugging - Should not be used continuously due to increased log volume - Always unset after troubleshooting is complete - Monitor log storage when debug logging is enabled ## Consequences ### Positive - Developers can inspect detailed logs for specific subsystems without log flooding - Production debugging becomes more targeted and efficient - No performance impact when debug logging is disabled - Compatible with existing Pino logging infrastructure - Follows familiar pattern from `debug` npm package ### Negative - Requires developers to know module names (mitigated by documentation) - Not all services have adopted scoped loggers yet (gradual migration) - Additional configuration complexity ## References - [ADR-027: Application-Wide Structured Logging](ADR-027-application-wide-structured-logging.md) - [ADR-017: Structured Logging with Pino](ADR-017-structured-logging-with-pino.md) - [debug npm package](https://www.npmjs.com/package/debug) - Inspiration for namespace pattern - [Pino Child Loggers](https://getpino.io/#/docs/child-loggers)