# 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 ```typescript 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 ```typescript // Information (development only) logger.info(message: string, context?: Record); // Warnings (development + production) logger.warn(message: string, context?: Record); // Errors (always logged, sent to monitoring in production) logger.error(message: string, context?: Record); // Debug (very verbose, development only) logger.debug(message: string, context?: Record); ``` ### Benefits of Structured Logging 1. **Automatic filtering**: Production logs only show errors/warnings 2. **Context preservation**: Rich metadata for debugging 3. **Searchable**: Can filter by userId, action, context, etc. 4. **Integration ready**: Works with Sentry, LogRocket, etc. 5. **Security**: Prevents accidental PII exposure --- ## ESLint Enforcement The `no-console` rule is enforced in `eslint.config.js`: ```javascript "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 ```bash # Check for violations npm run lint # Auto-fix where possible npm run lint -- --fix ``` --- ## Migration Guide ### 1. Replace Console.log with Logger.info ```typescript // Before console.log('[ModerationQueue] Fetching submissions'); // After logger.info('Fetching submissions', { component: 'ModerationQueue' }); ``` ### 2. Replace Console.error with Logger.error ```typescript // 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 ```typescript // Before console.log('[DEBUG] Auth state:', authState); // After logger.debug('Auth state', { authState }); ``` ### 4. Use Toast for User-Facing Messages ```typescript // 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 ```typescript 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 ```typescript 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: 1. **Third-party library issues**: Debugging external library behavior 2. **Build/bundler errors**: Issues during development build process 3. **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: ```typescript // 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 ```typescript 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 ```typescript // 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 implementation - `eslint.config.js` - Enforcement configuration - `docs/PHASE_1_JSONB_COMPLETE.md` - Related improvements