All checks were successful
Deploy to Test Environment / deploy-to-test (push) Successful in 49m39s
263 lines
7.4 KiB
Markdown
263 lines
7.4 KiB
Markdown
# 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)
|