# 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: 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