mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 12:31:26 -05:00
6.3 KiB
6.3 KiB
Logging Policy
✅ Console Statement Prevention (P0 #2)
Status: Enforced via ESLint
Severity: Critical - Security & Information Leakage
The Problem
Console statements in production code cause:
- Information leakage: Sensitive data exposed in browser console
- Performance overhead: Console operations are expensive
- Unprofessional UX: Users see debug output
- No structured logging: Can't filter, search, or analyze logs effectively
128 console statements were found during the security audit.
The Solution
✅ Use the Structured Logger
import { logger } from '@/lib/logger';
// ❌ DON'T use console
console.log('User logged in:', userId);
console.error('Failed to load data:', error);
// ✅ DO use structured logger
logger.info('User logged in', { userId });
logger.error('Failed to load data', { error, context: 'DataLoader' });
Logger Methods
// Information (development only)
logger.info(message: string, context?: Record<string, unknown>);
// Warnings (development + production)
logger.warn(message: string, context?: Record<string, unknown>);
// Errors (always logged, sent to monitoring in production)
logger.error(message: string, context?: Record<string, unknown>);
// Debug (very verbose, development only)
logger.debug(message: string, context?: Record<string, unknown>);
Benefits of Structured Logging
- Automatic filtering: Production logs only show errors/warnings
- Context preservation: Rich metadata for debugging
- Searchable: Can filter by userId, action, context, etc.
- Integration ready: Works with Sentry, LogRocket, etc.
- Security: Prevents accidental PII exposure
ESLint Enforcement
The no-console rule is enforced in eslint.config.js:
"no-console": ["error", { allow: ["warn", "error"] }]
This rule will:
- ❌ Block:
console.log(),console.debug(),console.info() - ✅ Allow:
console.warn(),console.error()(for critical edge cases only)
Running Lint
# Check for violations
npm run lint
# Auto-fix where possible
npm run lint -- --fix
Migration Guide
1. Replace Console.log with Logger.info
// Before
console.log('[ModerationQueue] Fetching submissions');
// After
logger.info('Fetching submissions', { component: 'ModerationQueue' });
2. Replace Console.error with Logger.error
// Before
console.error('Upload failed:', error);
// After
logger.error('Upload failed', {
error: error instanceof Error ? error.message : String(error),
stack: error instanceof Error ? error.stack : undefined
});
3. Replace Debug Logs with Logger.debug
// Before
console.log('[DEBUG] Auth state:', authState);
// After
logger.debug('Auth state', { authState });
4. Use Toast for User-Facing Messages
// Before
console.error('Failed to save changes');
// After
logger.error('Failed to save changes', { userId, entityId });
toast.error('Failed to save changes', {
description: 'Please try again or contact support.'
});
Examples
Good: Structured Logging with Context
const handleSubmit = async () => {
logger.info('Starting submission', {
entityType,
entityId,
userId
});
try {
const result = await submitData();
logger.info('Submission successful', {
submissionId: result.id,
processingTime: Date.now() - startTime
});
} catch (error) {
logger.error('Submission failed', {
error: getErrorMessage(error),
entityType,
entityId,
userId
});
toast.error('Submission failed', {
description: 'Please try again.'
});
}
};
Bad: Console Logging
const handleSubmit = async () => {
console.log('Submitting...'); // ❌ Will fail ESLint
try {
const result = await submitData();
console.log('Success:', result); // ❌ Will fail ESLint
} catch (error) {
console.error(error); // ⚠️ Allowed but discouraged
}
};
When Console.warn/error is Acceptable
Only in these rare cases:
- Third-party library issues: Debugging external library behavior
- Build/bundler errors: Issues during development build process
- Critical failures: Logger itself has failed (extremely rare)
In 99% of cases, use the structured logger instead.
Environment-Aware Logging
The logger automatically adjusts based on environment:
// Development: All logs shown
logger.debug('Verbose details'); // ✅ Visible
logger.info('Operation started'); // ✅ Visible
logger.warn('Potential issue'); // ✅ Visible
logger.error('Critical error'); // ✅ Visible
// Production: Only warnings and errors
logger.debug('Verbose details'); // ❌ Hidden
logger.info('Operation started'); // ❌ Hidden
logger.warn('Potential issue'); // ✅ Visible
logger.error('Critical error'); // ✅ Visible + Sent to monitoring
Testing with Logger
import { logger } from '@/lib/logger';
// Mock logger in tests
jest.mock('@/lib/logger', () => ({
logger: {
info: jest.fn(),
warn: jest.fn(),
error: jest.fn(),
debug: jest.fn(),
}
}));
test('logs error on failure', async () => {
await failingOperation();
expect(logger.error).toHaveBeenCalledWith(
'Operation failed',
expect.objectContaining({ error: expect.any(String) })
);
});
Monitoring Integration (Future)
The logger is designed to integrate with:
- Sentry: Automatic error tracking
- LogRocket: Session replay with logs
- Datadog: Log aggregation and analysis
- Custom dashboards: Structured JSON logs
// Future: Logs will automatically flow to monitoring services
logger.error('Payment failed', {
userId,
amount,
paymentProvider
});
// → Automatically sent to Sentry with full context
// → Triggers alert if error rate exceeds threshold
Summary
✅ Always use logger.* instead of console.*
✅ Provide rich context with every log
✅ Use appropriate log levels (debug/info/warn/error)
✅ Let ESLint catch violations early
❌ Never log sensitive data (passwords, tokens, PII)
See Also:
src/lib/logger.ts- Logger implementationeslint.config.js- Enforcement configurationdocs/PHASE_1_JSONB_COMPLETE.md- Related improvements