--- name: sentry-setup-metrics description: Setup Sentry Metrics in any project. Use this when asked to add Sentry metrics, track custom metrics, setup counters/gauges/distributions, or instrument application performance metrics. Supports JavaScript, TypeScript, Python, React, Next.js, and Node.js. allowed-tools: Read, Edit, Write, Bash, Grep, Glob, AskUserQuestion --- # Setup Sentry Metrics This skill helps configure Sentry's custom metrics feature to track counters, gauges, and distributions across your applications. ## When to Use This Skill Invoke this skill when: - User asks to "setup Sentry metrics" or "add custom metrics" - User wants to "track metrics in Sentry" - User requests "counters", "gauges", or "distributions" with Sentry - User mentions they want to track business KPIs or application health - User asks about `Sentry.metrics` or `sentry_sdk.metrics` - User wants to instrument performance metrics ## Platform Support **Supported Platforms:** - JavaScript/TypeScript (SDK 10.25.0+): Next.js, React, Node.js, Browser - Python (SDK 2.44.0+): Django, Flask, FastAPI, general Python **Note:** Ruby does not currently have dedicated metrics support in the Sentry SDK. ## Metric Types Overview Before setup, explain the three metric types to the user: | Type | Purpose | Use Cases | Aggregations | |------|---------|-----------|--------------| | **Counter** | Track cumulative occurrences | Button clicks, API calls, errors | sum, per_second, per_minute | | **Gauge** | Point-in-time snapshots | Queue depth, memory usage, connections | min, max, avg | | **Distribution** | Statistical analysis of values | Response times, cart amounts, query duration | p50, p75, p95, p99, avg, min, max | ## Platform Detection ### JavaScript/TypeScript Detection Check for these files: - `package.json` - Read to identify framework and Sentry SDK version - Look for `@sentry/nextjs`, `@sentry/react`, `@sentry/node`, `@sentry/browser` - Check if SDK version is 10.25.0+ (required for metrics) ### Python Detection Check for: - `requirements.txt`, `pyproject.toml`, `setup.py`, or `Pipfile` - Look for `sentry-sdk` version 2.44.0+ (required for metrics) ## Required Information Ask the user: ``` I'll help you set up Sentry Metrics. First, let me check your project setup. After detecting the platform: 1. **What metrics do you want to track?** - Counters: Event counts (clicks, API calls, errors) - Gauges: Point-in-time values (queue depth, memory) - Distributions: Value analysis (response times, amounts) 2. **Do you need metric filtering?** - Yes: Configure beforeSendMetric to filter/modify metrics - No: Send all metrics as-is ``` --- ## JavaScript/TypeScript Configuration ### Minimum SDK Version - All JS platforms: `10.25.0+` ### Step 1: Verify SDK Version ```bash grep -E '"@sentry/(nextjs|react|node|browser)"' package.json ``` If version is below 10.25.0, inform user to upgrade: ```bash npm install @sentry/nextjs@latest # or appropriate package ``` ### Step 2: Verify Metrics Are Enabled Metrics are **enabled by default** in SDK 10.25.0+. No changes to `Sentry.init()` are required unless user wants to disable or filter. **Locate init files:** - Next.js: `instrumentation-client.ts`, `sentry.server.config.ts`, `sentry.edge.config.ts` - React: `src/index.tsx`, `src/main.tsx`, or dedicated sentry config file - Node.js: Entry point file or dedicated sentry config - Browser: Entry point or config file **Optional: Explicitly enable (not required):** ```javascript import * as Sentry from "@sentry/nextjs"; // or @sentry/react, @sentry/node Sentry.init({ dsn: "YOUR_DSN_HERE", // Metrics enabled by default, but can be explicit enableMetrics: true, // ... other existing config }); ``` ### Step 3: Add Metric Filtering (Optional) If user wants to filter metrics before sending: ```javascript Sentry.init({ dsn: "YOUR_DSN_HERE", beforeSendMetric: (metric) => { // Drop metrics with sensitive attributes if (metric.attributes?.sensitive === true) { return null; } // Remove specific attribute before sending if (metric.attributes?.internal) { delete metric.attributes.internal; } return metric; }, }); ``` ### Step 4: Disable Metrics (If Requested) If user explicitly wants to disable metrics: ```javascript Sentry.init({ dsn: "YOUR_DSN_HERE", enableMetrics: false, }); ``` --- ## JavaScript Metrics API Examples Provide these examples to the user based on their use case: ### Counter Examples ```javascript // Basic counter - increment by 1 Sentry.metrics.count("button_click", 1); // Counter with attributes for filtering/grouping Sentry.metrics.count("api_call", 1, { attributes: { endpoint: "/api/users", method: "GET", status_code: 200, }, }); // Counter for errors Sentry.metrics.count("checkout_error", 1, { attributes: { error_type: "payment_declined", payment_provider: "stripe", }, }); // Counter for business events Sentry.metrics.count("email_sent", 1, { attributes: { template: "welcome", recipient_type: "new_user", }, }); ``` ### Gauge Examples ```javascript // Basic gauge Sentry.metrics.gauge("queue_depth", 42); // Memory usage gauge Sentry.metrics.gauge("memory_usage", process.memoryUsage().heapUsed, { unit: "byte", attributes: { process: "main", }, }); // Connection pool gauge Sentry.metrics.gauge("db_connections", 15, { attributes: { pool: "primary", max_connections: 100, }, }); // Active users gauge Sentry.metrics.gauge("active_users", currentUserCount, { attributes: { region: "us-east", }, }); ``` ### Distribution Examples ```javascript // Response time distribution Sentry.metrics.distribution("response_time", 187.5, { unit: "millisecond", attributes: { endpoint: "/api/products", method: "GET", }, }); // Cart value distribution Sentry.metrics.distribution("cart_value", 149.99, { unit: "usd", attributes: { customer_tier: "premium", }, }); // Query duration distribution Sentry.metrics.distribution("db_query_duration", 45.2, { unit: "millisecond", attributes: { query_type: "select", table: "users", }, }); // File size distribution Sentry.metrics.distribution("upload_size", 2048576, { unit: "byte", attributes: { file_type: "image", }, }); ``` ### Manual Flush ```javascript // Force pending metrics to send immediately await Sentry.flush(); // Useful before process exit or after critical operations process.on("beforeExit", async () => { await Sentry.flush(); }); ``` --- ## Python Configuration ### Minimum SDK Version - `sentry-sdk` version `2.44.0+` ### Step 1: Verify SDK Version ```bash pip show sentry-sdk | grep Version ``` If version is below 2.44.0: ```bash pip install --upgrade sentry-sdk ``` ### Step 2: Verify Metrics Are Enabled Metrics are **enabled by default** in SDK 2.44.0+. No changes required unless filtering is needed. **Common init locations:** - Django: `settings.py` - Flask: `app.py` or `__init__.py` - FastAPI: `main.py` - General: Entry point or dedicated config file ### Step 3: Add Metric Filtering (Optional) ```python import sentry_sdk def before_send_metric(metric, hint): # Drop metrics with specific attributes if metric.get("attributes", {}).get("sensitive"): return None # Modify metric before sending if metric.get("attributes", {}).get("internal"): del metric["attributes"]["internal"] return metric sentry_sdk.init( dsn="YOUR_DSN_HERE", before_send_metric=before_send_metric, ) ``` --- ## Python Metrics API Examples ### Counter Examples ```python import sentry_sdk # Basic counter sentry_sdk.metrics.count("button_click", 1) # Counter with attributes sentry_sdk.metrics.count( "api_call", 1, attributes={ "endpoint": "/api/users", "method": "GET", "status_code": 200, } ) # Counter for errors sentry_sdk.metrics.count( "checkout_error", 1, attributes={ "error_type": "payment_declined", "payment_provider": "stripe", } ) # Business event counter sentry_sdk.metrics.count( "email_sent", 5, # Can increment by more than 1 attributes={ "template": "newsletter", "batch_id": "batch_123", } ) ``` ### Gauge Examples ```python import sentry_sdk import psutil # Basic gauge sentry_sdk.metrics.gauge("queue_depth", 42) # Memory usage gauge sentry_sdk.metrics.gauge( "memory_usage", psutil.virtual_memory().used, unit="byte", attributes={"host": "web-01"} ) # Database connection gauge sentry_sdk.metrics.gauge( "db_connections", connection_pool.size(), attributes={ "pool": "primary", "max": connection_pool.max_size, } ) # Cache hit rate gauge sentry_sdk.metrics.gauge( "cache_hit_rate", 0.85, attributes={"cache": "redis"} ) ``` ### Distribution Examples ```python import sentry_sdk import time # Response time distribution start = time.time() # ... do work ... duration_ms = (time.time() - start) * 1000 sentry_sdk.metrics.distribution( "response_time", duration_ms, unit="millisecond", attributes={ "endpoint": "/api/products", "method": "GET", } ) # Order value distribution sentry_sdk.metrics.distribution( "order_value", order.total, unit="usd", attributes={ "customer_tier": customer.tier, "payment_method": order.payment_method, } ) # Query duration distribution sentry_sdk.metrics.distribution( "db_query_duration", query_time_ms, unit="millisecond", attributes={ "query_type": "select", "table": "orders", } ) ``` --- ## Framework-Specific Notes ### Next.js - Metrics work on both client and server - Consider tracking in API routes and server components - Use attributes to distinguish client vs server metrics ### React (Browser) - Track user interactions (clicks, form submissions) - Monitor component render performance - Be mindful of metric volume from client-side ### Node.js - Track API response times, queue depths, background job metrics - Use gauges for resource monitoring - Flush metrics before process exit ### Django - Track view response times - Monitor database query performance - Use middleware for automatic request metrics ### Flask/FastAPI - Track endpoint performance - Monitor external API call durations - Use decorators or middleware for automatic instrumentation --- ## Common Units Use these units for better readability in Sentry dashboard: | Category | Units | |----------|-------| | **Time** | `nanosecond`, `microsecond`, `millisecond`, `second`, `minute`, `hour`, `day`, `week` | | **Size** | `bit`, `byte`, `kilobyte`, `megabyte`, `gigabyte`, `terabyte` | | **Currency** | `usd`, `eur`, `gbp` (or any ISO currency code) | | **Rate** | `ratio`, `percent` | | **Other** | `none` (default), or custom string | --- ## Best Practices ### DO Use Metrics For: - Business KPIs (orders, signups, revenue) - Application health (error rates, latency) - Resource utilization (queue depth, connections) - User actions (clicks, page views, feature usage) ### DON'T Use Metrics For: - Infrastructure monitoring (use dedicated tools) - Log aggregation (use Sentry Logs instead) - Full request tracing (use Sentry Tracing) - High-cardinality data in attributes ### Naming Conventions: ``` # Good - descriptive, namespaced api.request.duration checkout.cart.value user.signup.completed # Avoid - vague, inconsistent duration value1 myMetric ``` ### Attribute Guidelines: - Keep cardinality low (avoid user IDs, request IDs) - Use consistent attribute names across metrics - Include relevant context for filtering - Avoid sensitive data in attributes --- ## Instrumentation Patterns ### Timing Helper (JavaScript) ```javascript async function withTiming(name, fn, attributes = {}) { const start = performance.now(); try { return await fn(); } finally { const duration = performance.now() - start; Sentry.metrics.distribution(name, duration, { unit: "millisecond", attributes, }); } } // Usage const result = await withTiming( "api.external.duration", () => fetch("https://api.example.com/data"), { service: "example-api" } ); ``` ### Timing Decorator (Python) ```python import functools import time import sentry_sdk def track_duration(metric_name, **extra_attrs): def decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): start = time.time() try: return func(*args, **kwargs) finally: duration_ms = (time.time() - start) * 1000 sentry_sdk.metrics.distribution( metric_name, duration_ms, unit="millisecond", attributes=extra_attrs, ) return wrapper return decorator # Usage @track_duration("db.query.duration", query_type="user_lookup") def get_user_by_id(user_id): return db.query(User).filter(User.id == user_id).first() ``` ### Request Middleware (Express/Node.js) ```javascript function metricsMiddleware(req, res, next) { const start = performance.now(); res.on("finish", () => { const duration = performance.now() - start; Sentry.metrics.distribution("http.request.duration", duration, { unit: "millisecond", attributes: { method: req.method, route: req.route?.path || req.path, status_code: res.statusCode, }, }); Sentry.metrics.count("http.request.count", 1, { attributes: { method: req.method, status_code: res.statusCode, }, }); }); next(); } app.use(metricsMiddleware); ``` ### Django Middleware ```python import time import sentry_sdk class SentryMetricsMiddleware: def __init__(self, get_response): self.get_response = get_response def __call__(self, request): start = time.time() response = self.get_response(request) duration_ms = (time.time() - start) * 1000 sentry_sdk.metrics.distribution( "http.request.duration", duration_ms, unit="millisecond", attributes={ "method": request.method, "path": request.path, "status_code": response.status_code, }, ) return response # Add to MIDDLEWARE in settings.py ``` --- ## Verification Steps After setup, provide these verification instructions: ### JavaScript Verification ```javascript // Add this temporarily to test Sentry.metrics.count("test_metric", 1, { attributes: { test: true }, }); Sentry.metrics.gauge("test_gauge", 42); Sentry.metrics.distribution("test_distribution", 100, { unit: "millisecond", }); // Force flush to send immediately await Sentry.flush(); ``` ### Python Verification ```python import sentry_sdk sentry_sdk.metrics.count("test_metric", 1, attributes={"test": True}) sentry_sdk.metrics.gauge("test_gauge", 42) sentry_sdk.metrics.distribution("test_distribution", 100, unit="millisecond") ``` Tell user to check their Sentry dashboard under **Metrics** section to verify metrics are arriving. --- ## Common Issues and Solutions ### Issue: Metrics not appearing in Sentry **Solutions:** 1. Verify SDK version meets minimum requirements (JS: 10.25.0+, Python: 2.44.0+) 2. Ensure metrics aren't disabled (`enableMetrics: false`) 3. Check DSN is correct 4. Wait a few minutes - metrics are buffered before sending 5. Try manual flush: `await Sentry.flush()` or check aggregation window ### Issue: Too many metrics being sent **Solutions:** 1. Use `beforeSendMetric` to filter unnecessary metrics 2. Reduce attribute cardinality 3. Sample metrics on high-traffic paths 4. Aggregate client-side before sending ### Issue: High cardinality warnings **Solutions:** 1. Avoid user IDs, request IDs, timestamps in attributes 2. Use categories instead of specific values 3. Limit unique attribute value combinations ### Issue: Metrics not linked to traces **Solutions:** 1. Ensure tracing is enabled alongside metrics 2. Emit metrics within traced spans 3. Check that trace sampling isn't dropping related traces --- ## Summary Checklist Provide this checklist after setup: ```markdown ## Sentry Metrics Setup Complete ### Configuration Applied: - [ ] SDK version verified (JS: 10.25.0+, Python: 2.44.0+) - [ ] Metrics enabled (default) or explicitly configured - [ ] Metric filtering configured (if requested) ### Instrumentation Added: - [ ] Counter metrics for events/actions - [ ] Gauge metrics for point-in-time values - [ ] Distribution metrics for timing/values - [ ] Appropriate units specified - [ ] Meaningful attributes added ### Next Steps: 1. Run your application 2. Trigger actions that emit metrics 3. Check Sentry dashboard > Metrics section 4. Create dashboards and alerts based on metrics ``` --- ## Quick Reference | Platform | SDK Version | API Namespace | |----------|-------------|---------------| | JavaScript | 10.25.0+ | `Sentry.metrics.*` | | Python | 2.44.0+ | `sentry_sdk.metrics.*` | | Method | JavaScript | Python | |--------|------------|--------| | Counter | `Sentry.metrics.count(name, value, options)` | `sentry_sdk.metrics.count(name, value, **kwargs)` | | Gauge | `Sentry.metrics.gauge(name, value, options)` | `sentry_sdk.metrics.gauge(name, value, **kwargs)` | | Distribution | `Sentry.metrics.distribution(name, value, options)` | `sentry_sdk.metrics.distribution(name, value, **kwargs)` | | Option | JavaScript | Python | |--------|------------|--------| | Unit | `{ unit: "millisecond" }` | `unit="millisecond"` | | Attributes | `{ attributes: { key: "value" } }` | `attributes={"key": "value"}` |