`n

Error Monitoring Setup - Complete Guide

Published: September 25, 2024 | Reading time: 21 minutes

Error Monitoring Overview

Error monitoring helps identify and resolve issues in production:

Monitoring Benefits
# Monitoring Benefits
- Real-time error detection
- Performance monitoring
- User experience tracking
- Proactive issue resolution
- Error trend analysis
- Alert management
- Debugging assistance

Error Monitoring Tools

Popular Error Monitoring Services

Error Monitoring Tools
# Error Monitoring Tools

# 1. Sentry Integration
const Sentry = require('@sentry/node');

Sentry.init({
  dsn: process.env.SENTRY_DSN,
  environment: process.env.NODE_ENV,
  tracesSampleRate: 1.0,
  integrations: [
    new Sentry.Integrations.Http({ tracing: true }),
    new Sentry.Integrations.Express({ app }),
    new Sentry.Integrations.OnUncaughtException(),
    new Sentry.Integrations.OnUnhandledRejection()
  ]
});

// Express error handler
app.use(Sentry.requestHandler());
app.use(Sentry.tracingHandler());

// Custom error reporting
try {
  // Some operation that might fail
  throw new Error('Something went wrong');
} catch (error) {
  Sentry.captureException(error);
}

# 2. New Relic Integration
const newrelic = require('newrelic');

// Custom error reporting
newrelic.noticeError(new Error('Custom error message'));

// Custom metrics
newrelic.recordMetric('Custom/ErrorRate', 0.05);

// Custom events
newrelic.recordCustomEvent('UserAction', {
  userId: '123',
  action: 'login',
  timestamp: Date.now()
});

# 3. DataDog Integration
const tracer = require('dd-trace');

tracer.init({
  service: 'my-app',
  env: process.env.NODE_ENV,
  version: process.env.APP_VERSION
});

// Custom error reporting
const span = tracer.startSpan('custom-operation');
try {
  // Some operation
  throw new Error('Operation failed');
} catch (error) {
  span.setTag('error', true);
  span.setTag('error.message', error.message);
  span.finish();
}

# 4. Rollbar Integration
const Rollbar = require('rollbar');

const rollbar = new Rollbar({
  accessToken: process.env.ROLLBAR_ACCESS_TOKEN,
  environment: process.env.NODE_ENV,
  captureUncaught: true,
  captureUnhandledRejections: true
});

// Custom error reporting
rollbar.error('Something went wrong', {
  custom: {
    userId: '123',
    action: 'user-login'
  }
});

# 5. Bugsnag Integration
const Bugsnag = require('@bugsnag/js');

Bugsnag.start({
  apiKey: process.env.BUGSNAG_API_KEY,
  releaseStage: process.env.NODE_ENV
});

// Custom error reporting
Bugsnag.notify(new Error('Something went wrong'), {
  userId: '123',
  action: 'user-login'
});

# 6. Custom Error Monitor
class ErrorMonitor {
  constructor(options = {}) {
    this.apiKey = options.apiKey;
    this.endpoint = options.endpoint || 'https://api.error-monitor.com';
    this.environment = options.environment || 'development';
    this.service = options.service || 'my-app';
    this.version = options.version || '1.0.0';
    this.errors = [];
    this.maxErrors = 1000;
  }
  
  captureError(error, context = {}) {
    const errorData = {
      timestamp: new Date().toISOString(),
      error: {
        name: error.name,
        message: error.message,
        stack: error.stack
      },
      context: {
        environment: this.environment,
        service: this.service,
        version: this.version,
        ...context
      }
    };
    
    this.errors.push(errorData);
    
    if (this.errors.length > this.maxErrors) {
      this.errors.shift();
    }
    
    // Send to external service
    this.sendToService(errorData);
  }
  
  async sendToService(errorData) {
    try {
      const response = await fetch(`${this.endpoint}/errors`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Authorization': `Bearer ${this.apiKey}`
        },
        body: JSON.stringify(errorData)
      });
      
      if (!response.ok) {
        console.error('Failed to send error to monitoring service');
      }
    } catch (error) {
      console.error('Error sending to monitoring service:', error);
    }
  }
  
  getErrorStats() {
    const errorTypes = {};
    this.errors.forEach(error => {
      const type = error.error.name;
      errorTypes[type] = (errorTypes[type] || 0) + 1;
    });
    
    return {
      totalErrors: this.errors.length,
      errorTypes,
      recentErrors: this.errors.slice(-10)
    };
  }
}

const errorMonitor = new ErrorMonitor({
  apiKey: process.env.ERROR_MONITOR_API_KEY,
  environment: process.env.NODE_ENV,
  service: 'my-app',
  version: '1.0.0'
});

# 7. Error Rate Monitoring
class ErrorRateMonitor {
  constructor(options = {}) {
    this.threshold = options.threshold || 0.05; // 5%
    this.windowSize = options.windowSize || 60000; // 1 minute
    this.errors = [];
    this.requests = [];
  }
  
  recordRequest() {
    this.requests.push(Date.now());
    this.cleanOldEntries();
  }
  
  recordError() {
    this.errors.push(Date.now());
    this.cleanOldEntries();
  }
  
  cleanOldEntries() {
    const now = Date.now();
    const cutoff = now - this.windowSize;
    
    this.errors = this.errors.filter(time => time > cutoff);
    this.requests = this.requests.filter(time => time > cutoff);
  }
  
  getErrorRate() {
    if (this.requests.length === 0) return 0;
    return this.errors.length / this.requests.length;
  }
  
  checkThreshold() {
    const errorRate = this.getErrorRate();
    
    if (errorRate > this.threshold) {
      console.warn(`High error rate detected: ${(errorRate * 100).toFixed(2)}%`);
      return true;
    }
    
    return false;
  }
}

const errorRateMonitor = new ErrorRateMonitor({
  threshold: 0.05,
  windowSize: 60000
});

# 8. Error Alerting
class ErrorAlerting {
  constructor(options = {}) {
    this.thresholds = options.thresholds || {
      errorRate: 0.05,
      errorCount: 100,
      timeWindow: 300000 // 5 minutes
    };
    this.alerts = [];
    this.lastAlert = {};
  }
  
  checkAlerts(errorStats) {
    const alerts = [];
    
    // Check error rate
    if (errorStats.errorRate > this.thresholds.errorRate) {
      alerts.push({
        type: 'HIGH_ERROR_RATE',
        message: `Error rate ${(errorStats.errorRate * 100).toFixed(2)}% exceeds threshold`,
        severity: 'high'
      });
    }
    
    // Check error count
    if (errorStats.errorCount > this.thresholds.errorCount) {
      alerts.push({
        type: 'HIGH_ERROR_COUNT',
        message: `${errorStats.errorCount} errors in ${this.thresholds.timeWindow / 1000}s`,
        severity: 'high'
      });
    }
    
    // Send alerts
    alerts.forEach(alert => {
      this.sendAlert(alert);
    });
  }
  
  sendAlert(alert) {
    const alertKey = `${alert.type}_${Date.now()}`;
    
    // Prevent duplicate alerts
    if (this.lastAlert[alert.type] && 
        Date.now() - this.lastAlert[alert.type] < 300000) {
      return;
    }
    
    this.lastAlert[alert.type] = Date.now();
    
    console.log(`ALERT: ${alert.type} - ${alert.message}`);
    
    // Send to external service
    this.sendToExternalService(alert);
  }
  
  async sendToExternalService(alert) {
    try {
      await fetch(process.env.ALERT_WEBHOOK_URL, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(alert)
      });
    } catch (error) {
      console.error('Failed to send alert:', error);
    }
  }
}

const errorAlerting = new ErrorAlerting({
  thresholds: {
    errorRate: 0.05,
    errorCount: 100,
    timeWindow: 300000
  }
});

# 9. Error Context Enrichment
class ErrorContextEnricher {
  constructor() {
    this.context = {};
  }
  
  setContext(key, value) {
    this.context[key] = value;
  }
  
  enrichError(error, additionalContext = {}) {
    return {
      error: {
        name: error.name,
        message: error.message,
        stack: error.stack
      },
      context: {
        ...this.context,
        ...additionalContext,
        timestamp: new Date().toISOString(),
        processId: process.pid,
        memoryUsage: process.memoryUsage(),
        uptime: process.uptime()
      }
    };
  }
  
  clearContext() {
    this.context = {};
  }
}

const errorContextEnricher = new ErrorContextEnricher();

# 10. Error Recovery
class ErrorRecovery {
  constructor(options = {}) {
    this.maxRetries = options.maxRetries || 3;
    this.retryDelay = options.retryDelay || 1000;
    this.backoffMultiplier = options.backoffMultiplier || 2;
  }
  
  async retry(operation, context = {}) {
    let lastError;
    
    for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
      try {
        return await operation();
      } catch (error) {
        lastError = error;
        
        if (attempt < this.maxRetries) {
          const delay = this.retryDelay * Math.pow(this.backoffMultiplier, attempt - 1);
          await new Promise(resolve => setTimeout(resolve, delay));
        }
      }
    }
    
    throw lastError;
  }
  
  async recover(operation, fallback, context = {}) {
    try {
      return await operation();
    } catch (error) {
      console.error('Primary operation failed, attempting recovery:', error);
      
      try {
        return await fallback();
      } catch (fallbackError) {
        console.error('Recovery operation also failed:', fallbackError);
        throw new Error(`Both primary and recovery operations failed: ${error.message}, ${fallbackError.message}`);
      }
    }
  }
}

const errorRecovery = new ErrorRecovery({
  maxRetries: 3,
  retryDelay: 1000,
  backoffMultiplier: 2
});

Production Error Monitoring

Production Setup and Best Practices

Monitoring Tools

  • Sentry
  • New Relic
  • DataDog
  • Rollbar
  • Bugsnag
  • Custom solutions
  • Open source tools

Best Practices

  • Real-time monitoring
  • Error rate tracking
  • Context enrichment
  • Alert management
  • Error recovery
  • Performance tracking
  • User impact analysis

Error Monitoring Implementation

Complete Implementation Example

Complete Implementation
# Complete Error Monitoring Implementation

# 1. Express Error Handler
const express = require('express');
const Sentry = require('@sentry/node');

const app = express();

// Initialize Sentry
Sentry.init({
  dsn: process.env.SENTRY_DSN,
  environment: process.env.NODE_ENV,
  tracesSampleRate: 1.0
});

// Sentry middleware
app.use(Sentry.requestHandler());
app.use(Sentry.tracingHandler());

// Error handling middleware
app.use((error, req, res, next) => {
  // Capture error with Sentry
  Sentry.captureException(error, {
    tags: {
      component: 'express'
    },
    extra: {
      url: req.url,
      method: req.method,
      headers: req.headers,
      body: req.body
    }
  });
  
  // Log error
  console.error('Express error:', error);
  
  // Send response
  res.status(500).json({
    error: 'Internal server error',
    message: process.env.NODE_ENV === 'development' ? error.message : 'Something went wrong'
  });
});

# 2. Global Error Handlers
process.on('uncaughtException', (error) => {
  console.error('Uncaught Exception:', error);
  
  Sentry.captureException(error, {
    tags: {
      type: 'uncaughtException'
    }
  });
  
  // Graceful shutdown
  process.exit(1);
});

process.on('unhandledRejection', (reason, promise) => {
  console.error('Unhandled Rejection:', reason);
  
  Sentry.captureException(reason, {
    tags: {
      type: 'unhandledRejection'
    },
    extra: {
      promise: promise.toString()
    }
  });
});

# 3. Error Monitoring Service
class ErrorMonitoringService {
  constructor() {
    this.sentry = Sentry;
    this.errorRateMonitor = new ErrorRateMonitor();
    this.errorAlerting = new ErrorAlerting();
    this.errorContextEnricher = new ErrorContextEnricher();
  }
  
  captureError(error, context = {}) {
    // Enrich error with context
    const enrichedError = this.errorContextEnricher.enrichError(error, context);
    
    // Record error for rate monitoring
    this.errorRateMonitor.recordError();
    
    // Capture with Sentry
    this.sentry.captureException(error, {
      tags: context.tags || {},
      extra: context.extra || {},
      user: context.user || {}
    });
    
    // Check for alerts
    const errorStats = {
      errorRate: this.errorRateMonitor.getErrorRate(),
      errorCount: this.errorRateMonitor.errors.length
    };
    
    this.errorAlerting.checkAlerts(errorStats);
  }
  
  captureMessage(message, level = 'info', context = {}) {
    this.sentry.captureMessage(message, level, {
      tags: context.tags || {},
      extra: context.extra || {}
    });
  }
  
  setUser(user) {
    this.sentry.setUser(user);
    this.errorContextEnricher.setContext('user', user);
  }
  
  setTag(key, value) {
    this.sentry.setTag(key, value);
    this.errorContextEnricher.setContext(key, value);
  }
  
  addBreadcrumb(breadcrumb) {
    this.sentry.addBreadcrumb(breadcrumb);
  }
}

const errorMonitoring = new ErrorMonitoringService();

# 4. Error Monitoring Middleware
function errorMonitoringMiddleware(req, res, next) {
  // Set user context
  if (req.user) {
    errorMonitoring.setUser({
      id: req.user.id,
      email: req.user.email
    });
  }
  
  // Set request context
  errorMonitoring.setTag('requestId', req.id);
  errorMonitoring.setTag('method', req.method);
  errorMonitoring.setTag('url', req.url);
  
  // Add breadcrumb
  errorMonitoring.addBreadcrumb({
    message: `${req.method} ${req.url}`,
    category: 'http',
    level: 'info'
  });
  
  // Record request
  errorRateMonitor.recordRequest();
  
  next();
}

app.use(errorMonitoringMiddleware);

# 5. Error Monitoring Routes
app.get('/health', (req, res) => {
  const health = {
    status: 'healthy',
    timestamp: new Date().toISOString(),
    uptime: process.uptime(),
    memory: process.memoryUsage(),
    errorRate: errorRateMonitor.getErrorRate()
  };
  
  res.json(health);
});

app.get('/errors', (req, res) => {
  const errorStats = errorRateMonitor.getErrorStats();
  res.json(errorStats);
});

# 6. Error Monitoring Dashboard
app.get('/monitoring', (req, res) => {
  const stats = {
    errorRate: errorRateMonitor.getErrorRate(),
    errorCount: errorRateMonitor.errors.length,
    requestCount: errorRateMonitor.requests.length,
    uptime: process.uptime(),
    memory: process.memoryUsage()
  };
  
  res.json(stats);
});

# 7. Error Monitoring Alerts
class ErrorMonitoringAlerts {
  constructor() {
    this.webhookUrl = process.env.ALERT_WEBHOOK_URL;
    this.emailService = process.env.EMAIL_SERVICE;
  }
  
  async sendAlert(alert) {
    const alertData = {
      timestamp: new Date().toISOString(),
      service: 'my-app',
      environment: process.env.NODE_ENV,
      ...alert
    };
    
    // Send webhook
    if (this.webhookUrl) {
      await this.sendWebhook(alertData);
    }
    
    // Send email
    if (this.emailService) {
      await this.sendEmail(alertData);
    }
  }
  
  async sendWebhook(alertData) {
    try {
      await fetch(this.webhookUrl, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(alertData)
      });
    } catch (error) {
      console.error('Failed to send webhook alert:', error);
    }
  }
  
  async sendEmail(alertData) {
    // Implementation depends on email service
    console.log('Sending email alert:', alertData);
  }
}

const errorMonitoringAlerts = new ErrorMonitoringAlerts();

# 8. Error Monitoring Metrics
class ErrorMonitoringMetrics {
  constructor() {
    this.metrics = {
      totalErrors: 0,
      errorRate: 0,
      averageResponseTime: 0,
      uptime: 0
    };
  }
  
  updateMetrics() {
    this.metrics.totalErrors = errorRateMonitor.errors.length;
    this.metrics.errorRate = errorRateMonitor.getErrorRate();
    this.metrics.uptime = process.uptime();
    this.metrics.memoryUsage = process.memoryUsage();
  }
  
  getMetrics() {
    this.updateMetrics();
    return this.metrics;
  }
}

const errorMonitoringMetrics = new ErrorMonitoringMetrics();

# 9. Error Monitoring Reports
class ErrorMonitoringReports {
  constructor() {
    this.reports = [];
  }
  
  generateReport() {
    const report = {
      timestamp: new Date().toISOString(),
      errorRate: errorRateMonitor.getErrorRate(),
      errorCount: errorRateMonitor.errors.length,
      requestCount: errorRateMonitor.requests.length,
      uptime: process.uptime(),
      memory: process.memoryUsage(),
      errors: errorRateMonitor.errors.slice(-10)
    };
    
    this.reports.push(report);
    
    if (this.reports.length > 100) {
      this.reports.shift();
    }
    
    return report;
  }
  
  getReports() {
    return this.reports;
  }
}

const errorMonitoringReports = new ErrorMonitoringReports();

# 10. Error Monitoring Cleanup
class ErrorMonitoringCleanup {
  constructor() {
    this.cleanupInterval = 300000; // 5 minutes
    this.maxAge = 3600000; // 1 hour
  }
  
  start() {
    setInterval(() => {
      this.cleanup();
    }, this.cleanupInterval);
  }
  
  cleanup() {
    const now = Date.now();
    const cutoff = now - this.maxAge;
    
    // Cleanup old errors
    errorRateMonitor.errors = errorRateMonitor.errors.filter(time => time > cutoff);
    errorRateMonitor.requests = errorRateMonitor.requests.filter(time => time > cutoff);
    
    // Cleanup old reports
    errorMonitoringReports.reports = errorMonitoringReports.reports.filter(
      report => new Date(report.timestamp).getTime() > cutoff
    );
  }
}

const errorMonitoringCleanup = new ErrorMonitoringCleanup();
errorMonitoringCleanup.start();

Summary

Error monitoring setup involves several key components:

  • Monitoring Tools: Sentry, New Relic, DataDog, and custom solutions
  • Error Tracking: Real-time error capture and context enrichment
  • Alerting: Error rate monitoring and alert management
  • Production Implementation: Complete monitoring setup and best practices

Need More Help?

Struggling with error monitoring setup or need help implementing production monitoring? Our Node.js experts can help you set up comprehensive error monitoring systems.

Get Error Monitoring Help