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

  1. Full coverage with four defense lines: onerror + error event + unhandledrejection + Error Boundary
  2. No SourceMap on production: Separate build artifacts, deobfuscate in CI environment
  3. Error sampling and filtering: Prevent high-frequency meaningless errors from drowning out real issues
  4. User behavior replay: Breadcrumbs + Session Replay to accelerate localization
  5. Alert tiering: P0 immediate notification, P1 ticket tracking, P2 weekly report summary
  6. Version association: Each release carries a version number, errors auto-associated with code changes
  7. Privacy compliance: Sanitize reported data, do not include user personal information
  8. 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