mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 10:11:13 -05:00
429 lines
11 KiB
Markdown
429 lines
11 KiB
Markdown
# 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`)
|
|
|
|
```typescript
|
|
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
|
|
|
|
```typescript
|
|
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
|
|
|
|
```typescript
|
|
// 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)
|
|
|
|
```typescript
|
|
// 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`:
|
|
|
|
```javascript
|
|
"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
|
|
|
|
```bash
|
|
# 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()
|
|
|
|
```typescript
|
|
// 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
|
|
|
|
```typescript
|
|
// Before
|
|
console.log('[ModerationQueue] Fetching submissions');
|
|
|
|
// After
|
|
logger.info('Fetching submissions', { component: 'ModerationQueue' });
|
|
```
|
|
|
|
### 3. Replace console.debug with logger.debug
|
|
|
|
```typescript
|
|
// Before
|
|
console.log('[DEBUG] Auth state:', authState);
|
|
|
|
// After
|
|
logger.debug('Auth state', { authState });
|
|
```
|
|
|
|
### 4. Replace console.warn with logger.warn
|
|
|
|
```typescript
|
|
// Before
|
|
console.warn('localStorage error:', error);
|
|
|
|
// After
|
|
logger.warn('localStorage error', { error });
|
|
```
|
|
|
|
---
|
|
|
|
## Examples
|
|
|
|
### Good: Error Handling with Admin Logging
|
|
|
|
```typescript
|
|
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
|
|
|
|
```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); // ❌ 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:
|
|
|
|
```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
|
|
```
|
|
|
|
---
|
|
|
|
## Edge Function Logging
|
|
|
|
### Using `edgeLogger` in Edge Functions
|
|
|
|
Edge functions use the `edgeLogger` utility from `_shared/logger.ts`:
|
|
|
|
```typescript
|
|
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
|