wulf-pulse/dev/ERROR_HANDLING_GUIDE.md
root 6eee14f8af Add comprehensive admin features and multi-system integration
- Add admin dashboard with sync controls and data browser
- Implement RMM, Auvik, and Addigy organization mappings
- Add chunked ticket sync with progress tracking
- Implement entity sync service with rate limiting
- Add analytics engine and performance optimizer
- Create data browser for all PSA entities
- Add navigation components and UI improvements
- Implement background processing and sync services
- Add comprehensive documentation and migration scripts
- Update configuration items with multi-system support
- Enhance contact management and purchase history
- Add issue type assignment and LLM analyzer
- Improve error handling and logging utilities
2025-11-19 14:18:16 -05:00

293 lines
7.6 KiB
Markdown

# Error Handling and Logging Guide
## Overview
This document describes the error handling and logging implementation for the PostgreSQL Autotask Sync system.
## Error Types
All sync errors extend from the base `SyncError` class defined in `lib/types/errors.ts`:
### Error Hierarchy
```
SyncError (base)
├── NetworkError (retryable)
├── AuthError (not retryable)
├── RateLimitError (retryable)
├── ApiError (retryable for 5xx)
├── DatabaseError (configurable)
│ └── ConstraintError (not retryable)
├── ValidationError (not retryable)
├── MappingError (not retryable)
├── ConfigError (not retryable)
└── TimeoutError (retryable)
```
### Error Properties
Each error includes:
- `message`: Human-readable error description
- `code`: Machine-readable error code (e.g., 'NETWORK_ERROR')
- `context`: Additional context data (entity, operation, etc.)
- `isRetryable`: Boolean indicating if operation can be retried
- `stack`: Stack trace for debugging
## Error Categorization
The `categorizeError()` function automatically categorizes generic errors:
```typescript
import { categorizeError, isRetryableError } from '@/lib/types/errors';
try {
// ... operation
} catch (error) {
const categorized = categorizeError(error);
console.log(`Error type: ${categorized.code}`);
console.log(`Retryable: ${categorized.isRetryable}`);
}
```
## Logging
### Logger Utility
The `Logger` class in `lib/utils/logger.ts` provides structured logging:
```typescript
import { createLogger } from '@/lib/utils/logger';
const logger = createLogger({ syncId: '123', entity: 'companies' });
logger.info('Starting sync');
logger.warn('Rate limit approaching', { remaining: 10 });
logger.error('Sync failed', error, { recordCount: 100 });
```
### Log Levels
- `DEBUG`: Detailed diagnostic information
- `INFO`: General informational messages
- `WARN`: Warning messages for non-critical issues
- `ERROR`: Error messages for failures
### Log Format
```
[2025-11-01T12:00:00.000Z] [INFO] [syncId=abc123, entity=companies] Starting sync
```
## Error Handling in Sync Operations
### Sync Service (`lib/services/sync-service.ts`)
The sync service implements comprehensive error handling:
1. **Entity-level error handling**: Each entity sync is wrapped in try-catch
2. **Error categorization**: Errors are categorized for better diagnostics
3. **Sync history updates**: Failed syncs are recorded in sync_history
4. **Detailed logging**: Errors include context (entity, sync ID, type)
5. **Graceful degradation**: One entity failure doesn't stop the entire sync
Example error log output:
```
✗ Failed to sync Companies:
Error: Autotask API error: Connection timeout
Stack: <stack trace>
Entity: companies
Sync Type: full
Sync ID: sync_20251101_120000_abc123
[NETWORK_ERROR] Autotask API error: Connection timeout
```
### Entity Sync Service (`lib/services/entity-sync.ts`)
The entity sync service provides granular error handling:
1. **Operation-level try-catch**: Each step (fetch, map, upsert) is protected
2. **Contextual logging**: All logs prefixed with `[entity]`
3. **Error wrapping**: Generic errors wrapped with context
4. **Soft delete tolerance**: Soft delete failures don't fail entire sync
Example log output:
```
[companies] Starting sync (full)
[companies] Fetching records from Autotask API...
[companies] Fetched 150 records from Autotask
[companies] Mapping 150 records to database schema...
[companies] Successfully mapped 150 records
[companies] Upserting records to PostgreSQL...
[companies] Upserted 150 records to PostgreSQL
[companies] Checking for records to soft delete...
[companies] Soft deleted 5 missing records
[companies] Sync completed in 2543ms
```
## Error Recovery Strategies
### Retryable Errors
For retryable errors (network, rate limit, 5xx API errors):
1. Error is logged with `isRetryable: true`
2. Sync history records the error
3. User/system can retry the operation
4. Rate limiter handles 429 responses automatically
### Non-Retryable Errors
For non-retryable errors (auth, validation, constraints):
1. Error is logged with detailed context
2. Sync history records the failure
3. User must fix the underlying issue before retrying
### Partial Sync Failures
When some entities succeed and others fail:
1. Successful entities are committed to database
2. Failed entities are logged with errors
3. Sync result includes both successes and failures
4. User can retry only failed entities
## Sync History
All sync operations are recorded in the `sync_history` table:
```sql
SELECT
entity_type,
sync_type,
status,
records_added,
records_updated,
records_deleted,
error_message,
started_at,
completed_at
FROM sync_history
WHERE status = 'failed'
ORDER BY started_at DESC;
```
Error messages in sync_history include:
- Error category (e.g., `[NETWORK_ERROR]`)
- Original error message
- Full context for debugging
## Best Practices
### 1. Always Use Try-Catch
```typescript
try {
await syncOperation();
} catch (error) {
const categorized = categorizeError(error);
logger.error('Operation failed', categorized);
throw categorized; // Re-throw categorized error
}
```
### 2. Provide Context
```typescript
try {
await fetchData();
} catch (error) {
throw new ApiError(
'Failed to fetch companies',
500,
{ entity: 'companies', operation: 'fetch', recordCount: 100 }
);
}
```
### 3. Log at Appropriate Levels
- Use `info` for normal operations
- Use `warn` for recoverable issues
- Use `error` for failures
- Use `debug` for detailed diagnostics
### 4. Include Timing Information
```typescript
const startTime = Date.now();
try {
await operation();
const duration = Date.now() - startTime;
logger.info(`Operation completed in ${duration}ms`);
} catch (error) {
const duration = Date.now() - startTime;
logger.error(`Operation failed after ${duration}ms`, error);
}
```
### 5. Update Sync History
Always update sync_history for tracking:
```typescript
const historyId = await createSyncHistory(entity, syncType);
try {
const stats = await syncEntity(entity);
await updateSyncHistory(historyId, 'completed', stats);
} catch (error) {
await updateSyncHistory(historyId, 'failed', 0, 0, 0, error.message);
throw error;
}
```
## Monitoring and Debugging
### View Recent Errors
```typescript
const syncService = createSyncService(autotaskClient);
const history = await syncService.getSyncHistory(50);
const failures = history.filter(h => h.status === 'failed');
```
### Check Error Patterns
```sql
SELECT
error_message,
COUNT(*) as occurrence_count,
MAX(started_at) as last_occurrence
FROM sync_history
WHERE status = 'failed'
AND started_at > NOW() - INTERVAL '7 days'
GROUP BY error_message
ORDER BY occurrence_count DESC;
```
### Identify Problematic Entities
```sql
SELECT
entity_type,
COUNT(*) as failure_count,
COUNT(*) FILTER (WHERE error_message LIKE '%NETWORK_ERROR%') as network_errors,
COUNT(*) FILTER (WHERE error_message LIKE '%API_ERROR%') as api_errors
FROM sync_history
WHERE status = 'failed'
AND started_at > NOW() - INTERVAL '7 days'
GROUP BY entity_type
ORDER BY failure_count DESC;
```
## Future Enhancements
Potential improvements for error handling:
1. **Retry Logic**: Automatic retry with exponential backoff for retryable errors
2. **Circuit Breaker**: Prevent repeated failures by temporarily disabling failing operations
3. **Error Notifications**: Send alerts for critical errors (email, Slack, etc.)
4. **Error Metrics**: Track error rates and patterns over time
5. **Detailed Stack Traces**: Store full stack traces in separate table for debugging
6. **Error Recovery Workflows**: Automated recovery procedures for common errors