Skip to content
Plurafy

Error Tracking and Alerting

Error tracking and alerting systems enable proactive issue detection and resolution. Learn to implement comprehensive error monitoring and alerting strategies.

Build an error tracking system that captures, categorizes, and alerts on issues in real-time while providing insights for continuous improvement.

  • Error Capture: Catch and record exceptions
  • Categorization: Group similar errors
  • Alerting: Notify on critical issues
  • Analytics: Analyze error patterns
  • Resolution: Track and manage fixes
  • Sentry: Error tracking and monitoring
  • New Relic: APM and error monitoring
  • Rollbar: Error tracking platform
  • Datadog: Comprehensive monitoring
  • CloudWatch: AWS monitoring
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),
};
}
}
// Alerters
const 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),
});
}
};
// Usage
const errorTracker = new ErrorTracker({
alertThreshold: 5,
windowMs: 60000,
});
errorTracker.addAlerter(slackAlerter);
errorTracker.addAlerter(emailAlerter);
// Catch errors
process.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 });
});
const Sentry = require("@sentry/node");
const Tracing = require("@sentry/tracing");
// Initialize Sentry
Sentry.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 middleware
app.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 middleware
app.use(Sentry.Handlers.errorHandler());
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 endpoint
const analytics = new ErrorAnalytics(errorTracker);
app.get('/admin/error-analytics', (req, res) => {
res.json(analytics.getAnalytics());
});
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 alerts
const alertManager = new AlertManager();
// Channels
alertManager.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
}
});
// Rules
alertManager.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'],
});
  1. Error Capture

    • Catch all exceptions
    • Include context information
    • Log stack traces
    • Track error frequency
  2. Alerting

    • Configure severity levels
    • Avoid alert fatigue
    • Test alerting channels
    • Document escalation paths
  3. Analytics

    • Analyze error patterns
    • Track trending issues
    • Monitor resolution time
    • Measure improvements
  4. Integration

    • Connect to issue tracking
    • Integrate with communication tools
    • Link to monitoring systems
    • Automate remediation
  5. Privacy

    • Don’t log PII
    • Redact sensitive data
    • Secure error storage
    • Control access
  • Production Monitoring: Immediate error detection
  • SaaS Platforms: Customer-specific error tracking
  • Microservices: Distributed error aggregation
  • Mobile Apps: Crash reporting
  • API Services: Error pattern analysis