TL;DR
Logs are gold for debugging but dangerous if they contain secrets. Never log passwords, tokens, full credit cards, or PII. Redact sensitive fields automatically, use structured logging, and secure log storage. These prompts help you log safely.
Logging Audit
Audit my codebase for insecure logging practices.
Search for logging that might contain sensitive data:
- Request body logging: console.log(req.body) // May contain passwords logger.info({ body: req.body })
- User data logging: console.log(user) // May contain PII console.log('User:', JSON.stringify(user))
- Token/credential logging:
console.log('Token:', token)
console.log('Auth header:', req.headers.authorization)
console.log(
API Key: ${apiKey}) - Error logging with context: console.error('Failed:', error, requestData) // requestData may have secrets
- Database query logging: console.log('Query:', query) // May show credentials in connection string
Check for:
- console.log with raw request/response objects
- Logging entire user objects
- Logging headers (contains auth tokens)
- Logging form data (contains passwords)
- Verbose debug logging in production
Sensitive Data Redaction
Create a utility to redact sensitive data from logs.
const sensitiveFields = 'password', 'passwd', 'secret', 'token', 'apiKey', 'api_key', 'authorization', 'auth', 'credential', 'ssn', 'creditCard', 'cardNumber', 'cvv', 'pin' ;
function redact(obj, depth = 0) { if (depth > 10) return 'MAX_DEPTH'; if (obj === null || obj === undefined) return obj; if (typeof obj !== 'object') return obj;
if (Array.isArray(obj)) { return obj.map(item => redact(item, depth + 1)); }
const redacted = {}; for (const key, value of Object.entries(obj)) { const lowerKey = key.toLowerCase();
if (sensitiveFields.some(f => lowerKey.includes(f))) {
redacted[key] = '[REDACTED]';
} else if (typeof value === 'object') {
redacted[key] = redact(value, depth + 1);
} else {
redacted[key] = value;
}
} return redacted; }
// Usage logger.info('Request:', redact(req.body)); // { email: "user@example.com", password: "REDACTED" }
// Redact specific patterns function redactPatterns(str) { return str .replace(/Bearer\s+\S+/gi, 'Bearer REDACTED') .replace(/\b\d{4}-?\d{4}-?\d{4}-?\d{4}\b/g, 'CARD_REDACTED') .replace(/\bA-Za-z0-9._%+-+@A-Za-z0-9.-+.A-Z|a-z{2,}\b/g, 'EMAIL_REDACTED'); }
Logs are often less protected than databases: They get shipped to third-party services, stored in plain text, and accessed by more people. Treat logs as public and redact accordingly.
Structured Logging
Set up structured logging with automatic redaction.
Using pino (fast JSON logger):
npm install pino pino-pretty
const pino = require('pino');
const logger = pino({ level: process.env.LOG_LEVEL || 'info',
// Redact sensitive paths automatically redact: { paths: 'req.headers.authorization', 'req.headers.cookie', 'req.body.password', 'req.body.token', '*.password', '*.secret', '*.apiKey' , censor: 'REDACTED' },
// Pretty print in development only transport: process.env.NODE_ENV !== 'production' ? { target: 'pino-pretty' } : undefined,
// Base fields for all logs base: { env: process.env.NODE_ENV, version: process.env.npm_package_version } });
// Usage with context logger.info({ userId: user.id, action: 'login' }, 'User logged in'); logger.error({ err, requestId }, 'Request failed');
// Request logging middleware app.use((req, res, next) => { req.log = logger.child({ requestId: crypto.randomUUID() }); req.log.info({ method: req.method, url: req.url }, 'Request received'); next(); });
Log Levels and Retention
Set up appropriate log levels and retention.
Log levels (use appropriately):
- fatal: System crash, immediate action needed
- error: Operation failed, needs investigation
- warn: Potential problem, monitoring needed
- info: Normal operations (requests, completions)
- debug: Detailed debugging info
- trace: Very verbose, rarely used
Production configuration: LOG_LEVEL=info # Don't log debug/trace in production
// Never log these at any level: // - Passwords, tokens, API keys // - Full credit card numbers // - Social security numbers // - Full session tokens
// OK to log: // - User IDs (not emails in some contexts) // - Request IDs // - Timestamps // - Action types // - Sanitized error messages // - Performance metrics
Retention policies:
- Production logs: 30-90 days
- Security logs: 1 year (compliance)
- Debug logs: 7 days max
Storage security:
- Encrypt logs at rest
- Restrict access (need-to-know)
- Audit log access
- Don't expose log endpoints publicly
Pro tip: Set up log alerts for security events: failed logins, permission denied errors, unusual patterns. Logs aren't just for debugging - they're your security audit trail.
Should I log user emails?
Depends on your privacy requirements. For GDPR compliance, consider logging user IDs instead of emails. If you must log emails, ensure logs are treated as PII and have appropriate retention policies.
How do I debug production issues without verbose logs?
Use request IDs to correlate logs, structured logging for searchability, and error tracking services. You can also temporarily enable debug logging for specific users or requests without enabling it globally.