- 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
293 lines
7.6 KiB
Markdown
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
|