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