What Metrics to Track
Practical guidance on what metrics to track, how to explore them, and when to set alerts.
You've set up Sentry Metrics. Now what?
This guide covers the high-value metric patterns that give you visibility into application health and how to drill into traces when something looks off.
Sentry supports three metric types:
| Type | Method | Use For |
|---|---|---|
| Counter | Sentry.metrics.count() | Events that happen (orders, clicks, errors) |
| Gauge | Sentry.metrics.gauge() | Current state (queue depth, connections) |
| Distribution | Sentry.metrics.distribution() | Values that vary (latency, sizes, amounts) |
Every metric is trace-connected. When a metric spikes, click into samples to see the exact trace that produced it.
Sentry.metrics.count("checkout.failed", 1, {
attributes: {
user_tier: "premium",
failure_reason: "payment_declined",
},
});
Sentry.metrics.count("checkout.failed", 1, {
attributes: {
user_tier: "premium",
failure_reason: "payment_declined",
},
});
import sentry_sdk
sentry_sdk.metrics.count("checkout.failed", 1, attributes={
"user_tier": "premium",
"failure_reason": "payment_declined"
})
\Sentry\traceMetrics()->count('checkout.failed', 1, [
'user_tier' => 'premium',
'failure_reason' => 'payment_declined',
]);
SentrySdk.Metrics.Counter("checkout.failed")
.Tag("user_tier", "premium")
.Tag("failure_reason", "payment_declined")
.Increment();
Sentry.metrics.count('checkout.failed', 1,
tags: { user_tier: 'premium', failure_reason: 'payment_declined' }
)
Sentry.metrics.count('checkout.failed', 1, attributes: {
'user_tier': 'premium',
'failure_reason': 'payment_declined',
});
import Sentry
SentrySDK.metrics.count(key: "checkout.failed", value: 1, attributes: [
"user_tier": "premium",
"failure_reason": "payment_declined"
])
import io.sentry.Sentry
Sentry.metrics().count("checkout.failed", 1.0)
Start with these five metrics and you'll spot issues before they become problems.
Track discrete events that matter to the business. These become your KPIs.
Sentry.metrics.count("checkout.completed", 1, {
attributes: { user_tier: "premium", payment_method: "card" },
});
Sentry.metrics.count("checkout.failed", 1, {
attributes: { user_tier: "premium", failure_reason: "payment_declined" },
});
Sentry.metrics.count("checkout.completed", 1, {
attributes: { user_tier: "premium", payment_method: "card" },
});
Sentry.metrics.count("checkout.failed", 1, {
attributes: { user_tier: "premium", failure_reason: "payment_declined" },
});
import sentry_sdk
sentry_sdk.metrics.count("checkout.completed", 1, attributes={
"user_tier": "premium", "payment_method": "card"
})
sentry_sdk.metrics.count("checkout.failed", 1, attributes={
"user_tier": "premium", "failure_reason": "payment_declined"
})
\Sentry\traceMetrics()->count('checkout.completed', 1, [
'user_tier' => 'premium', 'payment_method' => 'card'
]);
\Sentry\traceMetrics()->count('checkout.failed', 1, [
'user_tier' => 'premium', 'failure_reason' => 'payment_declined'
]);
SentrySdk.Metrics.Counter("checkout.completed")
.Tag("user_tier", "premium")
.Tag("payment_method", "card")
.Increment();
SentrySdk.Metrics.Counter("checkout.failed")
.Tag("user_tier", "premium")
.Tag("failure_reason", "payment_declined")
.Increment();
Sentry.metrics.count('checkout.completed', 1,
tags: { user_tier: 'premium', payment_method: 'card' }
)
Sentry.metrics.count('checkout.failed', 1,
tags: { user_tier: 'premium', failure_reason: 'payment_declined' }
)
Sentry.metrics.count('checkout.completed', 1, attributes: {
'user_tier': 'premium',
'payment_method': 'card',
});
Sentry.metrics.count('checkout.failed', 1, attributes: {
'user_tier': 'premium',
'failure_reason': 'payment_declined',
});
import Sentry
SentrySDK.metrics.count(key: "checkout.completed", value: 1, attributes: [
"user_tier": "premium", "payment_method": "card"
])
SentrySDK.metrics.count(key: "checkout.failed", value: 1, attributes: [
"user_tier": "premium", "failure_reason": "payment_declined"
])
import io.sentry.Sentry
Sentry.metrics().count("checkout.completed", 1.0)
Sentry.metrics().count("checkout.failed", 1.0)
Explore in Sentry:
- Go to Explore > Metrics
- Select
checkout.failed, set Aggregate tosum - Group by
failure_reason - Click Samples to see individual events and their traces
Track success and failure of critical operations.
Sentry.metrics.count("email.sent", 1, {
attributes: { email_type: "welcome", provider: "sendgrid" },
});
Sentry.metrics.count("email.failed", 1, {
attributes: { email_type: "welcome", error: "rate_limited" },
});
Sentry.metrics.count("job.processed", 1, {
attributes: { job_type: "invoice-generation", queue: "billing" },
});
Sentry.metrics.count("email.sent", 1, {
attributes: { email_type: "welcome", provider: "sendgrid" },
});
Sentry.metrics.count("email.failed", 1, {
attributes: { email_type: "welcome", error: "rate_limited" },
});
Sentry.metrics.count("job.processed", 1, {
attributes: { job_type: "invoice-generation", queue: "billing" },
});
import sentry_sdk
sentry_sdk.metrics.count("email.sent", 1, attributes={
"email_type": "welcome", "provider": "sendgrid"
})
sentry_sdk.metrics.count("email.failed", 1, attributes={
"email_type": "welcome", "error": "rate_limited"
})
sentry_sdk.metrics.count("job.processed", 1, attributes={
"job_type": "invoice-generation", "queue": "billing"
})
\Sentry\traceMetrics()->count('email.sent', 1, [
'email_type' => 'welcome', 'provider' => 'sendgrid'
]);
\Sentry\traceMetrics()->count('email.failed', 1, [
'email_type' => 'welcome', 'error' => 'rate_limited'
]);
\Sentry\traceMetrics()->count('job.processed', 1, [
'job_type' => 'invoice-generation', 'queue' => 'billing'
]);
SentrySdk.Metrics.Counter("email.sent")
.Tag("email_type", "welcome")
.Tag("provider", "sendgrid")
.Increment();
SentrySdk.Metrics.Counter("email.failed")
.Tag("email_type", "welcome")
.Tag("error", "rate_limited")
.Increment();
SentrySdk.Metrics.Counter("job.processed")
.Tag("job_type", "invoice-generation")
.Tag("queue", "billing")
.Increment();
Sentry.metrics.count('email.sent', 1,
tags: { email_type: 'welcome', provider: 'sendgrid' }
)
Sentry.metrics.count('email.failed', 1,
tags: { email_type: 'welcome', error: 'rate_limited' }
)
Sentry.metrics.count('job.processed', 1,
tags: { job_type: 'invoice-generation', queue: 'billing' }
)
Sentry.metrics.count('email.sent', 1, attributes: {
'email_type': 'welcome',
'provider': 'sendgrid',
});
Sentry.metrics.count('email.failed', 1, attributes: {
'email_type': 'welcome',
'error': 'rate_limited',
});
Sentry.metrics.count('job.processed', 1, attributes: {
'job_type': 'invoice-generation',
'queue': 'billing',
});
import Sentry
SentrySDK.metrics.count(key: "email.sent", value: 1, attributes: [
"email_type": "welcome", "provider": "sendgrid"
])
SentrySDK.metrics.count(key: "email.failed", value: 1, attributes: [
"email_type": "welcome", "error": "rate_limited"
])
SentrySDK.metrics.count(key: "job.processed", value: 1, attributes: [
"job_type": "invoice-generation", "queue": "billing"
])
import io.sentry.Sentry
Sentry.metrics().count("email.sent", 1.0)
Sentry.metrics().count("email.failed", 1.0)
Sentry.metrics().count("job.processed", 1.0)
Query in Explore > Metrics: Add both email.sent and email.failed, group by email_type, compare the ratio.
Track the current state of pools, queues, and connections. Call these periodically (e.g., every 30 seconds).
Sentry.metrics.gauge("queue.depth", await queue.size(), {
attributes: { queue_name: "notifications" },
});
Sentry.metrics.gauge("pool.connections_active", pool.activeConnections, {
attributes: { pool_name: "postgres-primary" },
});
Sentry.metrics.gauge("queue.depth", await queue.size(), {
attributes: { queue_name: "notifications" },
});
Sentry.metrics.gauge("pool.connections_active", pool.activeConnections, {
attributes: { pool_name: "postgres-primary" },
});
import sentry_sdk
sentry_sdk.metrics.gauge("queue.depth", queue.size(), attributes={
"queue_name": "notifications"
})
sentry_sdk.metrics.gauge("pool.connections_active", pool.active_connections, attributes={
"pool_name": "postgres-primary"
})
use \Sentry\Metrics\Unit;
\Sentry\traceMetrics()->gauge('queue.depth', $queue->size(), [
'queue_name' => 'notifications'
]);
\Sentry\traceMetrics()->gauge('pool.connections_active', $pool->activeConnections, [
'pool_name' => 'postgres-primary'
]);
SentrySdk.Metrics.Gauge("queue.depth")
.Tag("queue_name", "notifications")
.Set(queue.Size);
SentrySdk.Metrics.Gauge("pool.connections_active")
.Tag("pool_name", "postgres-primary")
.Set(pool.ActiveConnections);
Sentry.metrics.gauge('queue.depth', queue.size,
tags: { queue_name: 'notifications' }
)
Sentry.metrics.gauge('pool.connections_active', pool.active_connections,
tags: { pool_name: 'postgres-primary' }
)
Sentry.metrics.gauge('queue.depth', queue.size.toDouble(), attributes: {
'queue_name': 'notifications',
});
Sentry.metrics.gauge('pool.connections_active', pool.activeConnections.toDouble(), attributes: {
'pool_name': 'postgres-primary',
});
import Sentry
SentrySDK.metrics.gauge(key: "queue.depth", value: Double(queue.size), attributes: [
"queue_name": "notifications"
])
SentrySDK.metrics.gauge(key: "pool.connections_active", value: Double(pool.activeConnections), attributes: [
"pool_name": "postgres-primary"
])
import io.sentry.Sentry
Sentry.metrics().gauge("queue.depth", queue.size.toDouble())
Sentry.metrics().gauge("pool.connections_active", pool.activeConnections.toDouble())
Query in Explore > Metrics: View max(queue.depth) over time to spot backlogs.
Track values that vary and need percentile analysis. Tip: averages can hide outliers, use p90/p95/p99 instead.
Sentry.metrics.distribution("api.latency", responseTimeMs, {
unit: "millisecond",
attributes: { endpoint: "/api/orders", method: "POST" },
});
Sentry.metrics.distribution("db.query_time", queryDurationMs, {
unit: "millisecond",
attributes: { table: "orders", operation: "select" },
});
Sentry.metrics.distribution("api.latency", responseTimeMs, {
unit: "millisecond",
attributes: { endpoint: "/api/orders", method: "POST" },
});
Sentry.metrics.distribution("db.query_time", queryDurationMs, {
unit: "millisecond",
attributes: { table: "orders", operation: "select" },
});
import sentry_sdk
sentry_sdk.metrics.distribution("api.latency", response_time_ms,
unit="millisecond",
attributes={"endpoint": "/api/orders", "method": "POST"}
)
sentry_sdk.metrics.distribution("db.query_time", query_duration_ms,
unit="millisecond",
attributes={"table": "orders", "operation": "select"}
)
use \Sentry\Metrics\Unit;
\Sentry\traceMetrics()->distribution('api.latency', $responseTimeMs,
['endpoint' => '/api/orders', 'method' => 'POST'],
Unit::millisecond()
);
\Sentry\traceMetrics()->distribution('db.query_time', $queryDurationMs,
['table' => 'orders', 'operation' => 'select'],
Unit::millisecond()
);
SentrySdk.Metrics.Distribution("api.latency", responseTimeMs,
unit: MeasurementUnit.Duration.Millisecond)
.Tag("endpoint", "/api/orders")
.Tag("method", "POST");
SentrySdk.Metrics.Distribution("db.query_time", queryDurationMs,
unit: MeasurementUnit.Duration.Millisecond)
.Tag("table", "orders")
.Tag("operation", "select");
Sentry.metrics.distribution('api.latency', response_time_ms,
unit: 'millisecond',
tags: { endpoint: '/api/orders', method: 'POST' }
)
Sentry.metrics.distribution('db.query_time', query_duration_ms,
unit: 'millisecond',
tags: { table: 'orders', operation: 'select' }
)
Sentry.metrics.distribution('api.latency', responseTimeMs,
unit: SentryMeasurementUnit.duration(SentryDurationUnit.milliSecond),
attributes: {'endpoint': '/api/orders', 'method': 'POST'},
);
Sentry.metrics.distribution('db.query_time', queryDurationMs,
unit: SentryMeasurementUnit.duration(SentryDurationUnit.milliSecond),
attributes: {'table': 'orders', 'operation': 'select'},
);
import Sentry
SentrySDK.metrics.distribution(key: "api.latency", value: responseTimeMs,
unit: .millisecond,
attributes: ["endpoint": "/api/orders", "method": "POST"]
)
SentrySDK.metrics.distribution(key: "db.query_time", value: queryDurationMs,
unit: .millisecond,
attributes: ["table": "orders", "operation": "select"]
)
import io.sentry.Sentry
import io.sentry.metrics.MetricsUnit
Sentry.metrics().distribution("api.latency", responseTimeMs,
MetricsUnit.Duration.MILLISECOND)
Sentry.metrics().distribution("db.query_time", queryDurationMs,
MetricsUnit.Duration.MILLISECOND)
Query in Explore > Metrics: View p95(api.latency) grouped by endpoint to find slow routes.
Track amounts, sizes, and quantities for analysis.
Sentry.metrics.distribution("order.amount", order.totalUsd, {
unit: "usd",
attributes: { user_tier: "premium", region: "us-west" },
});
Sentry.metrics.distribution("upload.size", fileSizeBytes, {
unit: "byte",
attributes: { file_type: "image", source: "profile-update" },
});
Sentry.metrics.distribution("order.amount", order.totalUsd, {
unit: "usd",
attributes: { user_tier: "premium", region: "us-west" },
});
Sentry.metrics.distribution("upload.size", fileSizeBytes, {
unit: "byte",
attributes: { file_type: "image", source: "profile-update" },
});
import sentry_sdk
sentry_sdk.metrics.distribution("order.amount", order.total_usd,
unit="usd",
attributes={"user_tier": "premium", "region": "us-west"}
)
sentry_sdk.metrics.distribution("upload.size", file_size_bytes,
unit="byte",
attributes={"file_type": "image", "source": "profile-update"}
)
use \Sentry\Metrics\Unit;
\Sentry\traceMetrics()->distribution('order.amount', $order->totalUsd,
['user_tier' => 'premium', 'region' => 'us-west'],
Unit::custom('usd')
);
\Sentry\traceMetrics()->distribution('upload.size', $fileSizeBytes,
['file_type' => 'image', 'source' => 'profile-update'],
Unit::byte()
);
SentrySdk.Metrics.Distribution("order.amount", order.TotalUsd,
unit: MeasurementUnit.Custom("usd"))
.Tag("user_tier", "premium")
.Tag("region", "us-west");
SentrySdk.Metrics.Distribution("upload.size", fileSizeBytes,
unit: MeasurementUnit.Information.Byte)
.Tag("file_type", "image")
.Tag("source", "profile-update");
Sentry.metrics.distribution('order.amount', order.total_usd,
unit: 'usd',
tags: { user_tier: 'premium', region: 'us-west' }
)
Sentry.metrics.distribution('upload.size', file_size_bytes,
unit: 'byte',
tags: { file_type: 'image', source: 'profile-update' }
)
Sentry.metrics.distribution('order.amount', order.totalUsd,
unit: SentryMeasurementUnit.custom('usd'),
attributes: {'user_tier': 'premium', 'region': 'us-west'},
);
Sentry.metrics.distribution('upload.size', fileSizeBytes.toDouble(),
unit: SentryMeasurementUnit.information(SentryInformationUnit.byte),
attributes: {'file_type': 'image', 'source': 'profile-update'},
);
import Sentry
SentrySDK.metrics.distribution(key: "order.amount", value: order.totalUsd,
unit: .generic("usd"),
attributes: ["user_tier": "premium", "region": "us-west"]
)
SentrySDK.metrics.distribution(key: "upload.size", value: Double(fileSizeBytes),
unit: .byte,
attributes: ["file_type": "image", "source": "profile-update"]
)
import io.sentry.Sentry
import io.sentry.metrics.MetricsUnit
Sentry.metrics().distribution("order.amount", order.totalUsd)
Sentry.metrics().distribution("upload.size", fileSizeBytes.toDouble(),
MetricsUnit.Information.BYTE)
Query in Explore > Metrics: View avg(order.amount) grouped by region to compare regional performance.
Any metric query you build in Explore can become a Monitor so you get notified when behavior changes. Application Metrics is available as a dataset in the Monitor form (alongside Errors, Spans, Logs, and Releases), with a dedicated template to get started quickly.
Monitors can detect threshold breaches, percentage changes, or dynamic anomalies — then create an issue you route through Alerts to Slack, PagerDuty, email, and more.
Once you're bringing in metrics, learn how to Build a dashboard and alert from custom metrics (~20 min recipe).
You can also add metric visualizations to Dashboards and save common Explore queries for quick access.
You can also create dashboards with an AI coding agent (~15 min recipe).
| Category | Type | Method | Example Attributes |
|---|---|---|---|
| Business events | Counter | count() | user_tier, payment_method, failure_reason |
| Application health | Counter | count() | email_type, provider, job_type, queue |
| Resource utilization | Gauge | gauge() | queue_name, pool_name |
| Latency/performance | Distribution | distribution() | endpoint, method, table, operation |
| Business values | Distribution | distribution() | user_tier, region, file_type |
| Signal | Best For | Example Question |
|---|---|---|
| Metrics | Aggregated counts, rates, percentiles | "How many checkouts failed this hour?" |
| Traces | Request flow, latency breakdown | "Why was this specific request slow?" |
| Logs | Detailed context, debugging | "What happened right before this error?" |
All three are trace-connected. Start wherever makes sense and navigate to the others.
Explore the Application Metrics product walkthrough to learn more about the Sentry interface and discover additional tips.
Our documentation is open source and available on GitHub. Your contributions are welcome, whether fixing a typo (drat!) or suggesting an update ("yeah, this would be better").