Sentry Advanced Features: Performance Monitoring, Profiling, and Session Replay
Most developers know Sentry as an error tracker. When an exception is thrown, Sentry captures it, groups it with similar errors, and notifies the team. This basic use case is valuable, but it covers only a fraction of what Sentry can do.
Sentry's platform has expanded significantly in recent years to include performance monitoring with distributed tracing, continuous profiling, session replay, and cron monitoring. For QA engineers and developers building quality into their release process, these capabilities extend Sentry from a reactive debugging tool into a comprehensive production quality platform.
This guide focuses on Sentry's advanced features: performance monitoring, profiling, session replay, and release health — assuming you already have basic error tracking configured.
Performance Monitoring with Transactions
Sentry's performance monitoring is built around transactions — units of work that represent a user request, a background job, or any operation you want to measure end-to-end.
A transaction captures:
- Duration of the full operation
- Spans for each sub-operation (database queries, HTTP requests, template rendering)
- The relationship between operations (parent-child span hierarchy)
- Custom data and tags
Browser automatic instrumentation:
import * as Sentry from '@sentry/browser';
import { BrowserTracing } from '@sentry/tracing';
Sentry.init({
dsn: 'YOUR_DSN',
integrations: [
new BrowserTracing({
tracePropagationTargets: ['localhost', 'yourapp.com'],
}),
new Sentry.Replay(),
],
tracesSampleRate: 0.1, // Sample 10% of transactions
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0, // Always replay sessions with errors
});The BrowserTracing integration automatically creates transactions for page loads and navigation events. Every XHR/Fetch request becomes a span within the current transaction, giving you a waterfall view of everything that happened during a page load.
Node.js:
const Sentry = require('@sentry/node');
const { ProfilingIntegration } = require('@sentry/profiling-node');
Sentry.init({
dsn: 'YOUR_DSN',
integrations: [
new ProfilingIntegration(),
],
tracesSampleRate: 0.1,
profilesSampleRate: 0.1, // Sample 10% of transactions for profiling
});
// Express - automatic transaction creation
const app = require('express')();
app.use(Sentry.Handlers.requestHandler());
app.use(Sentry.Handlers.tracingHandler());
// ... routes ...
app.use(Sentry.Handlers.errorHandler());Custom transactions and spans:
// Manual transaction for operations that aren't auto-instrumented
const transaction = Sentry.startTransaction({
op: 'checkout',
name: 'Checkout Flow',
});
Sentry.configureScope(scope => scope.setSpan(transaction));
try {
// Create child span for each sub-operation
const cartSpan = transaction.startChild({
op: 'db.query',
description: 'Fetch cart items',
});
const cartItems = await fetchCart(userId);
cartSpan.finish();
const paymentSpan = transaction.startChild({
op: 'http.client',
description: 'POST /api/stripe/charge',
});
const result = await processPayment(cartItems);
paymentSpan.finish();
transaction.setStatus('ok');
} catch (error) {
transaction.setStatus('error');
Sentry.captureException(error);
} finally {
transaction.finish();
}Distributed Tracing
When your application is distributed across multiple services, a single user request might span your frontend, API gateway, authentication service, and database. Sentry's distributed tracing follows a request across service boundaries, giving you a complete picture of what happened.
For distributed tracing to work, trace context must propagate between services via HTTP headers:
// Sentry automatically adds trace headers to outgoing requests
// when tracePropagationTargets is configured
Sentry.init({
dsn: 'YOUR_DSN',
integrations: [new BrowserTracing()],
tracePropagationTargets: [
'localhost',
/^https:\/\/api\.yourapp\.com/,
],
});
// Each service initialized with Sentry creates spans that
// automatically attach to the trace from the calling serviceIn the Sentry UI, distributed traces appear as a combined waterfall: you can follow a single user request from the browser's page load through the API call, the database query, the cache lookup, and the response — all in one trace view.
For QA, distributed tracing answers questions like: "This API endpoint is slow — is it the query or the downstream service it's calling?" And: "This error appears in the API service — was it triggered by a specific request pattern from the frontend?"
Continuous Profiling
Profiling captures a statistical sample of your application's call stack over time, showing you where CPU time is being spent. Sentry's profiling product captures this data in production and correlates it with transaction data.
The difference from APM-style profiling: Sentry collects profile data from actual production transactions, not synthetic benchmarks. You see where time is spent in the code paths your users actually exercise.
// Python (requires sentry-sdk >= 1.18.0)
import sentry_sdk
from sentry_sdk.integrations.django import DjangoIntegration
sentry_sdk.init(
dsn='YOUR_DSN',
integrations=[DjangoIntegration()],
traces_sample_rate=0.1,
profiles_sample_rate=0.1, # Profile 10% of sampled transactions
)In the Sentry UI, the flame graph shows function call hierarchies with time attribution. A function that takes 80% of a transaction's time while your users wait is immediately visible. This is particularly valuable for QA performance testing: you can confirm that performance optimizations in code are actually reflected in production profiling data.
Session Replay
Sentry's Session Replay integrates with error tracking to show you the user session context around every error. When an error occurs, you can watch the session replay to see exactly what the user was doing — which is often more informative than the stack trace alone.
Configuration (shown above in the init call) requires deciding on sample rates:
replaysSessionSampleRate: Percentage of all sessions to record (for proactive browsing)replaysOnErrorSampleRate: Percentage of sessions with errors to record (typically 1.0 — record all error sessions)
Sentry's session replay captures DOM mutations and user interactions, similar to dedicated tools like LogRocket. The advantage is the tight integration with error data: every Sentry error links directly to the replay session where it occurred, without any separate configuration.
For privacy, Sentry masks all text content in replays by default. You can configure masking behavior:
Sentry.init({
integrations: [
new Sentry.Replay({
maskAllText: true, // Default: true
blockAllMedia: false,
mask: ['.sensitive-field'], // CSS selectors for additional masking
unmask: ['.public-content'], // Unmask specific elements
}),
],
});Release Health and Crash-Free Rate
Sentry's Release Health tracks a crash-free sessions rate for each release — the percentage of sessions that contain no crashes or errors. This is analogous to Bugsnag's stability score.
// Associate sessions with releases
Sentry.init({
dsn: 'YOUR_DSN',
release: process.env.SENTRY_RELEASE, // Usually set to git SHA or version tag
autoSessionTracking: true, // Default: true
});The Release Comparison view shows crash-free rate across releases on a timeline. A deployment that causes a regression in crash-free rate is immediately visible.
Creating releases in CI/CD:
# Using the Sentry CLI
export SENTRY_AUTH_TOKEN=your-auth-token
export SENTRY_ORG=your-org
export SENTRY_PROJECT=your-project
# Create release
sentry-cli releases new $SENTRY_RELEASE
# Upload source maps
sentry-cli releases files $SENTRY_RELEASE upload-sourcemaps ./dist \
--url-prefix '~/static/dist/' \
--rewrite
# Mark as deployed
sentry-cli releases deploys $SENTRY_RELEASE new -e production
# Finalize
sentry-cli releases finalize $SENTRY_RELEASEGitHub Actions:
- name: Create Sentry release
uses: getsentry/action-release@v1
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
with:
environment: production
sourcemaps: './dist'Cron Monitoring
Sentry's Cron Monitoring tracks whether scheduled jobs are running on schedule and completing successfully. For QA, this provides monitoring coverage for background tasks that aren't exercised by frontend-initiated test flows.
// Node.js cron job instrumentation
const Sentry = require('@sentry/node');
const checkInId = Sentry.captureCheckIn(
{
monitorSlug: 'daily-report-generator',
status: 'in_progress',
},
{
schedule: {
type: 'crontab',
value: '0 6 * * *', // 6am daily
},
checkinMargin: 5, // Minutes before alert if not checked in
maxRuntime: 30, // Minutes before alert if not completed
timezone: 'UTC',
}
);
try {
await generateDailyReport();
Sentry.captureCheckIn({
checkInId: checkInId,
monitorSlug: 'daily-report-generator',
status: 'ok',
});
} catch (error) {
Sentry.captureCheckIn({
checkInId: checkInId,
monitorSlug: 'daily-report-generator',
status: 'error',
});
throw error;
}Cron monitors alert you when a job doesn't check in within its expected window (missed execution) or takes longer than expected (timeout). These failure modes are often invisible to standard error tracking, since the job either doesn't run at all or gets stuck rather than throwing an exception.
Performance Thresholds and Alerts
Sentry allows configuring performance alerts in addition to error alerts:
- Alert when p95 transaction duration exceeds a threshold
- Alert when error rate for a transaction exceeds a percentage
- Alert when Apdex score (user satisfaction metric) drops below a threshold
These thresholds become part of your deployment validation checklist. After deploying, verify that performance alerts haven't fired and that transaction duration percentiles are within expected ranges for critical flows.
Sampling Strategy
At scale, sampling is necessary — capturing 100% of transactions is expensive. Sentry's SDK supports dynamic sampling:
Sentry.init({
tracesSampler: (samplingContext) => {
// Always sample checkout transactions
if (samplingContext.transactionContext.name.includes('checkout')) {
return 1.0;
}
// Sample high-priority user segments at higher rates
if (samplingContext.request?.headers?.['x-user-plan'] === 'enterprise') {
return 0.5;
}
// Default rate for everything else
return 0.05;
},
});For QA purposes, you can set higher sampling rates in staging than production to ensure full visibility during testing, without incurring production-scale costs.
Integrating Sentry's Full Platform into QA
The value of Sentry's expanded platform is the connected view across error types, performance, and user experience:
- Error in the error tracking dashboard → click to see the replay session → see what the user was doing
- Slow transaction in performance monitoring → click to see the trace waterfall → identify the slow span → click to see the profiling data for that transaction
- Release deployed → check release health dashboard → monitor crash-free rate change → investigate any new error groups
This connected view reduces context switching between tools and makes it faster to answer the core questions after any deployment: Did we break anything? Did we make anything slower?