From sentry-pack
Verifies Sentry production setup in Node.js apps: DSN config, env tags, releases, source maps, alerts, PII scrubbing, and error testing with @sentry/node and sentry-cli.
npx claudepluginhub jeremylongshore/claude-code-plugins-plus-skills --plugin sentry-packThis skill is limited to using the following tools:
Walk through every production-critical Sentry configuration item before a deploy — SDK init options, source map uploads, alert routing, PII scrubbing, sample rate tuning, and test error verification. Covers `@sentry/node` (v8+) and `sentry-cli` workflows.
Configures Sentry for dev, staging, and prod with separate DSNs, env-specific sample rates, per-env alerts, and dashboard filtering. For @sentry/node (TS) and sentry-sdk (Python).
Checks and configures Sentry error tracking SDKs for frontend, Next.js, Node.js, and Python projects including DSN env vars, source maps, and CI/CD release tracking.
Sets up full Sentry SDK for Node.js, Bun, and Deno runtimes with error monitoring, tracing, logging, profiling, metrics, crons, and AI monitoring for server-side JS/TS apps.
Share bugs, ideas, or general feedback.
Walk through every production-critical Sentry configuration item before a deploy — SDK init options, source map uploads, alert routing, PII scrubbing, sample rate tuning, and test error verification. Covers @sentry/node (v8+) and sentry-cli workflows.
Use when:
@sentry/node (or framework-specific SDK like @sentry/nextjs, @sentry/react) installedsentry-cli installed globally or as a devDependency (npm i -D @sentry/cli)SENTRY_AUTH_TOKEN with scope project:releases available in CI environmentWork through each section in order. Check off each item as you verify it.
// CORRECT — DSN from environment
Sentry.init({
dsn: process.env.SENTRY_DSN,
});
// WRONG — hardcoded DSN leaks project ID and org info
Sentry.init({
dsn: 'https://abc123@o456.ingest.sentry.io/789',
});
Verify with: grep -r "ingest.sentry.io" src/ --include="*.ts" --include="*.js" — should return zero results.
SENTRY_DSN set in production environment (not just .env.local)SENTRY_ORG and SENTRY_PROJECT set for CLI operationsenvironment tag set to 'production'Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: process.env.NODE_ENV || 'production',
});
This enables environment-scoped alert rules and release health filtering. Without it, all events land in the default (empty) environment.
release set to match the deploy versionSentry.init({
dsn: process.env.SENTRY_DSN,
environment: 'production',
release: process.env.SENTRY_RELEASE || `myapp@${process.env.npm_package_version}`,
});
The release value must match exactly what you pass to sentry-cli releases new. Mismatches break source map resolution and release health tracking.
sampleRate tuned — not 1.0 in high-traffic productionSentry.init({
// For most apps: 0.1 to 0.25 captures enough to spot trends
// without burning through your event quota
sampleRate: 0.25, // 25% of errors captured
// For low-traffic apps or critical services, 1.0 is fine
// sampleRate: 1.0,
});
| Traffic level | Recommended sampleRate |
|---|---|
| < 10K errors/day | 1.0 |
| 10K-100K errors/day | 0.25 |
| > 100K errors/day | 0.1 |
tracesSampleRate tuned (0.05-0.2 for production)Sentry.init({
tracesSampleRate: 0.1, // 10% of transactions traced
// Or use tracesSampler for endpoint-specific rates
tracesSampler: (samplingContext) => {
const name = samplingContext.transactionContext?.name || '';
// Never trace health checks
if (name.includes('/health') || name.includes('/ready')) return 0;
// Always trace payments
if (name.includes('/checkout') || name.includes('/payment')) return 1.0;
// Default: 10%
return 0.1;
},
});
Never ship tracesSampleRate: 1.0 in production — it generates massive event volume and will exhaust your quota within hours on any real workload.
sentry-cli releases files#!/bin/bash
# Run in CI after build, before deploy
set -euo pipefail
VERSION="${SENTRY_RELEASE:-$(git rev-parse --short HEAD)}"
# Create the release
sentry-cli releases new "$VERSION"
# Associate commits for suspect commits feature
sentry-cli releases set-commits "$VERSION" --auto
# Upload source maps from build output
sentry-cli releases files "$VERSION" upload-sourcemaps ./dist \
--url-prefix '~/static/js' \
--validate
# Mark release as deployed
sentry-cli releases finalize "$VERSION"
sentry-cli releases deploys "$VERSION" new -e production
--url-prefix matches the path where assets are served (e.g., ~/static/js or ~/assets)--validate flag is used to catch malformed maps at build time.gitignore or strip from deploy artifact)Verify upload succeeded:
sentry-cli releases files "$VERSION" list
# Should show .js and .js.map files with correct paths
beforeSend Noise FilterbeforeSend configured to drop noiseSentry.init({
beforeSend(event, hint) {
const error = hint?.originalException;
const message = typeof error === 'string' ? error : error?.message || '';
// Drop browser noise that is never actionable
const noise = [
'ResizeObserver loop',
'Non-Error promise rejection captured',
/Loading chunk \d+ failed/,
'Network request failed',
'AbortError',
'TypeError: cancelled',
'TypeError: Failed to fetch',
];
for (const pattern of noise) {
if (pattern instanceof RegExp ? pattern.test(message) : message.includes(pattern)) {
return null; // Drop this event
}
}
// Scrub auth headers if they leak into request context
if (event.request?.headers) {
delete event.request.headers['Authorization'];
delete event.request.headers['Cookie'];
delete event.request.headers['X-API-Key'];
}
return event;
},
});
sendDefaultPii: falseSentry.init({
sendDefaultPii: false, // Do NOT send cookies, user IPs, or auth headers
// If you need user context, set it explicitly with scrubbed data
// Sentry.setUser({ id: user.id }); // ID only, no email/name
});
Also verify in Sentry project settings:
ssn, creditCard)Set up these minimum alert rules in Alerts > Create Alert Rule:
| Alert | Type | Condition | Action |
|---|---|---|---|
| New issue in production | Issue | First seen, environment:production | Slack channel + email |
| Regression detected | Issue | Regressed, environment:production | Slack channel |
| Error spike | Metric | Error count > N in 5 min | PagerDuty (on-call) |
| P95 latency breach | Metric | p95(transaction.duration) > threshold | Slack #performance |
| Crash-free rate drop | Release health | Crash-free sessions < 99% | Email + Slack |
Sentry.flush() called before process exitFor serverless functions, CLI tools, or any short-lived process, events may be lost if the process exits before the SDK flushes its queue:
// AWS Lambda / serverless
import * as Sentry from '@sentry/node';
export const handler = Sentry.wrapHandler(async (event) => {
// Your handler logic
return { statusCode: 200 };
});
// CLI tool / script
async function main() {
try {
await doWork();
} catch (err) {
Sentry.captureException(err);
await Sentry.flush(2000); // 2000ms timeout — enough for network round-trip
process.exit(1);
}
}
For long-running servers (Express, Fastify), this is handled automatically — skip this step.
// Send a test error after deploy
Sentry.captureException(new Error('Production deploy verification — safe to ignore'));
// Or from CLI
// node -e "
// const Sentry = require('@sentry/node');
// Sentry.init({ dsn: process.env.SENTRY_DSN, environment: 'production' });
// Sentry.captureMessage('Deploy verification test');
// Sentry.flush(2000).then(() => console.log('Test event sent')); // 2s flush timeout
// "
After sending, verify in the Sentry dashboard:
environment:productionIn Sentry dashboard under Settings > Inbound Filters, enable:
These filters drop events server-side before they count against your quota, unlike beforeSend which drops client-side after the network request.
Run this after completing all checklist items:
#!/bin/bash
# scripts/verify-sentry-prod.sh
set -euo pipefail
echo "=== Sentry Production Verification ==="
PASS=0; FAIL=0
check() {
if eval "$2" > /dev/null 2>&1; then
echo " PASS: $1"; ((PASS++))
else
echo " FAIL: $1"; ((FAIL++))
fi
}
# Environment variables
for var in SENTRY_DSN SENTRY_ORG SENTRY_PROJECT SENTRY_AUTH_TOKEN; do
check "$var is set" "[ -n \"\${$var:-}\" ]"
done
# CLI authentication
check "sentry-cli authenticated" "sentry-cli info"
# Source maps uploaded
RELEASE="${SENTRY_RELEASE:-$(git rev-parse --short HEAD)}"
check "Source maps for $RELEASE" \
"[ \$(sentry-cli releases files \"$RELEASE\" list 2>/dev/null | wc -l) -gt 1 ]"
# Network connectivity
check "sentry.io reachable" \
"[ \$(curl -s -o /dev/null -w '%{http_code}' https://sentry.io/api/0/) = '200' ]"
# No hardcoded DSN in source
check "No hardcoded DSN in src/" \
"! grep -r 'ingest.sentry.io' src/ --include='*.ts' --include='*.js' --include='*.tsx' 2>/dev/null | grep -v node_modules"
echo ""
echo "=== Results: $PASS passed, $FAIL failed ==="
[ "$FAIL" -eq 0 ] && echo "All checks passed." || exit 1
After completing this checklist, you will have:
Sentry.init() with tuned sample rates and PII scrubbingsentry-cli for the current releasebeforeSend filtering known browser noise before it burns quota| Error | Cause | Solution |
|---|---|---|
| Stack traces show minified code | Source maps missing or --url-prefix wrong | Re-upload with correct prefix; run sentry-cli sourcemaps explain to diagnose |
| Events not appearing in dashboard | Wrong DSN or network blocked | Verify SENTRY_DSN is the production DSN; check firewall allows *.ingest.sentry.io on HTTPS |
release mismatch warning | SDK release differs from CLI release | Ensure Sentry.init({ release }) matches sentry-cli releases new $VERSION exactly |
| Events from wrong environment | environment not set in SDK init | Explicitly set environment: 'production' — do not rely on defaults |
| Excessive event volume / quota exhausted | sampleRate: 1.0 on high-traffic service | Lower to 0.1-0.25; add beforeSend filters; enable server-side inbound filters |
| Alerts not firing | Alert rules scoped to wrong environment | Edit alert: filter to environment:production; test with Sentry.captureMessage() |
| Events lost in serverless / CLI | Process exits before flush | Call await Sentry.flush(2000) before process.exit() or use Sentry.wrapHandler() |
beforeSend dropping real errors | Overly broad noise filter | Audit filter patterns; log dropped events to a secondary sink during rollout |
Example 1: Full production init (Node.js / Express)
import * as Sentry from '@sentry/node';
Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: 'production',
release: process.env.SENTRY_RELEASE,
debug: false,
sendDefaultPii: false,
sampleRate: 0.25,
tracesSampleRate: 0.1,
maxBreadcrumbs: 50,
attachStacktrace: true,
ignoreErrors: [
'ResizeObserver loop',
'Non-Error promise rejection captured',
/Loading chunk \d+ failed/,
],
beforeSend(event, hint) {
if (event.request?.headers) {
delete event.request.headers['Authorization'];
delete event.request.headers['Cookie'];
}
return event;
},
tracesSampler: (ctx) => {
const name = ctx.transactionContext?.name || '';
if (name.includes('/health')) return 0;
if (name.includes('/payment')) return 1.0;
return 0.1;
},
});
Example 2: CI source map upload (GitHub Actions)
# .github/workflows/deploy.yml
- name: Upload source maps to Sentry
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: my-org
SENTRY_PROJECT: my-project
VERSION: ${{ github.sha }}
run: |
npx sentry-cli releases new "$VERSION"
npx sentry-cli releases set-commits "$VERSION" --auto
npx sentry-cli releases files "$VERSION" upload-sourcemaps ./dist \
--url-prefix '~/static/js' --validate
npx sentry-cli releases finalize "$VERSION"
npx sentry-cli releases deploys "$VERSION" new -e production
Example 3: Serverless flush pattern (AWS Lambda)
import * as Sentry from '@sentry/node';
Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: 'production',
release: process.env.SENTRY_RELEASE,
tracesSampleRate: 0.2,
});
export const handler = Sentry.wrapHandler(async (event, context) => {
// wrapHandler automatically calls flush() on completion
const result = await processEvent(event);
return { statusCode: 200, body: JSON.stringify(result) };
});
new, set-commits, finalize, deploysAfter completing this production checklist:
@sentry/browser) — capture user sessions for faster debugging