# 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
