Setup Sentry Tracing (Performance Monitoring) in any project. Use this when asked to add performance monitoring, enable tracing, track transactions/spans, or instrument application performance. Supports JavaScript, TypeScript, Python, Ruby, React, Next.js, and Node.js.
npx claudepluginhub troykelly/claude-skills --plugin issue-driven-developmentThis skill is limited to using the following tools:
This skill helps configure Sentry's Tracing (Performance Monitoring) to track application performance, measure latency, and create distributed traces across services.
Configures Sentry SDK v8+ for performance monitoring, distributed tracing, sampling, and profiling in Node.js/TypeScript and Python apps.
Sets up Sentry performance monitoring with transactions, spans, distributed tracing from browser to Node.js, and sampling configurations.
Instruments apps with OpenTelemetry for distributed tracing and Jaeger/Tempo integration. Debugs latency in microservices, analyzes request flows, correlates traces with logs/metrics.
Share bugs, ideas, or general feedback.
This skill helps configure Sentry's Tracing (Performance Monitoring) to track application performance, measure latency, and create distributed traces across services.
Invoke this skill when:
tracesSampleRate or custom spansBefore configuring, detect the project's platform:
Check package.json for:
@sentry/nextjs - Next.js@sentry/react - React@sentry/node - Node.js@sentry/browser - Browser/vanilla JS@sentry/vue - Vue@sentry/angular - Angular@sentry/sveltekit - SvelteKitCheck for sentry-sdk in requirements
Check for sentry-ruby in Gemfile
Explain these concepts to the user:
| Concept | Description |
|---|---|
| Trace | Complete journey of a request across services |
| Transaction | Single instance of a service being called (root span) |
| Span | Individual unit of work within a transaction |
| Sample Rate | Percentage of transactions to capture (0-1) |
Find the Sentry.init() call:
instrumentation-client.ts, sentry.server.config.ts, sentry.edge.config.tssrc/index.tsx or entry fileimport * as Sentry from "@sentry/react"; // or @sentry/browser
Sentry.init({
dsn: "YOUR_DSN_HERE",
// Add browser tracing integration
integrations: [Sentry.browserTracingIntegration()],
// Set sample rate (1.0 = 100% for testing, lower for production)
tracesSampleRate: 1.0,
// Configure which URLs receive trace headers for distributed tracing
tracePropagationTargets: [
"localhost",
/^https:\/\/yourserver\.io\/api/,
],
});
Client (instrumentation-client.ts):
import * as Sentry from "@sentry/nextjs";
Sentry.init({
dsn: "YOUR_DSN_HERE",
tracesSampleRate: 1.0,
// Browser tracing is automatic in @sentry/nextjs
});
Server (sentry.server.config.ts):
import * as Sentry from "@sentry/nextjs";
Sentry.init({
dsn: "YOUR_DSN_HERE",
tracesSampleRate: 1.0,
});
Edge (sentry.edge.config.ts):
import * as Sentry from "@sentry/nextjs";
Sentry.init({
dsn: "YOUR_DSN_HERE",
tracesSampleRate: 1.0,
});
Next.js 14+ App Router Requirement:
For distributed tracing in App Router, add trace data to root layout metadata:
// app/layout.tsx
import * as Sentry from "@sentry/nextjs";
export async function generateMetadata() {
return {
other: {
...Sentry.getTraceData(),
},
};
}
const Sentry = require("@sentry/node");
Sentry.init({
dsn: "YOUR_DSN_HERE",
tracesSampleRate: 1.0,
});
Sentry.init({
// Capture 20% of all transactions
tracesSampleRate: 0.2,
});
Sentry.init({
tracesSampler: ({ name, attributes, parentSampled }) => {
// Always skip health checks
if (name.includes("healthcheck")) {
return 0;
}
// Always capture auth transactions
if (name.includes("auth")) {
return 1;
}
// Sample comments at 1%
if (name.includes("comment")) {
return 0.01;
}
// Inherit parent sampling decision if available
if (typeof parentSampled === "boolean") {
return parentSampled;
}
// Default: 50%
return 0.5;
},
});
Note: If both tracesSampleRate and tracesSampler are set, tracesSampler takes precedence.
The browserTracingIntegration() accepts many configuration options:
Sentry.init({
integrations: [
Sentry.browserTracingIntegration({
// Trace propagation targets (which URLs get trace headers)
tracePropagationTargets: ["localhost", /^https:\/\/api\./],
// Modify spans before creation (e.g., parameterize URLs)
beforeStartSpan: (context) => {
return {
...context,
name: context.name.replace(/\/users\/\d+/, "/users/:id"),
};
},
// Filter unwanted spans
shouldCreateSpanForRequest: (url) => {
return !url.includes("healthcheck");
},
// Timing configurations
idleTimeout: 1000, // ms before finishing idle spans
finalTimeout: 30000, // max span duration
childSpanTimeout: 15000, // max child span duration
// Feature toggles
instrumentNavigation: true, // Track URL changes
instrumentPageLoad: true, // Track initial page load
enableLongTask: true, // Track long tasks
enableInp: true, // Track Interaction to Next Paint
// INP sampling (separate from tracesSampleRate)
interactionsSampleRate: 1.0,
}),
],
});
import sentry_sdk
sentry_sdk.init(
dsn="YOUR_DSN_HERE",
traces_sample_rate=1.0, # 100% for testing
)
sentry_sdk.init(
dsn="YOUR_DSN_HERE",
traces_sample_rate=0.2, # 20% of transactions
)
def traces_sampler(sampling_context):
transaction_name = sampling_context.get("transaction_context", {}).get("name", "")
if "healthcheck" in transaction_name:
return 0
if "auth" in transaction_name:
return 1.0
# Inherit from parent if available
if sampling_context.get("parent_sampled") is not None:
return sampling_context["parent_sampled"]
return 0.5
sentry_sdk.init(
dsn="YOUR_DSN_HERE",
traces_sampler=traces_sampler,
)
Sentry.init do |config|
config.dsn = "YOUR_DSN_HERE"
config.traces_sample_rate = 1.0 # 100% for testing
end
Sentry.init do |config|
config.dsn = "YOUR_DSN_HERE"
config.traces_sampler = lambda do |sampling_context|
transaction_name = sampling_context[:transaction_context][:name]
return 0 if transaction_name.include?("healthcheck")
return 1.0 if transaction_name.include?("auth")
0.5 # Default 50%
end
end
// Synchronous operation
const result = Sentry.startSpan(
{ name: "expensive-calculation", op: "function" },
() => {
return calculateSomething();
}
);
// Async operation
const result = await Sentry.startSpan(
{ name: "fetch-user-data", op: "http.client" },
async () => {
const response = await fetch("/api/user");
return response.json();
}
);
// With attributes
const result = await Sentry.startSpan(
{
name: "process-order",
op: "task",
attributes: {
"order.id": orderId,
"order.amount": amount,
},
},
async () => {
return processOrder(orderId);
}
);
await Sentry.startSpan({ name: "checkout-flow", op: "transaction" }, async () => {
// Child span 1
await Sentry.startSpan({ name: "validate-cart", op: "validation" }, async () => {
await validateCart();
});
// Child span 2
await Sentry.startSpan({ name: "process-payment", op: "payment" }, async () => {
await processPayment();
});
// Child span 3
await Sentry.startSpan({ name: "send-confirmation", op: "email" }, async () => {
await sendConfirmationEmail();
});
});
function middleware(req, res, next) {
return Sentry.startSpanManual({ name: "middleware", op: "middleware" }, (span) => {
res.once("finish", () => {
span.setStatus({ code: res.statusCode < 400 ? 1 : 2 });
span.end();
});
return next();
});
}
let checkoutSpan;
// Start inactive span
function onStartCheckout() {
checkoutSpan = Sentry.startInactiveSpan({ name: "checkout-flow" });
Sentry.setActiveSpanInBrowser(checkoutSpan);
}
// End when done
function onCompleteCheckout() {
checkoutSpan?.end();
}
import sentry_sdk
@sentry_sdk.trace
def expensive_function():
# Automatically creates a span
return do_work()
# With parameters (SDK 2.35.0+)
@sentry_sdk.trace(op="database", name="fetch-users")
def fetch_users():
return db.query(User).all()
import sentry_sdk
def process_order(order_id):
with sentry_sdk.start_span(name="process-order", op="task") as span:
span.set_data("order.id", order_id)
# Nested span
with sentry_sdk.start_span(name="validate-order", op="validation"):
validate(order_id)
with sentry_sdk.start_span(name="charge-payment", op="payment"):
charge(order_id)
return {"success": True}
import sentry_sdk
with sentry_sdk.start_transaction(op="task", name="batch-process") as transaction:
for item in items:
with sentry_sdk.start_span(name=f"process-item-{item.id}"):
process(item)
transaction.set_tag("items_processed", len(items))
sentry_sdk.init(
dsn="YOUR_DSN_HERE",
traces_sample_rate=1.0,
functions_to_trace=[
{"qualified_name": "myapp.services.process_order"},
{"qualified_name": "myapp.services.send_notification"},
],
)
Sentry propagates trace context via HTTP headers:
sentry-trace: Contains trace ID, span ID, sampling decisionbaggage: Contains additional trace metadataOnly URLs matching these patterns receive trace headers:
Sentry.init({
tracePropagationTargets: [
"localhost",
"https://api.yourapp.com",
/^https:\/\/.*\.yourapp\.com\/api/,
],
});
For SSR apps, inject trace data in HTML:
// Server renders these meta tags
const traceData = Sentry.getTraceData();
// Include in HTML <head>:
// <meta name="sentry-trace" content="..." />
// <meta name="baggage" content="..." />
The browser SDK automatically reads these and continues the trace.
For non-HTTP channels (WebSockets, message queues):
// Sender
const traceData = Sentry.getTraceData();
sendMessage({
...payload,
_traceHeaders: traceData,
});
// Receiver
Sentry.continueTrace(
{
sentryTrace: message._traceHeaders["sentry-trace"],
baggage: message._traceHeaders["baggage"],
},
() => {
processMessage(message);
}
);
Use consistent op values for better organization:
| Operation | Use Case |
|---|---|
http.client | Outgoing HTTP requests |
http.server | Incoming HTTP requests |
db | Database operations |
db.query | Database queries |
cache | Cache operations |
task | Background tasks |
function | Function execution |
ui.render | UI rendering |
ui.action | User interactions |
serialize | Serialization |
middleware | Middleware execution |
Important: Setting tracesSampleRate: 0 does NOT disable tracing - it still processes traces but never sends them.
To fully disable tracing, omit both sampling options:
Sentry.init({
dsn: "YOUR_DSN_HERE",
// Do NOT include tracesSampleRate or tracesSampler
});
| Traffic Level | Recommended Rate |
|---|---|
| Development/Testing | 1.0 (100%) |
| Low traffic (<1K req/min) | 0.5 - 1.0 |
| Medium traffic (1K-10K req/min) | 0.1 - 0.5 |
| High traffic (>10K req/min) | 0.01 - 0.1 |
Use dynamic sampling to capture more of important transactions:
tracesSampler: ({ name }) => {
// Always capture errors and slow endpoints
if (name.includes("checkout") || name.includes("payment")) {
return 1.0;
}
// Sample most at 10%
return 0.1;
},
After setup, verify tracing is working:
// Trigger a test transaction
await Sentry.startSpan(
{ name: "test-transaction", op: "test" },
async () => {
console.log("Tracing test");
await new Promise(resolve => setTimeout(resolve, 100));
}
);
with sentry_sdk.start_transaction(op="test", name="test-transaction"):
print("Tracing test")
Check in Sentry:
Solutions:
tracesSampleRate > 0 or tracesSampler returns > 0browserTracingIntegration() is addedSolutions:
tracePropagationTargets includes your API URLssentry-trace and baggage headersSolutions:
tracesSampleRatetracesSampler to filter by transaction nameshouldCreateSpanForRequest to skip health checksSolutions:
startSpan callback pattern (not manual)parentSpanIsAlwaysRootSpan: false## Sentry Tracing Setup Complete
### Configuration Applied:
- [ ] `tracesSampleRate` or `tracesSampler` configured
- [ ] Browser: `browserTracingIntegration()` added
- [ ] `tracePropagationTargets` configured for APIs
- [ ] Next.js App Router: `getTraceData()` in metadata
### Sampling Strategy:
- [ ] Development: 100% sampling for testing
- [ ] Production: Appropriate rate based on traffic
### Custom Instrumentation (if applicable):
- [ ] Critical paths instrumented with custom spans
- [ ] Consistent `op` values used
### Next Steps:
1. Trigger some requests in your application
2. Check Sentry > Performance for transactions
3. Review trace waterfalls for span hierarchy
4. Adjust sampling rates based on volume
| Platform | Enable Tracing | Custom Span |
|---|---|---|
| JS/Browser | tracesSampleRate + browserTracingIntegration() | Sentry.startSpan() |
| Next.js | tracesSampleRate (auto-integrated) | Sentry.startSpan() |
| Node.js | tracesSampleRate | Sentry.startSpan() |
| Python | traces_sample_rate | @sentry_sdk.trace or start_span() |
| Ruby | traces_sample_rate | start_span() |
| Sampling Option | Purpose |
|---|---|
tracesSampleRate | Uniform percentage (0-1) |
tracesSampler | Dynamic function (takes precedence) |