Error Tracking and Alerting
Error tracking and alerting systems enable proactive issue detection and resolution. Learn to implement comprehensive error monitoring and alerting strategies.
Challenge
Section titled “Challenge”Build an error tracking system that captures, categorizes, and alerts on issues in real-time while providing insights for continuous improvement.
Explanation
Section titled “Explanation”Error Tracking Components
Section titled “Error Tracking Components”- Error Capture: Catch and record exceptions
- Categorization: Group similar errors
- Alerting: Notify on critical issues
- Analytics: Analyze error patterns
- Resolution: Track and manage fixes
Popular Services
Section titled “Popular Services”- Sentry: Error tracking and monitoring
- New Relic: APM and error monitoring
- Rollbar: Error tracking platform
- Datadog: Comprehensive monitoring
- CloudWatch: AWS monitoring
Code Examples
Section titled “Code Examples”1. Custom Error Tracking
Section titled “1. Custom Error Tracking”class ErrorTracker { constructor(options = {}) { this.errors = []; this.maxErrors = options.maxErrors || 1000; this.alertThreshold = options.alertThreshold || 10; this.windowMs = options.windowMs || 60000; // 1 minute this.alerters = []; }
trackError(error, context = {}) { const errorRecord = { id: this.generateId(), message: error.message, type: error.constructor.name, stack: error.stack, code: error.code, statusCode: error.statusCode || 500, timestamp: Date.now(), context, count: 1, };
// Check if similar error already exists const existing = this.findSimilarError(error); if (existing) { existing.count++; existing.lastOccurrence = Date.now(); } else { this.errors.push(errorRecord); if (this.errors.length > this.maxErrors) { this.errors.shift(); } }
this.checkAlerts(errorRecord); }
findSimilarError(error) { return this.errors.find(e => e.message === error.message && e.type === error.constructor.name ); }
checkAlerts(errorRecord) { const recentErrors = this.getErrorsInWindow(); const count = recentErrors.length;
if (count >= this.alertThreshold) { this.alert({ type: 'error-threshold-exceeded', count, errors: recentErrors, threshold: this.alertThreshold, }); }
if (errorRecord.statusCode >= 500) { this.alert({ type: 'critical-error', error: errorRecord, }); } }
getErrorsInWindow() { const now = Date.now(); return this.errors.filter(e => now - e.timestamp < this.windowMs); }
addAlerter(alerter) { this.alerters.push(alerter); }
alert(alertData) { this.alerters.forEach(alerter => { alerter(alertData).catch(err => { console.error('Alerter failed:', err.message); }); }); }
generateId() { return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; }
getStats() { const errors = this.errors; return { total: errors.length, criticalCount: errors.filter(e => e.statusCode >= 500).length, topErrors: errors .sort((a, b) => b.count - a.count) .slice(0, 10), }; }}
// Alertersconst slackAlerter = async (alertData) => { const message = `Error Alert: ${alertData.type}Count: ${alertData.count || 1}Threshold: ${alertData.threshold} `;
// Send to Slack webhook await fetch(process.env.SLACK_WEBHOOK, { method: 'POST', body: JSON.stringify({ text: message }), });};
const emailAlerter = async (alertData) => { if (alertData.type === 'critical-error') { await emailService.send({ to: 'team@example.com', subject: 'Critical Error Occurred', body: JSON.stringify(alertData.error, null, 2), }); }};
// Usageconst errorTracker = new ErrorTracker({ alertThreshold: 5, windowMs: 60000,});
errorTracker.addAlerter(slackAlerter);errorTracker.addAlerter(emailAlerter);
// Catch errorsprocess.on('unhandledRejection', (reason) => { errorTracker.trackError(reason instanceof Error ? reason : new Error(String(reason)), { source: 'unhandledRejection', });});
app.use((err, req, res, next) => { errorTracker.trackError(err, { method: req.method, path: req.path, ip: req.ip, }); res.status(err.statusCode || 500).json({ error: err.message });});2. Sentry Integration
Section titled “2. Sentry Integration”const Sentry = require("@sentry/node");const Tracing = require("@sentry/tracing");
// Initialize SentrySentry.init({ dsn: process.env.SENTRY_DSN, environment: process.env.NODE_ENV, tracesSampleRate: 1.0,
integrations: [ new Sentry.Integrations.Http({ tracing: true }), new Tracing.Integrations.Express({ app: true, request: true, serverName: false, transaction: "path", }), ],
// Custom filtering beforeSend(event, hint) { // Filter out 404 errors if (event.exception) { const error = hint.originalException; if (error?.statusCode === 404) { return null; } } return event; },});
const app = require('express')();
// Request handler middlewareapp.use(Sentry.Handlers.requestHandler());app.use(Sentry.Handlers.tracingHandler());
app.get('/api/users/:id', async (req, res) => { const span = Sentry.startActiveSpan({ op: 'database.query', name: 'fetch_user', });
try { const user = await database.users.findById(req.params.id); res.json(user); } catch (error) { Sentry.captureException(error, { tags: { section: 'users', action: 'fetch', }, extra: { userId: req.params.id, method: req.method, }, }); res.status(500).json({ error: 'Internal server error' }); } finally { span?.end(); }});
// Error handler middlewareapp.use(Sentry.Handlers.errorHandler());3. Error Analytics Dashboard
Section titled “3. Error Analytics Dashboard”class ErrorAnalytics { constructor(errorTracker) { this.errorTracker = errorTracker; }
getErrorTrends(hours = 24) { const now = Date.now(); const startTime = now - hours * 60 * 60 * 1000; const interval = 60 * 60 * 1000; // 1 hour buckets
const buckets = {}; for (let time = startTime; time < now; time += interval) { buckets[new Date(time).toISOString()] = 0; }
this.errorTracker.errors.forEach(error => { if (error.timestamp >= startTime) { const bucket = Math.floor((error.timestamp - startTime) / interval) * interval; const key = new Date(startTime + bucket).toISOString(); buckets[key] = (buckets[key] || 0) + error.count; } });
return buckets; }
getErrorsByType() { const byType = {}; this.errorTracker.errors.forEach(error => { byType[error.type] = (byType[error.type] || 0) + error.count; }); return byType; }
getErrorsByStatusCode() { const byStatus = {}; this.errorTracker.errors.forEach(error => { const status = error.statusCode || 'unknown'; byStatus[status] = (byStatus[status] || 0) + error.count; }); return byStatus; }
getMostFrequentErrors(limit = 10) { return this.errorTracker.errors .sort((a, b) => b.count - a.count) .slice(0, limit) .map(e => ({ message: e.message, type: e.type, count: e.count, lastOccurrence: e.lastOccurrence, })); }
getAnalytics() { return { totalErrors: this.errorTracker.errors.reduce((sum, e) => sum + e.count, 0), uniqueErrors: this.errorTracker.errors.length, errorTrends: this.getErrorTrends(), byType: this.getErrorsByType(), byStatus: this.getErrorsByStatusCode(), mostFrequent: this.getMostFrequentErrors(), }; }}
// Dashboard endpointconst analytics = new ErrorAnalytics(errorTracker);
app.get('/admin/error-analytics', (req, res) => { res.json(analytics.getAnalytics());});4. Alert Configuration
Section titled “4. Alert Configuration”class AlertManager { constructor() { this.rules = []; this.channels = new Map(); }
registerChannel(name, handler) { this.channels.set(name, handler); }
addRule(rule) { this.rules.push(rule); }
async evaluateAndAlert(errorData) { for (const rule of this.rules) { if (this.matchesRule(errorData, rule)) { await this.sendAlert(rule.channels, { rule: rule.name, error: errorData, severity: rule.severity, }); } } }
matchesRule(errorData, rule) { if (rule.statusCodes && !rule.statusCodes.includes(errorData.statusCode)) { return false; }
if (rule.errorTypes && !rule.errorTypes.includes(errorData.type)) { return false; }
if (rule.condition && !rule.condition(errorData)) { return false; }
return true; }
async sendAlert(channelNames, data) { const promises = channelNames.map(name => { const handler = this.channels.get(name); if (!handler) return Promise.resolve(); return handler(data); });
await Promise.allSettled(promises); }}
// Configure alertsconst alertManager = new AlertManager();
// ChannelsalertManager.registerChannel('slack', async (data) => { await fetch(process.env.SLACK_WEBHOOK, { method: 'POST', body: JSON.stringify({ text: `${data.severity}: ${data.error.message}`, }), });});
alertManager.registerChannel('pagerduty', async (data) => { if (data.severity === 'critical') { // Send to PagerDuty }});
// RulesalertManager.addRule({ name: 'Critical Errors', severity: 'critical', statusCodes: [500, 502, 503], channels: ['slack', 'pagerduty'],});
alertManager.addRule({ name: 'Database Errors', severity: 'warning', errorTypes: ['DatabaseError', 'ConnectionError'], channels: ['slack'],});Best Practices
Section titled “Best Practices”-
Error Capture
- Catch all exceptions
- Include context information
- Log stack traces
- Track error frequency
-
Alerting
- Configure severity levels
- Avoid alert fatigue
- Test alerting channels
- Document escalation paths
-
Analytics
- Analyze error patterns
- Track trending issues
- Monitor resolution time
- Measure improvements
-
Integration
- Connect to issue tracking
- Integrate with communication tools
- Link to monitoring systems
- Automate remediation
-
Privacy
- Don’t log PII
- Redact sensitive data
- Secure error storage
- Control access
Real-World Applications
Section titled “Real-World Applications”- Production Monitoring: Immediate error detection
- SaaS Platforms: Customer-specific error tracking
- Microservices: Distributed error aggregation
- Mobile Apps: Crash reporting
- API Services: Error pattern analysis