Skip to content
Back to skills

Sentry Sdk Patterns

ASecurity

'Best practices for using Sentry SDK in TypeScript and Python.

  • 2,719 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 21, 2026
ai-agentsjavascripttypescriptpythongojavareactnodeexpressdjangoapi

Works with

  • claude code
  • cli
  • api

Security analysis

A100/100

Pro scans all 4 files and shows the line behind each finding

Scanned September 21, 2026

npx -y skills add jeremylongshore/claude-code-plugins-plus --skill sentry-sdk-patterns --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Sentry Sdk Patterns?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Sentry Sdk Patterns
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/jeremylongshore-sentry-sdk-patterns-dd3d9842/badge)](https://www.skillsdirectory.com/skills/jeremylongshore-sentry-sdk-patterns-dd3d9842)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: sentry-sdk-patterns
description: 'Best practices for using Sentry SDK in TypeScript and Python.

  Use when implementing structured error context with scopes, breadcrumb

  strategies, beforeSend/beforeBreadcrumb filtering, custom fingerprinting,

  user context, or performance span creation.

  Trigger: "sentry best practices", "sentry patterns", "sentry sdk usage",

  "sentry scope", "sentry breadcrumbs", "sentry beforeSend", "sentry fingerprint".

  '
allowed-tools: Read, Write, Edit, Grep
version: 1.51.0
license: MIT
author: Jeremy Longshore <jeremy@intentsolutions.io>
tags:
- saas
- sentry
- python
- typescript
- best-practices
- error-handling
compatibility: Designed for Claude Code
---
# Sentry SDK Patterns

## Overview

Production patterns for `@sentry/node` (v8+) and `sentry-sdk` (Python 2.x+) covering scoped error context, breadcrumb strategies, event filtering with `beforeSend`, custom fingerprinting for issue grouping, and performance instrumentation with spans. All examples use real Sentry SDK APIs.

## Prerequisites

- Sentry SDK v8+ installed (`@sentry/node`, `@sentry/react`, or `sentry-sdk`)
- `SENTRY_DSN` environment variable configured
- Familiarity with async/await (TypeScript) or context managers (Python)

## Instructions

### Step 1 -- Structured Error Context with Scopes

Use `Sentry.withScope()` (TypeScript) or `sentry_sdk.new_scope()` (Python) to attach context to individual events without leaking state across requests.

**TypeScript -- Scoped error capture:**

```typescript
import * as Sentry from '@sentry/node';

type ErrorSeverity = 'low' | 'medium' | 'high' | 'critical';

interface ErrorOptions {
  severity?: ErrorSeverity;
  tags?: Record<string, string>;
  context?: Record<string, unknown>;
  user?: { id: string; email?: string };
  fingerprint?: string[];
}

const SEVERITY_MAP: Record<ErrorSeverity, Sentry.SeverityLevel> = {
  low: 'info',
  medium: 'warning',
  high: 'error',
  critical: 'fatal',
};

export function captureError(error: Error, options: ErrorOptions = {}) {
  Sentry.withScope((scope) => {
    scope.setLevel(SEVERITY_MAP[options.severity || 'medium']);

    if (options.tags) {
      Object.entries(options.tags).forEach(([key, value]) => {
        scope.setTag(key, value);
      });
    }
    if (options.context) {
      scope.setContext('app', options.context);
    }
    if (options.user) {
      scope.setUser(options.user);
    }
    if (options.fingerprint) {
      scope.setFingerprint(options.fingerprint);
    }

    Sentry.captureException(error);
  });
}
```

**Python -- Scoped error capture:**

```python
import sentry_sdk

def capture_error(error, severity="error", tags=None, context=None, user=None):
    """Capture exception with isolated scope context."""
    with sentry_sdk.new_scope() as scope:
        scope.set_level(severity)
        if tags:
            for key, value in tags.items():
                scope.set_tag(key, value)
        if context:
            scope.set_context("app", context)
        if user:
            scope.set_user(user)
        sentry_sdk.capture_exception(error)
```

**Key rule:** Never call `Sentry.setTag()` or `sentry_sdk.set_tag()` at the module level inside request handlers. Those mutate the global scope and leak between concurrent requests. Always use `withScope()` or `new_scope()`.

### Step 2 -- Breadcrumbs, Filtering, and Fingerprints

#### Structured breadcrumb helpers

```typescript
import * as Sentry from '@sentry/node';

export const breadcrumb = {
  auth(action: string, userId?: string) {
    Sentry.addBreadcrumb({
      category: 'auth',
      message: `${action}${userId ? ` for user ${userId}` : ''}`,
      level: 'info',
    });
  },

  db(operation: string, table: string, durationMs?: number) {
    Sentry.addBreadcrumb({
      category: 'db',
      message: `${operation} on ${table}`,
      level: 'info',
      data: { table, operation, ...(durationMs && { duration_ms: durationMs }) },
    });
  },

  http(method: string, url: string, status: number) {
    Sentry.addBreadcrumb({
      category: 'http',
      message: `${method} ${url} -> ${status}`,
      level: status >= 400 ? 'warning' : 'info',
      data: { method, url, status_code: status },
    });
  },
};
```

**Python breadcrumbs:**

```python
sentry_sdk.add_breadcrumb(
    category="auth", message="User logged in",
    level="info", data={"user_id": user_id, "method": "oauth"},
)
```

#### beforeSend -- Drop noise, scrub PII

```typescript
Sentry.init({
  dsn: process.env.SENTRY_DSN,
  beforeSend(event, hint) {
    const error = hint?.originalException;
    // Drop non-actionable errors
    if (error instanceof Error) {
      if (error.message.includes('ResizeObserver loop')) return null;
      if (error.message.includes('Network request failed')) return null;
    }
    // Scrub PII from user context
    if (event.user) {
      delete event.user.ip_address;
      delete event.user.email;
    }
    return event;
  },
});
```

**Python beforeSend:**

```python
def before_send(event, hint):
    if "exc_info" in hint:
        exc_type, exc_value, tb = hint["exc_info"]
        if isinstance(exc_value, (KeyboardInterrupt, SystemExit)):
            return None
    if "user" in event:
        event["user"].pop("email", None)
        event["user"].pop("ip_address", None)
    return event

sentry_sdk.init(dsn=os.environ["SENTRY_DSN"], before_send=before_send)
```

#### beforeBreadcrumb -- Filter noisy breadcrumbs

```typescript
Sentry.init({
  dsn: process.env.SENTRY_DSN,
  beforeBreadcrumb(breadcrumb, hint) {
    // Drop console.log breadcrumbs in production
    if (breadcrumb.category === 'console' && breadcrumb.level === 'log') {
      return null;
    }
    // Redact auth tokens from HTTP breadcrumbs
    if (breadcrumb.category === 'fetch' && breadcrumb.data?.url) {
      const url = new URL(breadcrumb.data.url);
      url.searchParams.delete('token');
      breadcrumb.data.url = url.toString();
    }
    return breadcrumb;
  },
});
```

#### Custom fingerprints for issue grouping

Override default stack-trace grouping when the same root cause produces different stacks:

```typescript
Sentry.withScope((scope) => {
  // Group all payment gateway timeouts together
  scope.setFingerprint(['payment-gateway-timeout', gatewayName]);
  Sentry.captureException(error);
});
```

```python
with sentry_sdk.new_scope() as scope:
    scope.fingerprint = ["payment-gateway-timeout", gateway_name]
    sentry_sdk.capture_exception(error)
```

### Step 3 -- Framework Integration and Performance Spans

#### Express middleware (Sentry v8)

```typescript
import * as Sentry from '@sentry/node';
import express from 'express';

const app = express();

// Sentry v8: register error handler
Sentry.setupExpressErrorHandler(app);

// Request context middleware (register BEFORE routes)
app.use((req, res, next) => {
  Sentry.setUser({ id: req.user?.id, ip_address: req.ip });
  Sentry.addBreadcrumb({
    category: 'http',
    message: `${req.method} ${req.path}`,
    data: { query: req.query, params: req.params },
  });
  next();
});
```

#### React Error Boundary

```tsx
import * as Sentry from '@sentry/react';

const SentryErrorBoundary = Sentry.withErrorBoundary(App, {
  fallback: ({ error, resetError }) => (
    <div>
      <h2>Something went wrong</h2>
      <button onClick={resetError}>Try again</button>
    </div>
  ),
  beforeCapture: (scope) => {
    scope.setTag('location', 'error-boundary');
    scope.setLevel('fatal');
  },
});
```

#### Performance spans (TypeScript)

```typescript
async function processOrder(orderId: string) {
  return Sentry.startSpan(
    { name: 'processOrder', op: 'task', attributes: { orderId } },
    async (span) => {
      const order = await Sentry.startSpan(
        { name: 'db.getOrder', op: 'db.query' },
        () => db.orders.findById(orderId),
      );
      await Sentry.startSpan(
        { name: 'payment.charge', op: 'http.client' },
        () => chargePayment(order),
      );
      span.setStatus({ code: 1, message: 'ok' });
      return order;
    },
  );
}
```

#### Performance spans (Python)

```python
import sentry_sdk
from functools import wraps

def sentry_traced(op="function"):
    """Decorator to wrap functions in Sentry spans."""
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            with sentry_sdk.start_span(op=op, name=func.__name__):
                return func(*args, **kwargs)
        return wrapper
    return decorator

@sentry_traced(op="db.query")
def get_user(user_id: str):
    return db.users.find_one({"_id": user_id})
```

#### Async batch processing with error isolation

```typescript
async function processItems(items: Item[]) {
  const results = await Promise.allSettled(
    items.map((item) =>
      Sentry.startSpan({ name: `process.${item.type}`, op: 'task' }, () =>
        processItem(item),
      ),
    ),
  );

  const failures = results.filter(
    (r): r is PromiseRejectedResult => r.status === 'rejected',
  );

  if (failures.length > 0) {
    Sentry.withScope((scope) => {
      scope.setTag('batch_size', String(items.length));
      scope.setTag('failure_count', String(failures.length));
      Sentry.captureMessage(`${failures.length}/${items.length} items failed`, 'warning');
    });
    failures.forEach((f) => Sentry.captureException(f.reason));
  }
}
```

See [implementation.md](references/implementation.md) for Django middleware, test mocking patterns, and additional framework examples.

## Output

After applying these patterns you will have:

- Centralized error handler module with typed severity and scoped context
- Structured breadcrumb helpers for auth, db, and http events
- `beforeSend` filter that drops noise and scrubs PII
- `beforeBreadcrumb` callback that redacts sensitive query parameters
- Custom fingerprinting for accurate issue grouping
- Framework error boundaries for Express and React
- Performance spans for tracing critical code paths

## Error Handling

| Error | Cause | Solution |
|-------|-------|----------|
| Scope leaking between requests | Global scope mutations in async handlers | Use `withScope()` / `new_scope()` for per-event context |
| Duplicate events | Error caught and re-thrown at two layers | Capture at one level only -- middleware or handler, not both |
| Missing breadcrumbs | Cleared after max count (default 100) | Set `maxBreadcrumbs` in `Sentry.init()` |
| `beforeSend` returns `undefined` | Missing return statement | Always return `event` or `null` explicitly |
| Events grouped incorrectly | Default stack-trace fingerprinting | Use `scope.setFingerprint()` with semantic keys |
| `Sentry is not defined` | SDK not imported | Verify `import * as Sentry from '@sentry/node'` |
| Spans not appearing | Missing tracing config | Set `tracesSampleRate` in `Sentry.init()` |

## Examples

**Centralized error handler:** Create `lib/error-handler.ts` wrapping `Sentry.withScope()` with typed severity, tags, context, user, and fingerprint support.

**Breadcrumb trail for checkout:** Add `breadcrumb.auth('login')`, `breadcrumb.db('SELECT', 'orders')`, `breadcrumb.http('POST', '/api/payments', 201)` before critical operations so errors include full context timeline.

**Noise filtering:** Configure `beforeSend` to drop `ResizeObserver loop` and `Network request failed`, scrub PII from user context and cookies.

**Fix issue grouping:** Add `scope.setFingerprint(['payment-gateway-timeout', gatewayName])` to group all payment timeouts by gateway.

See [examples.md](references/examples.md) for full worked scenarios with Python context managers and async wrappers.

## Resources

- [Sentry JavaScript SDK Best Practices](https://docs.sentry.io/platforms/javascript/best-practices/)
- [Scopes and Context](https://docs.sentry.io/platforms/javascript/enriching-events/scopes/)
- [Express Integration Guide](https://docs.sentry.io/platforms/javascript/guides/express/)
- [Python SDK Documentation](https://docs.sentry.io/platforms/python/)
- [Custom Fingerprinting](https://docs.sentry.io/platforms/javascript/enriching-events/fingerprinting/)
- [Performance Monitoring](https://docs.sentry.io/platforms/javascript/tracing/)

## Next Steps

- **sentry-error-capture** -- Deep dive on `captureException` vs `captureMessage` semantics
- **sentry-performance-tracing** -- Full distributed tracing with `tracesSampleRate` and custom instrumentation
- **sentry-data-handling** -- PII scrubbing, data residency, and GDPR-compliant configuration
- **sentry-common-errors** -- Troubleshooting guide for frequent SDK issues

Files in this skill

  • SKILL.md12.3 KB
  • references/errors.md1.3 KB
  • references/examples.md2.5 KB
  • references/implementation.md5.4 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.