Files
thrilltrack-explorer/docs/LOGGING_POLICY.md
2025-11-03 20:04:11 +00:00

11 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 handleError() for Application Errors

CRITICAL: All application errors MUST be logged to the Admin Panel Error Log (/admin/error-monitoring)

import { handleError } from '@/lib/errorHandler';

// ❌ DON'T use console or raw toast for errors
try {
  await fetchData();
} catch (error) {
  console.error('Failed:', error); // ❌ No admin logging
  toast.error('Failed to load data'); // ❌ Not tracked
}

// ✅ DO use handleError() for application errors
try {
  await fetchData();
} catch (error) {
  handleError(error, {
    action: 'Load Data',
    userId: user?.id,
    metadata: { entityId, context: 'DataLoader' }
  });
  throw error; // Re-throw for parent error boundaries
}

Use the Structured Logger for Non-Error Logging

import { logger } from '@/lib/logger';

// ❌ DON'T use console
console.log('User logged in:', userId);

// ✅ DO use structured logger
logger.info('User logged in', { userId });
logger.debug('Auth state changed', { state, userId });

Error Handling Method

// Application errors (REQUIRED for errors that need admin visibility)
handleError(
  error: unknown,
  context: {
    action: string;           // What operation failed
    userId?: string;          // Who was affected
    metadata?: Record<string, unknown>; // Additional context
  }
): string // Returns error reference ID

What handleError() does:

  1. Logs error to request_metadata table (Admin Panel visibility)
  2. Shows user-friendly toast with reference ID
  3. Captures breadcrumbs and environment context
  4. Makes errors searchable in /admin/error-monitoring
  5. Returns error reference ID for tracking

Logger Methods (for non-error logging)

// Information (development only)
logger.info(message: string, context?: Record<string, unknown>);

// Warnings (development + production)
logger.warn(message: string, context?: Record<string, unknown>);

// Errors (development + production, but prefer handleError() for app errors)
logger.error(message: string, context?: Record<string, unknown>);

// Debug (very verbose, development only)
logger.debug(message: string, context?: Record<string, unknown>);

Benefits of Structured Error Handling & Logging

  1. Admin visibility: All errors logged to Admin Panel (/admin/error-monitoring)
  2. User-friendly: Shows toast with reference ID for support tickets
  3. Context preservation: Rich metadata for debugging
  4. Searchable: Filter by user, action, date, error type
  5. Trackable: Each error gets unique reference ID
  6. Automatic filtering: Development logs show everything, production shows warnings/errors
  7. Security: Prevents accidental PII exposure

ESLint Enforcement

The no-console rule is enforced in eslint.config.js:

"no-console": "error" // Blocks ALL console statements

This rule will:

  • Block: console.log(), console.debug(), console.info(), console.warn(), console.error()
  • Use instead: logger.* for logging, handleError() for error handling

Running Lint

# Check for violations
npm run lint

# Auto-fix where possible
npm run lint -- --fix

Migration Guide

1. Replace console.error in catch blocks with handleError()

// Before
try {
  await saveData();
} catch (error) {
  console.error('Save failed:', error);
  toast.error('Failed to save');
}

// After
try {
  await saveData();
} catch (error) {
  handleError(error, {
    action: 'Save Data',
    userId: user?.id,
    metadata: { entityId, entityType }
  });
  throw error; // Re-throw for parent components
}

2. Replace console.log with logger.info

// Before
console.log('[ModerationQueue] Fetching submissions');

// After
logger.info('Fetching submissions', { component: 'ModerationQueue' });

3. Replace console.debug with logger.debug

// Before
console.log('[DEBUG] Auth state:', authState);

// After
logger.debug('Auth state', { authState });

4. Replace console.warn with logger.warn

// Before
console.warn('localStorage error:', error);

// After
logger.warn('localStorage error', { error });

Examples

Good: Error Handling with Admin Logging

import { handleError } from '@/lib/errorHandler';
import { logger } from '@/lib/logger';

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 
    });
    toast.success('Submission created successfully');
  } catch (error) {
    // handleError logs to admin panel + shows toast
    const errorId = handleError(error, { 
      action: 'Submit Data',
      userId,
      metadata: { entityType, entityId }
    });
    throw error; // Re-throw for parent error boundaries
  }
};

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); // ❌ Will fail ESLint
    toast.error('Failed'); // ❌ Not logged to admin panel
  }
};

When to Use What

Use handleError() for:

  • Database errors (fetch, insert, update, delete)
  • API call failures
  • Form submission errors
  • Authentication errors
  • Any error that users should report to support
  • Any error that needs admin investigation

Use logger.* for:

  • Debug information (development only)
  • Performance tracking
  • Component lifecycle events
  • Non-error warnings (localStorage issues, etc.)

Use toast.* (without handleError) for:

  • Success messages
  • Info messages
  • User-facing validation errors (no admin logging needed)

NEVER use console.*:

  • All console statements are blocked by ESLint
  • Use handleError() or 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

Edge Function Logging

Using edgeLogger in Edge Functions

Edge functions use the edgeLogger utility from _shared/logger.ts:

import { edgeLogger, startRequest, endRequest } from "../_shared/logger.ts";

const handler = async (req: Request): Promise<Response> => {
  const tracking = startRequest('function-name');
  
  try {
    edgeLogger.info('Processing request', {
      requestId: tracking.requestId,
      // ... context
    });
    
    // ... your code
    
    const duration = endRequest(tracking);
    edgeLogger.info('Request completed', { requestId: tracking.requestId, duration });
  } catch (error) {
    const errorMessage = error instanceof Error ? error.message : String(error);
    const duration = endRequest(tracking);
    edgeLogger.error('Request failed', {
      error: errorMessage,
      requestId: tracking.requestId,
      duration
    });
  }
};

Logger Methods for Edge Functions

  • edgeLogger.info() - General information logging
  • edgeLogger.warn() - Warning conditions
  • edgeLogger.error() - Error conditions
  • edgeLogger.debug() - Detailed debugging (dev only)

All logs are visible in the Supabase Edge Function Logs dashboard.

CRITICAL: Never use console.* in edge functions. Always use edgeLogger.* instead.


Summary

Use handleError() for application errors → Logs to Admin Panel + user-friendly toast
Use logger.* for general logging (client-side) → Environment-aware console output
Use edgeLogger.* for edge function logging → Structured logs visible in Supabase dashboard
Never use console.* → Blocked by ESLint

This approach ensures:

  • Production builds are clean (no console noise)
  • All errors are tracked and actionable in Admin Panel
  • Users get helpful error messages with reference IDs
  • Development remains productive with detailed logs
  • Edge functions have structured, searchable logs

Admin Panel Error Monitoring

All errors logged via handleError() are visible in the Admin Panel at:

Path: /admin/error-monitoring

Features:

  • Search and filter errors by action, user, date range
  • View error context (metadata, breadcrumbs, environment)
  • Track error frequency and patterns
  • One-click copy of error details for debugging

Access: Admin role required


Updated: 2025-11-03
Status: Enforced via ESLint (Frontend + Edge Functions)


See Also:

  • src/lib/errorHandler.ts - Error handling utilities
  • src/lib/logger.ts - Logger implementation
  • eslint.config.js - Enforcement configuration
  • docs/JSONB_ELIMINATION.md - Related improvements