# 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/)