Frontend Error Monitoring Complete Guide: Sentry, SourceMap, and User-Transparent Exception Capture
前端工程(Updated Jun 2, 2026)
Why Frontend Error Monitoring Is Indispensable
Users encountering blank screens, unresponsive buttons, or API errors in production — if you don't know about it, you can't fix it. Frontend error monitoring is the bridge connecting real user experience with developer awareness.
| Cost of Not Monitoring | Benefit of Monitoring |
|---|---|
| Bugs discovered only through user complaints | Real-time anomaly detection, minute-level response |
| Unreproducible sporadic issues | Complete stack traces + user behavior replay |
| Production incidents lasting hours | Auto-alerting, drastically reduced MTTR |
| Guesswork-based fixes | Data-driven, precise localization |
The Five Sources of Frontend Errors
1. Syntax and Runtime Errors
// ReferenceError: x is not defined
console.log(x);
// TypeError: Cannot read properties of undefined
const user = undefined;
user.name;
2. Resource Loading Errors
// Image, script, stylesheet loading failures
const img = new Image();
img.src = '/broken-image.png';
img.onerror = () => {
trackError({ type: 'resource', tag: 'img', src: img.src });
};
3. Uncaught Promise Exceptions
// The most easily overlooked error type
fetch('/api/data')
.then(res => res.json())
// .then throws an exception but no .catch
.then(data => data.results.map(r => r.name));
4. API Exceptions
// HTTP error codes + business error codes
fetch('/api/order')
.then(res => {
if (!res.ok) {
trackError({ type: 'http', status: res.status, url: res.url });
}
return res.json();
});
5. Web Worker / iframe Errors
// Worker internal exceptions don't bubble to the main thread
const worker = new Worker('/worker.js');
worker.onerror = (e) => {
trackError({
type: 'worker',
message: e.message,
filename: e.filename,
lineno: e.lineno,
});
};
Global Error Capture: Four Lines of Defense
Defense Line 1: window.onerror
window.onerror = (message, source, lineno, colno, error) => {
trackError({
type: 'runtime',
message,
source,
lineno,
colno,
stack: error?.stack,
});
return false; // Don't suppress default console output
};
Limitation: Cannot capture resource loading errors or Promise exceptions.
Defense Line 2: window.addEventListener('error')
window.addEventListener('error', (event) => {
if (event.target instanceof HTMLElement) {
// Resource loading error
trackError({
type: 'resource',
tagName: event.target.tagName,
src: event.target.src || event.target.href,
});
} else {
// Runtime error (may duplicate with onerror, needs dedup)
trackError({
type: 'runtime',
message: event.message,
stack: event.error?.stack,
});
}
}, true); // Use capture phase
Defense Line 3: window.addEventListener('unhandledrejection')
window.addEventListener('unhandledrejection', (event) => {
trackError({
type: 'unhandledRejection',
reason: event.reason?.message || String(event.reason),
stack: event.reason?.stack,
});
});
Defense Line 4: React Error Boundary
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
trackError({
type: 'react',
message: error.message,
stack: error.stack,
componentStack: errorInfo.componentStack,
});
}
render() {
if (this.state.hasError) {
return <FallbackUI onRetry={() => this.setState({ hasError: false })} />;
}
return this.props.children;
}
}
SourceMap Deobfuscation: Restoring Real Stack Traces from Minified Code
Production code is minified and obfuscated, making error stack traces completely unreadable. SourceMap deobfuscation is the core capability for production troubleshooting.
Minified Stack Trace Example
TypeError: Cannot read properties of undefined (reading 'name')
at a (main.abc123.js:1:2345)
at o (main.abc123.js:1:5678)
at t (main.abc123.js:1:9012)
Deobfuscated Real Stack Trace
TypeError: Cannot read properties of undefined (reading 'name')
at getUserInfo (src/services/user.ts:42:15)
at fetchDashboard (src/pages/dashboard.tsx:128:22)
at useEffectCallback (src/pages/dashboard.tsx:115:5)
SourceMap Security Strategies
| Strategy | Security | Complexity | Best For |
|---|---|---|---|
| Private NPM + CI deobfuscation | High | Medium | Most teams |
| Sentry server-side deobfuscation | Medium | Low | Quick integration |
| Base64 inline SourceMap | Low | Low | Dev environment only |
| Independent .map files on production | Very Low | Low | Not recommended |
// Deobfuscate using the source-map library
import { SourceMapConsumer } from 'source-map';
async function applySourceMap(position, mapContent) {
const consumer = await new SourceMapConsumer(mapContent);
const original = consumer.originalPositionFor({
line: position.line,
column: position.column,
});
return original;
}
Sentry Integration in Practice
Installation and Initialization
npm install @sentry/react @sentry/tracing
import * as Sentry from '@sentry/react';
Sentry.init({
dsn: 'https://xxx@o123456.ingest.sentry.io/789',
environment: process.env.NODE_ENV,
release: process.env.APP_VERSION,
tracesSampleRate: 0.1,
replaysSessionSampleRate: 0.01,
replaysOnErrorSampleRate: 1.0,
integrations: [
Sentry.browserTracingIntegration(),
Sentry.replayIntegration(),
],
beforeSend(event, hint) {
// Filter meaningless errors
if (event.exception?.values?.[0]?.type === 'ResizeObserver loop limit exceeded') {
return null;
}
return event;
},
});
React Router Integration
const SentryRoutes = Sentry.withSentryReactRouterV6Routing(Routes);
function App() {
return (
<SentryRoutes>
<Route path="/" element={<Home />} />
<Route path="/dashboard" element={<Dashboard />} />
</SentryRoutes>
);
}
Manual Reporting and Breadcrumbs
// Manual business exception reporting
Sentry.captureException(new Error('Order payment timeout'), {
tags: { orderId: 'ORD-12345', paymentMethod: 'alipay' },
extra: { retryCount: 3, lastAttempt: Date.now() },
});
// Add breadcrumbs (user behavior trail)
Sentry.addBreadcrumb({
category: 'ui.click',
message: 'Clicked submit button',
level: 'info',
});
Building a Lightweight Monitoring System
If you don't want to depend on third-party services, you can build your own:
Data Collection SDK
class ErrorMonitor {
constructor(options) {
this.endpoint = options.endpoint;
this.queue = [];
this.timer = null;
this.install();
}
install() {
window.addEventListener('error', this.handleError.bind(this), true);
window.addEventListener('unhandledrejection', this.handleRejection.bind(this));
}
handleError(event) {
this.report({
type: event.target instanceof HTMLElement ? 'resource' : 'runtime',
message: event.message || event.target?.outerHTML,
stack: event.error?.stack,
timestamp: Date.now(),
url: location.href,
ua: navigator.userAgent,
});
}
handleRejection(event) {
this.report({
type: 'unhandledRejection',
message: event.reason?.message || String(event.reason),
stack: event.reason?.stack,
timestamp: Date.now(),
});
}
report(data) {
this.queue.push(data);
if (!this.timer) {
this.timer = setTimeout(() => this.flush(), 5000);
}
}
flush() {
if (this.queue.length === 0) return;
const batch = this.queue.splice(0);
navigator.sendBeacon(this.endpoint, JSON.stringify(batch));
this.timer = null;
}
}
Key Metric Dashboard
| Metric | Calculation | Alert Threshold |
|---|---|---|
| JS Error Rate | Error PV / Total PV | > 0.1% |
| API Failure Rate | 5xx count / Total requests | > 1% |
| First Screen Crash Rate | Blank screen PV / Total PV | > 0.01% |
| Mean Time to Repair | First alert to deployment time | > 30min |
Best Practices Checklist
- Full coverage with four defense lines: onerror + error event + unhandledrejection + Error Boundary
- No SourceMap on production: Separate build artifacts, deobfuscate in CI environment
- Error sampling and filtering: Prevent high-frequency meaningless errors from drowning out real issues
- User behavior replay: Breadcrumbs + Session Replay to accelerate localization
- Alert tiering: P0 immediate notification, P1 ticket tracking, P2 weekly report summary
- Version association: Each release carries a version number, errors auto-associated with code changes
- Privacy compliance: Sanitize reported data, do not include user personal information
- Performance correlation: Link error monitoring with performance monitoring to discover root causes of performance degradation
Try these browser-local tools — no sign-up required →
#错误监控#Sentry#SourceMap#异常捕获#DevOps