Span Metrics
Learn how to add custom metrics to your spans for enhanced performance monitoring and debugging.
To use span metrics effectively, you must first set up tracing in your application.
Span metrics allow you to track custom performance data and debugging information within your application's traces. There are two main approaches to instrumenting metrics:
- Adding metrics to existing spans
- Creating dedicated spans with custom metrics
You can enhance existing spans with custom metrics by adding attributes. This is useful when you want to augment automatic instrumentation or add contextual data to spans you've already created.
const span = Sentry.getActiveSpan();
if (span) {
// Add individual metrics
span.setAttribute("database.rows_affected", 42);
span.setAttribute("cache.hit_rate", 0.85);
// Add multiple metrics at once
span.setAttributes({
"memory.heap_used": 1024000,
"queue.length": 15,
"processing.duration_ms": 127,
});
}
When adding metrics as span attributes:
- Use consistent naming conventions (e.g.,
category.metric_name
) - Keep attribute names concise but descriptive
- Use appropriate data types (string, number, boolean, or arrays of these types)
- Consider the cardinality of your metrics to avoid excessive unique combinations
For more detailed transaction tracking, you can create custom dedicated spans that focus on specific metrics or attributes that you want to track. This approach provides better discoverability and more precise span configurations, however it can also appear to create more noise in your transaction traces.
Sentry.startSpan(
{
name: "Database Query Metrics",
op: "db.metrics",
attributes: {
"db.query_type": "SELECT",
"db.table": "users",
"db.execution_time_ms": 45,
"db.rows_returned": 100,
"db.connection_pool_size": 5,
},
},
() => {
// Your database operation here
},
);
Here are some common patterns for creating metric-focused spans:
Sentry.startSpan(
{
name: "Resource Usage Metrics",
op: "system.metrics",
attributes: {
"cpu.usage_percent": 75.5,
"memory.used_mb": 1024,
"memory.free_mb": 512,
"disk.iops": 150,
},
},
async () => {
// System operation being monitored
},
);
Sentry.startSpan(
{
name: "User Session Metrics",
op: "app.metrics",
attributes: {
"session.duration_sec": 300,
"session.interactions": 25,
"session.errors": 0,
"session.latency_ms": 150,
},
},
() => {
// Session-related code
},
);
To consistently add metrics across all spans in your application, you can use the beforeSendTransaction
callback:
Sentry.init({
beforeSendTransaction(event) {
// Add metrics to the root span
event.contexts.trace.data = {
...event.contexts.trace.data,
"app.version": "1.2.3",
"environment.region": "us-west-2",
};
// Add metrics to all child spans
event.spans.forEach((span) => {
span.data = {
...span.data,
"app.component_version": "2.0.0",
"app.deployment_stage": "production",
};
});
return event;
},
});
Metric Naming
- Use clear, consistent naming patterns
- Include the metric category (e.g.,
db
,cache
,http
) - Use snake_case for metric names
- Avoid high-cardinality values in metric names
Data Types
- Use appropriate numeric types for measurements
- Use booleans for status flags
- Use strings for categorical data
- Use arrays when grouping related values
Performance Considerations
- Avoid adding too many metrics to a single span
- Consider the overhead of metric collection
- Use sampling when collecting high-frequency metrics
- Balance metric granularity with system performance
Debugging and Monitoring
- Include relevant error and status information
- Add timestamps for important events
- Include correlation IDs for related operations
- Add context that helps with troubleshooting
Sentry.startSpan(
{
name: "File Upload and Processing",
op: "file.process",
attributes: {
"file.size_bytes": 15728640, // 15MB
"file.type": "image/jpeg",
"file.name": "user-profile.jpg",
"processing.steps_completed": ["resize", "compress", "metadata"],
"processing.output_size_bytes": 524288, // 512KB
"processing.compression_ratio": 0.033,
"upload.chunk_size": 1048576, // 1MB
"upload.chunks_completed": 15,
"upload.storage_provider": "s3",
"upload.cdn_propagation_ms": 1500,
"error.count": 0,
},
},
async () => {
// File processing and upload execution
},
);
Sentry.startSpan(
{
name: "External API Call",
op: "http.client",
attributes: {
"http.endpoint": "/api/users",
"http.response_size_bytes": 2048,
"http.retry_count": 0,
"http.cache_status": "miss",
"http.response_time_ms": 200,
},
},
async () => {
// API call execution
},
);
Sentry.startSpan(
{
name: "LLM Generation",
op: "ai.completion",
attributes: {
"llm.model": "gpt-4",
"llm.prompt_tokens": 425,
"llm.completion_tokens": 632,
"llm.total_tokens": 1057,
"llm.time_to_first_token_ms": 245,
"llm.total_duration_ms": 3250,
"llm.temperature": 0.7,
"llm.max_tokens": 2000,
"llm.stream_mode": true,
"llm.request_status": "success",
},
},
async () => {
// LLM API call execution
},
);
Sentry.startSpan(
{
name: "Purchase Transaction",
op: "commerce.checkout",
attributes: {
"cart.item_count": 3,
"cart.total_amount": 159.99,
"cart.currency": "USD",
"cart.items": ["SKU123", "SKU456", "SKU789"],
"payment.provider": "stripe",
"payment.method": "credit_card",
"payment.status": "success",
"transaction.id": "ord_123456789",
"customer.type": "returning",
"shipping.method": "express",
"promotion.code_applied": "SUMMER23",
"promotion.discount_amount": 20.0,
},
},
async () => {
// Checkout process execution
},
);
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").