We use cookies to enhance your experience on the site
CodeWorlds

Error Logging and Monitoring

On a spacecraft, every failure is recorded in the black box. Without it, the crew couldn't analyze the causes of problems after the fact. In React applications, we have exactly the same problem -- if an error occurs for a user in production and we don't log it, we'll never know about it. Error logging and monitoring is your production black box.

Error Logging Strategies

In a production environment, we need several layers of logging:

1. console.error -- Only for Development

console.error
is useful during local development, but in production the user doesn't see it, and you don't have access to it.

1// Good for development, useless in production
2componentDidCatch(error, errorInfo) {
3  console.error('Error caught:', error);
4  console.error('Component stack:', errorInfo.componentStack);
5}

2. Logging to an External Service

In production, errors must be sent to a monitoring service:

1class ErrorBoundary extends React.Component {
2  componentDidCatch(error, errorInfo) {
3    // Send to monitoring service
4    logErrorToService({
5      message: error.message,
6      stack: error.stack,
7      componentStack: errorInfo.componentStack,
8      timestamp: new Date().toISOString(),
9      url: window.location.href,
10      userAgent: navigator.userAgent,
11    });
12  }
13
14  // ...render with fallback
15}
16
17async function logErrorToService(errorData) {
18  try {
19    await fetch('/api/errors', {
20      method: 'POST',
21      headers: { 'Content-Type': 'application/json' },
22      body: JSON.stringify(errorData),
23    });
24  } catch (e) {
25    // Fallback -- don't block the app if logging fails
26    console.error('Failed to log error:', e);
27  }
28}

Sentry and Error Tracking Services

Sentry is the most popular service for monitoring errors in JavaScript and React applications. Other popular tools include LogRocket, Bugsnag, Datadog, and New Relic.

Basic Sentry Integration

1import * as Sentry from '@sentry/react';
2
3// Initialization in main/index.js
4Sentry.init({
5  dsn: 'https://key@sentry.io/project',
6  environment: 'production',
7  // Percentage of sessions to record (performance)
8  tracesSampleRate: 0.2,
9});
10
11// Sentry automatically captures:
12// - Unhandled exceptions
13// - Unhandled Promise rejections
14// - Network errors

Sentry with Error Boundary

1import * as Sentry from '@sentry/react';
2
3// Sentry provides a ready-made Error Boundary
4function App() {
5  return (
6    <Sentry.ErrorBoundary
7      fallback={({ error }) => (
8        <div className="error-page">
9          <h2>Something went wrong</h2>
10          <p>The error has been automatically reported</p>
11          <button onClick={() => window.location.reload()}>
12            Refresh page
13          </button>
14        </div>
15      )}
16    >
17      <SpaceStation />
18    </Sentry.ErrorBoundary>
19  );
20}

Context and Breadcrumbs in Sentry

Sentry allows you to add context to errors -- information about the user, recent actions (breadcrumbs), and additional data:

1// Set user context
2Sentry.setUser({
3  id: user.id,
4  email: user.email,
5  username: user.callsign,
6});
7
8// Manually add breadcrumbs (user action trail)
9Sentry.addBreadcrumb({
10  category: 'navigation',
11  message: 'User navigated to mission panel',
12  level: 'info',
13});
14
15// Manually report an error with additional context
16Sentry.captureException(error, {
17  extra: {
18    missionId: currentMission.id,
19    moduleState: moduleStatus,
20  },
21});

Custom Error Logging Hook

You can create your own hook that centralizes logging logic:

1function useErrorLogger() {
2  const logError = useCallback((error, context = {}) => {
3    const errorReport = {
4      message: error.message,
5      stack: error.stack,
6      timestamp: new Date().toISOString(),
7      url: window.location.href,
8      ...context,
9    };
10
11    // In production, send to the service
12    if (process.env.NODE_ENV === 'production') {
13      sendToErrorService(errorReport);
14    }
15
16    // In development, log to console
17    console.error('[ErrorLogger]', errorReport);
18  }, []);
19
20  return { logError };
21}
22
23// Usage in a component
24function MissionControl() {
25  const { logError } = useErrorLogger();
26
27  const handleDataFetch = async () => {
28    try {
29      const data = await fetchMissionData();
30      setMissions(data);
31    } catch (error) {
32      logError(error, {
33        component: 'MissionControl',
34        action: 'fetchMissionData',
35      });
36      setError('Failed to fetch mission data');
37    }
38  };
39}

Error Reporting in Production

Global Error Handlers

In addition to Error Boundaries and try-catch, it's worth adding global handlers that catch errors escaping other mechanisms:

1// Global catching of uncaught errors
2window.addEventListener('error', (event) => {
3  logErrorToService({
4    type: 'uncaught_error',
5    message: event.message,
6    filename: event.filename,
7    lineno: event.lineno,
8    colno: event.colno,
9  });
10});
11
12// Global catching of unhandled Promise rejections
13window.addEventListener('unhandledrejection', (event) => {
14  logErrorToService({
15    type: 'unhandled_rejection',
16    message: event.reason?.message || 'Unknown rejection',
17    stack: event.reason?.stack,
18  });
19});

Error Severity Levels

Not all errors are equally important. Error tracking services allow classification:

1// Critical error - application is non-functional
2Sentry.captureException(error, { level: 'fatal' });
3
4// Error - functionality is broken, but app works
5Sentry.captureException(error, { level: 'error' });
6
7// Warning - something might be wrong
8Sentry.captureMessage('API responding slowly', 'warning');
9
10// Info - useful for debugging
11Sentry.captureMessage('User changed language', 'info');

Source Maps for Production Debugging

Production code is minified -- the stack trace is unreadable without source maps. Source maps are like a decoding map -- they allow you to translate positions in minified code back to the original source files.

1// Without source maps, the stack trace looks like:
2// Error at e.render (main.a1b2c3.js:1:45678)
3
4// With source maps:
5// Error at MissionControl.render (MissionControl.jsx:42:15)

Source Maps Configuration

1// webpack.config.js
2module.exports = {
3  devtool: 'source-map', // Generate source maps
4  // ...
5};
6
7// Important: source maps should NOT be public!
8// Upload them to Sentry, but don't serve them to users:
9// sentry-cli sourcemaps upload ./build

Source maps allow you to see exactly which line of original code caused the error, even if the production bundle is minified into a single line. It's like having the technical blueprint of a spacecraft -- instead of looking at an incomprehensible minified panel, you see exactly which module failed and why. Remember, however, to never make source maps publicly available -- that would be like sharing the ship's blueprints with potential invaders.

Go to CodeWorlds