Skip to content

Sentry — Error Tracking & Performance Monitoring Guide

DodaTech Updated 2026-06-24 6 min read

In this tutorial, you'll learn about Sentry. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.

Sentry is an open-source error tracking and performance monitoring platform that helps developers see, triage, and resolve errors and performance issues in real time across their entire software stack.

What You'll Learn

Why It Matters

Users experiencing errors rarely report them. They silently leave your application or write a negative review. Sentry captures every error automatically — frontend JavaScript exceptions, backend API crashes, mobile app crashes — with full stack traces, user context, and breadcrumbs leading up to the error. DodaTech reduced unhandled error resolution time by 85% after integrating Sentry across all applications.

Real-World Use

DodaZIP's desktop application uses Sentry's native SDK to capture crashes with full minidump analysis. When a user's file compression crashes, Sentry captures the crash thread, loaded modules, and OS information — enabling the team to fix the issue within hours of the first report.

flowchart TD
    A[Application Error] --> B[Sentry SDK]
    B --> C[Sentry Server]
    C --> D[Issue Grouping]
    C --> E[Release Tracking]
    C --> F[Source Maps]
    C --> G[Performance Traces]
    D --> H[Alert Rules]
    H --> I[Slack Notification]
    H --> I2[PagerDuty]
    H --> I3[Email]
    D --> J[Issue Dashboard]
    J --> K[Assign to Developer]
    K --> L[Fix & Deploy]
    L --> M[Resolve in Sentry]
    style C fill:#362D59,color:#fff
â„šī¸ Info

Prerequisites: A Sentry account (sentry.io or self-hosted). Basic knowledge of JavaScript or your target language.

Installation

# Self-hosted Sentry (using getsentry/self-hosted)
git clone https://github.com/getsentry/self-hosted.git
cd self-hosted
sudo ./install.sh

# Expected output:
# Checking minimum requirements...
# Creating volumes...
# Building images...
# Creating services...
# Installation complete.
# Your Sentry server is ready! Visit http://your-ip:9000

# Create DSN for your project
# Settings > Projects > Your Project > DSN
# DSN format: https://publicKey@o123456.ingest.sentry.io/654321

JavaScript/Node.js Setup

// sentry.js — Sentry initialization
const Sentry = require('@sentry/node');
const { nodeProfilingIntegration } = require('@sentry/profiling-node');

Sentry.init({
  dsn: 'https://publicKey@o123456.ingest.sentry.io/654321',
  environment: 'production',
  release: 'dodazip@2.5.0',
  dist: '1',

  // Performance monitoring
  tracesSampleRate: 0.2,
  profilesSampleRate: 0.1,

  // Send errors from all transactions
  errorSampleRate: 1.0,

  integrations: [
    nodeProfilingIntegration(),
  ],

  // Capture unhandled rejections
  captureUnhandledRejections: true,

  // Denylist sensitive data
  denyUrls: [
    /https?:\/\/localhost/,
    /https?:\/\/.*\.local/,
  ],

  beforeSend(event) {
    if (event.user) {
      delete event.user.email;
      delete event.user.ip_address;
    }
    return event;
  },

  // Sampling for performance
  tracesSampler(samplingContext) {
    if (samplingContext.request?.url?.includes('/health')) {
      return 0;
    }
    if (samplingContext.parentSampled) {
      return 1;
    }
    return 0.2;
  },
});

Capturing Errors

// Automatic error capturing
// Express error handler
const express = require('express');
const app = express();

// RequestHandler creates a separate trace per request
app.use(Sentry.Handlers.requestHandler());
app.use(Sentry.Handlers.tracingHandler());

app.get('/api/users/:id', async (req, res) => {
  try {
    const user = await db.query('SELECT * FROM users WHERE id = $1', [req.params.id]);
    if (!user) {
      throw new Error('User not found');
    }
    res.json(user);
  } catch (error) {
    // Sentry automatically captures in error handler
    Sentry.captureException(error, {
tags: {
        endpoint: 'GET /api/users/:id',
        user_id: req.params.id,
      },
      extra: {
        query: req.query,
        headers: req.headers,
      },
      user: {
        id: req.user?.id,
        username: req.user?.username,
      },
    });
    res.status(500).json({ error: 'Internal server error' });
  }
});

// The error handler middleware
app.use(Sentry.Handlers.errorHandler());

// Manual error capture
Sentry.captureMessage('Database migration completed', {
  level: 'info',
tags: { migration: 'v2.5.0' },
});

// Set user context
Sentry.setUser({
  id: 'user-123',
  username: 'jdoe',
  email: 'jdoe@dodatech.com',
});

// Add breadcrumbs for debugging
Sentry.addBreadcrumb({
  category: 'auth',
  message: 'User authenticated',
  level: 'info',
  data: {
    auth_method: 'OAuth2',
    provider: 'GitHub',
  },
});

