Why Error Tracking Matters
Why Error Tracking Matters
Error tracking is the process of collecting, grouping, and alerting on runtime exceptions across your application. Unlike logging which records events, error tracking captures stack traces, breadcrumbs, and user context to help you reproduce and fix bugs faster.
The Problem Without Error Tracking
When a production issue is reported, the developer asks for reproduction steps, the user cannot reproduce it, and there are no error messages. Without error tracking, you are flying blind in production.
What Error Tracking Provides
- Automatic capture: Unhandled exceptions are caught without manual logging
- Grouping: Similar errors are clustered so you fix root causes, not symptoms
- Context: Breadcrumbs show what the user did before the error
- Alerting: Real-time notifications when error rates spike
- Prioritization: Error volume and user impact guide what to fix first
Error Tracking vs Logging
| Aspect | Logging | Error Tracking |
|---|---|---|
| Purpose | Record events | Capture exceptions |
| Format | Text lines | Structured events with stack traces |
| Grouping | Manual | Automatic fingerprinting |
| Alerting | Log-based queries | Error rate thresholds |
| Context | What you log | Auto-collected breadcrumbs |
Sentry Architecture
Application Code -> Sentry SDK (init + capture) -> Sentry Relay (server-side) -> Sentry Backend (Grouping Engine, Alerting Pipeline, Dashboard UI)
Sentry Integration
Sentry Integration
Sentry is the industry-standard error tracking platform. It provides SDKs for virtually every language and framework.
Node.js Backend Setup
const Sentry = require('@sentry/node');
const express = require('express');
// Initialize Sentry BEFORE any other require/import
Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: process.env.NODE_ENV,
release: process.env.COMMIT_SHA,
tracesSampleRate: 0.2,
integrations: [
new Sentry.Integrations.Http({ tracing: true }),
new Sentry.Integrations.Express({ app }),
new Sentry.Integrations.Mongo(),
],
beforeSend(event) {
if (event.request && event.request.data) {
delete event.request.data.password;
delete event.request.data.creditCard;
}
return event;
},
});
const app = express();
app.use(Sentry.Handlers.requestHandler());
app.get('/api/orders', async (req, res) => {
const orders = await Order.find({ userId: req.user.id });
res.json(orders);
});
app.use(Sentry.Handlers.errorHandler());
app.use((err, req, res, next) => {
const statusCode = err.statusCode || 500;
res.status(statusCode).json({
error: { message: err.message, stack: process.env.NODE_ENV === 'development' ? err.stack : undefined }
});
});
React Frontend Setup
import * as Sentry from '@sentry/react';
Sentry.init({
dsn: import.meta.env.VITE_SENTRY_DSN,
environment: import.meta.env.MODE,
release: import.meta.env.VITE_APP_VERSION,
integrations: [
Sentry.browserTracingIntegration(),
Sentry.replayIntegration({ maskAllText: true, blockAllMedia: true }),
],
tracesSampleRate: 0.1,
replaysSessionSampleRate: 0.01,
replaysOnErrorSampleRate: 1.0,
});
Custom Error Fingerprinting
Sentry.withScope((scope) => {
scope.setFingerprint(['order-payment-failure', order.paymentMethod, String(order.amount)]);
Sentry.captureException(new Error('Payment processing failed'));
});
Enriching Events with Context
Sentry.setUser({ id: req.user.id, email: req.user.email });
Sentry.setTag('order_id', orderId);
Sentry.setTag('service', 'payment');
Sentry.setExtra('request_body', req.body);
Sentry.addBreadcrumb({
category: 'payment',
message: 'Attempting Stripe charge',
level: 'info',
data: { amount: 99.99, currency: 'USD' },
});
React Error Boundaries
React Error Boundaries
Error boundaries are React components that catch JavaScript errors in their child component tree. They prevent the entire app from crashing when a single component fails.
How Error Boundaries Work
Component Tree: App -> ErrorBoundary -> Header (renders OK) + Sidebar (ERROR, caught by boundary) + MainContent (continues working). The error boundary catches failures in its children and renders fallback UI while the rest of the app keeps running.
Implementation
import React from 'react';
import * as Sentry from '@sentry/react';
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
componentDidCatch(error, errorInfo) {
Sentry.withScope((scope) => {
scope.setExtras({
componentStack: errorInfo.componentStack,
...this.props.extraContext,
});
scope.setLevel('error');
Sentry.captureException(error);
});
}
render() {
if (this.state.hasError) {
return this.props.fallback || (
<div className='error-boundary-fallback'>
<h2>Something went wrong</h2>
<p>{this.state.error ? this.state.error.message : 'Unknown error'}</p>
<button onClick={() => this.setState({ hasError: false })}>
Try again
</button>
</div>
);
}
return this.props.children;
}
}
// Usage
function App() {
return (
<ErrorBoundary fallback={<CriticalErrorPage />}>
<Dashboard />
</ErrorBoundary>
);
}
Hook-Based Error Handling
import { useEffect } from 'react';
import * as Sentry from '@sentry/react';
function useSentryErrorBoundary() {
useEffect(() => {
const errorHandler = (event) => {
Sentry.captureException(event.error || new Error('Unknown error'), {
extra: {
filename: event.filename,
lineno: event.lineno,
colno: event.colno,
},
});
};
window.addEventListener('error', errorHandler);
window.addEventListener('unhandledrejection', (event) => {
Sentry.captureException(event.reason);
});
return () => {
window.removeEventListener('error', errorHandler);
};
}, []);
}
Alerting and Monitoring
Alerting and Monitoring
Effective error tracking requires alerting that notifies the right team at the right time without causing fatigue.
Alert Configuration
alerts:
- name: Error Rate Spike
type: metric
metric: error_rate
threshold: 5%
window: 10 minutes
action:
- notify: slack
channel: '#incidents'
- notify: pagerduty
severity: high
- name: New Error Type
type: event
trigger: first_seen
action:
- notify: slack
channel: '#engineering'
- name: Release Regression
type: metric
metric: error_rate
compare: release_over_release
threshold: 2x
action:
- notify: slack
channel: '#deployments'
- notify: email
to: team-lead@company.com
Dashboard Metrics
const errorMetrics = {
totalErrors: 'count of all captured exceptions',
errorRate: 'errors / total sessions',
affectedUsers: 'unique users who saw an error',
errorTrend: 'change in error rate over time',
regressions: 'new errors in current release vs previous',
resolved: 'errors marked as resolved vs still occurring',
apdex: 'application performance index',
crashFreeRate: 'sessions without errors / total sessions',
userImpact: 'percentage of users affected',
};
Cron-Based Error Monitoring
const cron = require('node-cron');
cron.schedule('*/5 * * * *', async () => {
const stats = await sentryClient.getProjectStats({
project: 'my-app',
since: Date.now() - 5 * 60 * 1000,
});
const errorRate = (stats.totalErrors / stats.totalEvents) * 100;
if (errorRate > 5) {
await slackClient.send({
channel: '#incidents',
text: `High error rate detected: ${errorRate.toFixed(2)}% in the last 5 minutes.`,
attachments: [{
color: 'danger',
fields: [
{ title: 'Error Rate', value: `${errorRate.toFixed(2)}%`, short: true },
{ title: 'Total Errors', value: stats.totalErrors, short: true },
{ title: 'Top Error', value: stats.topError.title, short: false },
],
}],
});
}
});
Error Triage Workflow
New Error Captured -> Auto-Group by Fingerprint -> Assign Severity (error rate + user impact)
- P0 (Critical): > 10% error rate OR payment/auth failure -> PagerDuty + Slack + Immediate fix
- P1 (High): 5-10% error rate OR core feature broken -> Slack + Fix within 4 hours
- P2 (Medium): 1-5% error rate OR non-core feature -> Jira ticket + Fix within 24 hours
- P3 (Low): < 1% error rate OR cosmetic issue -> Backlog + Fix when available
Suppression Rules
const ignoreErrors = [
'ResizeObserver loop',
'Non-Error promise rejection',
'Network request failed',
'Script error.',
];
const maintenanceWindow = {
start: '2024-01-15T02:00:00Z',
end: '2024-01-15T04:00:00Z',
suppressAlerts: true,
};
Quiz
1. What is the key difference between error tracking and logging?
2. Why should Sentry.init() be called before any other imports in a Node.js application?
3. What is the purpose of componentDidCatch in a React error boundary?
Flashcards
Question
What is error fingerprinting?
Click to reveal answer
Answer
A technique Sentry uses to group similar exceptions together. You can customize fingerprints to control which errors are grouped as one issue vs separate issues.
Question
What are breadcrumbs in error tracking?
Click to reveal answer
Answer
A chronological trail of events (console logs, HTTP requests, user clicks) captured before an error occurs. They help developers understand what led to the exception.
Question
Why use error boundaries in React?
Click to reveal answer
Answer
Error boundaries catch errors in child component trees and render fallback UI, preventing the entire app from crashing when a single component fails.
Revision Notes
Key Takeaways
- 1. Sentry.init() must be called before all other imports to properly patch Node.js APIs
- 2. Use beforeSend to strip sensitive data (passwords, tokens) before sending to Sentry
- 3. Error boundaries in React catch rendering errors and prevent full app crashes
- 4. Alert on error rate changes (not just raw counts) to account for traffic fluctuations
- 5. Error fingerprinting controls grouping; customize it for domain-specific error types
- 6. Breadcrumbs provide the context needed to reproduce production bugs
Interview Tips
- • Explain the difference between error tracking and logging and when to use each
- • Describe how you would handle a sudden spike in production errors (triage workflow)
- • Walk through implementing an error boundary and where to place it in the component tree
- • Discuss how to avoid alert fatigue while still catching critical issues
- • Explain how Sentry groups errors and how custom fingerprinting works
- • Describe the security considerations when sending error data to external services
Cheat Sheet
Error Tracking Cheat Sheet
Sentry Setup
- Init before all imports
- Use DSN from environment variables
- Set release, environment, tracesSampleRate
- Use beforeSend to strip PII
Error Boundaries
- Catch errors in child component trees
- Render fallback UI, send to Sentry
- Place at feature/page level, not app level
Alerting
- Alert on error rate %, not raw counts
- Use suppression rules for known issues
- Tier alerts: P0 (PagerDuty) -> P3 (backlog)
Best Practices
- Fingerprint custom errors for better grouping
- Collect breadcrumbs for debugging context
- Strip sensitive data before sending
- Use session replay for frontend errors
- Set up release tracking to catch regressions