Refactor: Implement logging and JSONB cleanup

This commit is contained in:
gpt-engineer-app[bot]
2025-11-03 18:05:58 +00:00
parent b6179372e6
commit e9b9faa3e1
18 changed files with 430 additions and 142 deletions

View File

@@ -21,21 +21,69 @@ Console statements in production code cause:
## The Solution
### ✅ Use the Structured Logger
### ✅ 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);
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.debug('Auth state changed', { state, userId });
```
### Logger Methods
### 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)
@@ -44,20 +92,22 @@ 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)
// 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 Logging
### Benefits of Structured Error Handling & 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
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
---
@@ -66,12 +116,12 @@ logger.debug(message: string, context?: Record<string, unknown>);
The `no-console` rule is enforced in `eslint.config.js`:
```javascript
"no-console": ["error", { allow: ["warn", "error"] }]
"no-console": "error" // Blocks ALL console statements
```
This rule will:
-**Block**: `console.log()`, `console.debug()`, `console.info()`
-**Allow**: `console.warn()`, `console.error()` (for critical edge cases only)
-**Block**: `console.log()`, `console.debug()`, `console.info()`, `console.warn()`, `console.error()`
-**Use instead**: `logger.*` for logging, `handleError()` for error handling
### Running Lint
@@ -87,7 +137,31 @@ npm run lint -- --fix
## Migration Guide
### 1. Replace Console.log with Logger.info
### 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
@@ -97,20 +171,7 @@ console.log('[ModerationQueue] Fetching submissions');
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
### 3. Replace console.debug with logger.debug
```typescript
// Before
@@ -120,26 +181,26 @@ console.log('[DEBUG] Auth state:', authState);
logger.debug('Auth state', { authState });
```
### 4. Use Toast for User-Facing Messages
### 4. Replace console.warn with logger.warn
```typescript
// Before
console.error('Failed to save changes');
console.warn('localStorage error:', error);
// After
logger.error('Failed to save changes', { userId, entityId });
toast.error('Failed to save changes', {
description: 'Please try again or contact support.'
});
logger.warn('localStorage error', { error });
```
---
## Examples
### Good: Structured Logging with Context
### 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,
@@ -153,17 +214,15 @@ const handleSubmit = async () => {
submissionId: result.id,
processingTime: Date.now() - startTime
});
toast.success('Submission created successfully');
} catch (error) {
logger.error('Submission failed', {
error: getErrorMessage(error),
entityType,
entityId,
userId
});
toast.error('Submission failed', {
description: 'Please try again.'
// 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
}
};
```
@@ -178,21 +237,38 @@ const handleSubmit = async () => {
const result = await submitData();
console.log('Success:', result); // ❌ Will fail ESLint
} catch (error) {
console.error(error); // ⚠️ Allowed but discouraged
console.error(error); // ❌ Will fail ESLint
toast.error('Failed'); // ❌ Not logged to admin panel
}
};
```
---
## When Console.warn/error is Acceptable
## When to Use What
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)
### 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
**In 99% of cases, use the structured logger instead.**
### 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
---
@@ -266,15 +342,28 @@ logger.error('Payment failed', {
## 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)**
**Use `handleError()` for all application errors** - Logs to Admin Panel
**Use `logger.*` for non-error logging** - Structured and filterable
**Provide rich context with every log** - Makes debugging easier
**Use appropriate log levels (debug/info/warn/error)** - Environment-aware
**Let ESLint catch violations early** - No console statements allowed
**Never log sensitive data (passwords, tokens, PII)** - Security critical
**Re-throw errors after handleError()** - Let parent error boundaries catch them
---
## Admin Panel Error Monitoring
All errors logged via `handleError()` are visible in the Admin Panel:
- **URL**: `/admin/error-monitoring`
- **Features**: Search by user, action, date, error type
- **Reference IDs**: Each error has a unique ID shown to users
- **Context**: Full metadata and breadcrumbs for debugging
---
**See Also:**
- `src/lib/errorHandler.ts` - Error handling utilities
- `src/lib/logger.ts` - Logger implementation
- `eslint.config.js` - Enforcement configuration
- `docs/PHASE_1_JSONB_COMPLETE.md` - Related improvements
- `docs/JSONB_ELIMINATION.md` - Related improvements