React Frontend Setup

// frontend/sentry.js
import * as Sentry from '@sentry/react';
import { BrowserTracing } from '@sentry/react';
import { createRoutesFromChildren, matchRoutes, useLocation, useNavigationType } from 'react-router-dom';

Sentry.init({
  dsn: 'https://publicKey@o123456.ingest.sentry.io/654321',
  environment: 'production',
  release: 'dodazip-frontend@3.1.0',

  integrations: [
    new BrowserTracing({
      routingInstrumentation: Sentry.reactRouterV6Instrumentation(
        React.useEffect,
        useLocation,
        useNavigationType,
        createRoutesFromChildren,
        matchRoutes
      ),
    }),
    new Sentry.Replay({
      maskAllText: false,
      maskAllInputs: false,
      blockAllMedia: false,
    }),
  ],

  tracesSampleRate: 0.1,
  replaysSessionSampleRate: 0.1,
  replaysOnErrorSampleRate: 1.0,
});
// Sentry Error Boundary
import { SentryErrorBoundary } from '@sentry/react';

function App() {
  return (
    <SentryErrorBoundary fallback={({ error, componentStack }) => (
      <div>
        <h2>Something went wrong</h2>
        <p>{error.message}</p>
        <button onClick={() => window.location.reload()}>
          Reload page
        </button>
      </div>
    )}>
      <MainContent />
    </SentryErrorBoundary>
  );
}

Release Tracking

# .github/workflows/sentry-release.yml
name: Create Sentry Release

on:
  push:
    branches: [main]

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Create Sentry release
        uses: getsentry/action-release@v1
        env:
          SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
          SENTRY_ORG: dodatech
          SENTRY_PROJECT: user-service
        with:
          environment: production
          version: dodazip@${{ github.sha }}
          sourcemaps: ./dist
          url_prefix: ~/static/js

Performance Monitoring

// Custom transaction
const transaction = Sentry.startTransaction({
  name: 'User Registration',
  op: 'registration',
  data: {
    method: 'email',
  },
});

Sentry.configureScope(scope => scope.setSpan(transaction));

try {
  // Child span: validation
  const validationSpan = transaction.startChild({
    op: 'validation',
    description: 'Validate user input',
  });
  validateInput(userData);
  validationSpan.finish();

  // Child span: database
  const dbSpan = transaction.startChild({
    op: 'db',
    description: 'Create user in database',
  });
  await createUser(userData);
  dbSpan.finish();

  // Child span: email
  const emailSpan = transaction.startChild({
    op: 'email',
    description: 'Send welcome email',
  });
  await sendEmail(userData.email);
  emailSpan.finish();

  transaction.setStatus('ok');
} catch (error) {
  transaction.setStatus('internal_error');
  Sentry.captureException(error);
} finally {
  transaction.finish();
}

Common Configuration Mistakes

  1. Not uploading source maps: Without source maps, Sentry shows minified stack traces. Upload source maps in CI with sentry-cli releases files or the GitHub Action.

  2. Over-sampling performance traces: 100% trace sampling generates excessive events (cost and rate limits). Use 0.1-0.2 for high-traffic services and 1.0 only during debugging.

  3. Ignoring beforeSend for PII filtering: Sentry captures request bodies and error details which may contain PII. Use beforeSend to redact sensitive fields.

  4. Not configuring release tracking: Without releases, you cannot track which version introduced an error. Set release in Sentry.init() and create releases in CI.

  5. Missing environment isolation: Development errors contaminate production error data. Set separate DSNs or use environment to distinguish dev, staging, and production errors.

Practice Questions

  1. What is a Sentry DSN? Answer: The Data Source Name (DSN) is a unique URL that identifies your project and tells the Sentry SDK where to send events. It includes the project key and ingestion endpoint.

  2. How does Sentry group similar errors? Answer: Sentry uses fingerprinting — it groups errors with the same stack trace, error type, and message into a single issue. Fingerprints can be customized with beforeSend or fingerprint callback.

  3. What is the purpose of source maps in Sentry? Answer: Source maps map minified JavaScript back to original source code, allowing Sentry to display readable stack traces with file names and line numbers.

  4. How does Sentry Replay help debug frontend issues? Answer: Session Replay captures a video-like recording of user interactions (clicks, scrolls, navigation) leading up to an error, helping developers reproduce the issue.

Challenge

Integrate Sentry across a full-stack application: initialize the Node.js SDK for backend error and performance tracking, configure error handlers for Express routes, upload source maps for a React frontend with Sentry Error Boundary and Browser Tracing, create and associate Sentry releases with CI pipeline, set up alert rules for critical errors to Slack and PagerDuty, configure custom fingerprinting for known non-actionable errors, and verify that all errors include user context, breadcrumbs, and request data.

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro