Yaltopia-Ticket-Admin/dev-docs/ERROR_TRACKING_FALLBACK.md

393 lines
7.2 KiB
Markdown

# Error Tracking with Fallback System
## Overview
This project uses a **dual error tracking system**:
1. **Primary**: Sentry (cloud-based)
2. **Fallback**: Custom backend logging (if Sentry fails)
## Architecture
```
Error Occurs
Try Sentry First
If Sentry Fails → Queue for Backend
Send to Backend API: POST /api/v1/errors/log
```
## Usage
### Basic Error Tracking
```typescript
import { errorTracker } from '@/lib/error-tracker'
try {
await riskyOperation()
} catch (error) {
errorTracker.trackError(error, {
tags: { section: 'payment' },
extra: { orderId: '123' },
userId: user.id
})
}
```
### Track Messages
```typescript
errorTracker.trackMessage('Payment processed successfully', 'info', {
amount: 100,
currency: 'USD'
})
```
### Set User Context
```typescript
// After login
errorTracker.setUser({
id: user.id,
email: user.email,
name: user.name
})
// On logout
errorTracker.clearUser()
```
### Add Breadcrumbs
```typescript
errorTracker.addBreadcrumb('navigation', 'User clicked checkout button', 'info')
```
## Backend API Required
Your backend needs to implement this endpoint:
### POST /api/v1/errors/log
**Request Body:**
```json
{
"message": "Error message",
"stack": "Error stack trace",
"url": "https://app.example.com/dashboard",
"userAgent": "Mozilla/5.0...",
"timestamp": "2024-02-24T10:30:00.000Z",
"userId": "user-123",
"extra": {
"section": "payment",
"orderId": "123"
}
}
```
**Response:**
```json
{
"success": true,
"logId": "log-456"
}
```
### Backend Implementation Example (Node.js/Express)
```javascript
// routes/errors.js
router.post('/errors/log', async (req, res) => {
try {
const { message, stack, url, userAgent, timestamp, userId, extra } = req.body
// Save to database
await ErrorLog.create({
message,
stack,
url,
userAgent,
timestamp: new Date(timestamp),
userId,
extra: JSON.stringify(extra)
})
// Optional: Send alert for critical errors
if (message.includes('payment') || message.includes('auth')) {
await sendSlackAlert(message, stack)
}
res.json({ success: true })
} catch (error) {
console.error('Failed to log error:', error)
res.status(500).json({ success: false })
}
})
```
### Database Schema
```sql
CREATE TABLE error_logs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
message TEXT NOT NULL,
stack TEXT,
url TEXT NOT NULL,
user_agent TEXT,
timestamp TIMESTAMP NOT NULL,
user_id UUID,
extra JSONB,
created_at TIMESTAMP DEFAULT NOW()
);
CREATE INDEX idx_error_logs_timestamp ON error_logs(timestamp DESC);
CREATE INDEX idx_error_logs_user_id ON error_logs(user_id);
```
## How It Works
### 1. Automatic Global Error Handling
All uncaught errors are automatically tracked:
```typescript
// Automatically catches all errors
window.addEventListener('error', (event) => {
errorTracker.trackError(new Error(event.message))
})
// Catches unhandled promise rejections
window.addEventListener('unhandledrejection', (event) => {
errorTracker.trackError(event.reason)
})
```
### 2. Queue System
Errors are queued and sent to backend:
- Max queue size: 50 errors
- Automatic retry on failure
- Prevents memory leaks
### 3. Dual Tracking
Every error is sent to:
1. **Sentry** (if available) - Rich debugging features
2. **Backend** (always) - Your own database for compliance/analysis
## Sentry Alternatives
If you want to replace Sentry entirely:
### 1. LogRocket
```bash
npm install logrocket
```
```typescript
import LogRocket from 'logrocket'
LogRocket.init('your-app-id')
// Track errors
LogRocket.captureException(error)
```
### 2. Rollbar
```bash
npm install rollbar
```
```typescript
import Rollbar from 'rollbar'
const rollbar = new Rollbar({
accessToken: 'your-token',
environment: 'production'
})
rollbar.error(error)
```
### 3. Bugsnag
```bash
npm install @bugsnag/js @bugsnag/plugin-react
```
```typescript
import Bugsnag from '@bugsnag/js'
import BugsnagPluginReact from '@bugsnag/plugin-react'
Bugsnag.start({
apiKey: 'your-api-key',
plugins: [new BugsnagPluginReact()]
})
```
### 4. Self-Hosted GlitchTip
```bash
# Docker Compose
docker-compose up -d
```
Free, open-source, Sentry-compatible API.
## Benefits of Fallback System
### 1. Reliability
- Never lose error data if Sentry is down
- Backend always receives errors
### 2. Compliance
- Keep error logs in your own database
- Meet data residency requirements
- Full control over sensitive data
### 3. Cost Control
- Reduce Sentry event count
- Use backend for high-volume errors
- Keep Sentry for detailed debugging
### 4. Custom Analysis
- Query errors with SQL
- Build custom dashboards
- Integrate with your alerting system
## Configuration
### Enable/Disable Fallback
```typescript
// src/lib/error-tracker.ts
// Disable backend logging (Sentry only)
const ENABLE_BACKEND_LOGGING = false
private queueError(errorLog: ErrorLog) {
if (!ENABLE_BACKEND_LOGGING) return
// ... rest of code
}
```
### Adjust Queue Size
```typescript
private maxQueueSize = 100 // Increase for high-traffic apps
```
### Change Backend Endpoint
```typescript
await apiClient.post('/custom/error-endpoint', errorLog)
```
## Monitoring Dashboard
Build a simple error dashboard:
```typescript
// Backend endpoint
router.get('/errors/stats', async (req, res) => {
const stats = await db.query(`
SELECT
DATE(timestamp) as date,
COUNT(*) as count,
COUNT(DISTINCT user_id) as affected_users
FROM error_logs
WHERE timestamp > NOW() - INTERVAL '7 days'
GROUP BY DATE(timestamp)
ORDER BY date DESC
`)
res.json(stats)
})
```
## Best Practices
### 1. Don't Log Everything
```typescript
// Bad: Logging expected errors
if (!user) {
errorTracker.trackError(new Error('User not found'))
}
// Good: Only log unexpected errors
try {
await criticalOperation()
} catch (error) {
errorTracker.trackError(error)
}
```
### 2. Add Context
```typescript
errorTracker.trackError(error, {
extra: {
action: 'checkout',
step: 'payment',
amount: 100
}
})
```
### 3. Set User Context Early
```typescript
// In your auth flow
useEffect(() => {
if (user) {
errorTracker.setUser({
id: user.id,
email: user.email,
name: user.name
})
}
}, [user])
```
### 4. Clean Up on Logout
```typescript
const handleLogout = () => {
errorTracker.clearUser()
// ... rest of logout
}
```
## Troubleshooting
### Errors Not Reaching Backend
1. Check network tab for failed requests
2. Verify backend endpoint exists
3. Check CORS configuration
4. Review backend logs
### Queue Growing Too Large
1. Increase `maxQueueSize`
2. Check backend availability
3. Add retry logic with exponential backoff
### Duplicate Errors
This is intentional - errors go to both Sentry and backend. To disable:
```typescript
// Only send to backend if Sentry fails
try {
Sentry.captureException(error)
} catch (sentryError) {
this.queueError(errorLog) // Only fallback
}
```
## Resources
- [Sentry Documentation](https://docs.sentry.io/)
- [LogRocket Documentation](https://docs.logrocket.com/)
- [Rollbar Documentation](https://docs.rollbar.com/)
- [GlitchTip (Self-hosted)](https://glitchtip.com/)