Custom Metrics
Complete the full lesson to earn 25 points
Work through each section, then tap “Mark as Complete” on the last one.
✦ Skip the page breaks and see fewer ads — read each lesson on a single page with Pro
Lesson: Mastering Custom Metrics for Root Cause Analysis
Introduction: Why Custom Metrics Matter
In the world of software engineering and system administration, we often rely on standard metrics—CPU usage, memory consumption, disk I/O, and network latency—to understand how our applications are behaving. While these "out-of-the-box" metrics are essential for identifying that a problem exists, they rarely provide enough context to explain why that problem is occurring. This is where custom metrics come into play. A custom metric is any data point that you define and instrument yourself to measure specific business logic, application states, or internal workflows that standard infrastructure monitoring tools cannot see.
When an incident occurs, time is your most valuable resource. If your dashboard only shows that a server is at 90% CPU, you know the server is busy, but you do not know which specific user action, background task, or data processing job is driving that load. By instrumenting your code with custom metrics—such as the number of items in a processing queue, the time taken to complete a specific database transaction, or the count of failed login attempts—you transform your monitoring system from a basic "health checker" into a powerful diagnostic tool. This lesson will guide you through the process of defining, implementing, and utilizing custom metrics to perform precise root cause analysis.
The Anatomy of a Custom Metric
A custom metric is not just a random number; it is a structured piece of data that provides observability into your application's internals. To be useful during an investigation, every custom metric must consist of four core components: the name, the value, the dimensions (or tags), and the timestamp.
1. The Metric Name
The name should be descriptive and follow a consistent naming convention. Avoid vague names like count_1 or data_val. Instead, use a hierarchical naming scheme such as application.order_processing.checkout_duration_ms or system.queue.pending_messages. A clear naming convention allows your team to find metrics quickly during a high-pressure outage without guessing what they represent.
2. The Metric Value
This is the raw data point you are collecting. Depending on the nature of the metric, this could be a gauge (a value that goes up and down, like the number of active connections), a counter (a value that only increases, like total requests handled), or a histogram (a distribution of values, like response latency). Choosing the right type of metric is critical because it dictates how you can aggregate and analyze the data later.
3. The Dimensions (Tags)
Dimensions are key-value pairs that add context to your metric. For example, if you are tracking the duration of an API request, you might add dimensions like endpoint=/api/v1/user, status_code=200, and region=us-east-1. These dimensions are what allow you to "slice and dice" your data. If you notice a spike in latency, you can filter by region to see if the problem is localized, or filter by endpoint to see if a specific feature is the culprit.
4. The Timestamp
The timestamp ensures that your metric data is accurately correlated with other system events. When you are performing root cause analysis, you will often overlay your custom metrics on top of infrastructure logs or error reports. If your timestamps are not synchronized or are missing, it becomes nearly impossible to determine if a specific code deployment or configuration change caused the issue you are observing.
Callout: Gauges vs. Counters It is vital to distinguish between gauges and counters. A gauge represents a snapshot of a state at a specific point in time, such as the current temperature or the number of threads currently running. A counter, however, is a cumulative value that only ever goes up. If you try to represent a counter as a gauge, you lose the ability to calculate rates of change, which is essential for identifying spikes in traffic or error rates. Always choose the metric type that aligns with the logical behavior of the data you are tracking.
Implementing Custom Metrics: A Practical Approach
To implement custom metrics effectively, you should integrate them into your application code as close to the business logic as possible. Most modern monitoring platforms provide libraries or SDKs that simplify this process. Let’s look at a practical example using a common pattern for tracking the duration of a function execution.
Example: Tracking Order Processing Time
Imagine you have a function that processes customer orders. You want to know how long this function takes to complete so you can investigate potential bottlenecks in your database or external API calls.
import time
from prometheus_client import Histogram
# Define the histogram metric
ORDER_PROCESSING_TIME = Histogram(
'order_processing_seconds',
'Time spent processing orders',
['payment_method', 'region']
)
def process_order(order_data):
start_time = time.time()
payment_method = order_data.get('payment_method')
region = order_data.get('region')
try:
# Business logic goes here
result = execute_payment(order_data)
return result
finally:
# Record the duration in the histogram
duration = time.time() - start_time
ORDER_PROCESSING_TIME.labels(
payment_method=payment_method,
region=region
).observe(duration)
In this example, we use a histogram because it captures the distribution of processing times, not just an average. By including payment_method and region as labels (dimensions), we can immediately answer questions like, "Is order processing slower for PayPal users in the EU region?" This is infinitely more useful than simply knowing that the average order processing time has increased by 200 milliseconds.
Note: Always use
try...finallyblocks when timing operations. If your business logic throws an exception and crashes, you still want to record that the operation finished (even if it failed), otherwise, your metrics will only reflect successful requests, leading to a "survivorship bias" in your data.
Strategy for Effective Root Cause Analysis
When you are in the middle of an incident, your goal is to narrow the scope of the problem as quickly as possible. Custom metrics are your primary navigation tool. Follow this step-by-step framework to use your metrics for root cause analysis:
Step 1: Establish the Baseline
Before you can identify a problem, you must know what "normal" looks like. Use your monitoring tool to visualize the historical trends of your custom metrics. If you do not have historical data, you are flying blind. Monitor these metrics during standard operations to understand the typical range of values, seasonality (e.g., higher traffic on weekends), and dependencies.
Step 2: Identify the Anomaly
When an alert triggers, look at the metric that fired the alert. Is it a sudden spike, a steady climb, or a complete drop-off? Use your dimensions to filter the data. If the spike occurs across all regions, the issue is likely global (e.g., a bad code deployment or a centralized database issue). If the spike is limited to one region or one specific API key, you have successfully isolated the problem to a subset of your infrastructure.
Step 3: Correlate with Other Events
Once you have identified the affected subset, look for other metrics that shifted at the exact same timestamp. Did the database connection pool count spike? Did the number of 500-series HTTP errors increase? Did a background job start consuming more CPU? Correlation does not always equal causation, but it provides the strongest leads for where to look in your logs.
Step 4: Inspect the Logs
Now that you have a narrow time window and a specific component (e.g., "the payment gateway processing service"), you can query your logs with surgical precision. Instead of searching through millions of log lines, you are searching through the specific logs generated by the service that your custom metrics flagged as problematic.
Common Pitfalls and How to Avoid Them
Even with the best intentions, it is easy to implement custom metrics in a way that creates more work rather than less. Here are the most common mistakes engineers make when defining custom metrics.
1. High Cardinality Metrics
Cardinality refers to the number of unique combinations of label values. If you add a label like user_id or order_id to a metric, you create a new time series for every single user or order. Most monitoring systems have a limit on the number of time series they can handle. If you exceed this limit, your monitoring system will become slow, inaccurate, or crash entirely.
- How to avoid it: Never use high-cardinality data like unique IDs as metric labels. If you need to track metrics per user, aggregate them at a higher level (e.g.,
user_tieroraccount_type) and use logs for the granular, per-user data.
2. Metrics as Logs
Some engineers try to use metrics to store event data (e.g., storing a full error message inside a metric). Metrics are meant for quantitative analysis, not qualitative description. If you find yourself wanting to store strings or large blocks of text, you are looking for a logging system, not a metrics system.
- How to avoid it: Keep metrics numeric and lightweight. Use logs for detailed error descriptions, stack traces, and debug information.
3. Lack of Documentation
A custom metric that no one understands is useless. If a new engineer joins the team and sees a metric called app_x_proc_total, they won't know if it counts successful processes, failed ones, or both.
- How to avoid it: Maintain a "Metric Dictionary." This can be a simple markdown file in your repository that lists each custom metric, its purpose, its labels, and its expected units.
4. Over-Instrumenting
It is tempting to measure everything, but every custom metric has a cost in terms of memory, network bandwidth, and storage. If you instrument every single function call, you will slow down your application and bloat your monitoring costs.
- How to avoid it: Follow the "Golden Signals" approach (latency, traffic, errors, and saturation). Only add custom metrics for things that are critical to the business or that you have identified as common pain points in your architecture.
Comparison: Metrics vs. Logs vs. Traces
To perform effective root cause analysis, you need to understand how custom metrics fit into the broader observability landscape.
| Feature | Custom Metrics | Logs | Distributed Tracing |
|---|---|---|---|
| Primary Use | Detecting anomalies and trends | Understanding the "why" of an event | Following a request across services |
| Data Format | Numerical (time-series) | Text/Structured JSON | Spans/Events with context |
| Scalability | Very High | High (requires storage) | Medium (high overhead) |
| Best For | "Is the system healthy?" | "What happened at 10:02 AM?" | "Where is the bottleneck in this request?" |
Callout: The Observability Trinity Metrics, logs, and traces are often called the "three pillars of observability." While they serve different purposes, they are most powerful when combined. Use metrics to identify that a problem exists, use traces to see which service is causing the delay, and use logs to examine the specific error message generated by the failing code. Never rely on just one of these; a complete root cause analysis strategy requires all three.
Advanced Techniques: Custom Metrics for Business Logic
Beyond infrastructure, custom metrics can track business-critical events that impact the bottom line. For example, you might track the "cart abandonment rate" or the "conversion rate per marketing channel." These metrics are invaluable during root cause analysis because they allow you to quantify the business impact of a technical failure.
Example: Tracking Business-Level Failures
If your checkout service fails, you don't just want to know that the service is down; you want to know how many orders were potentially lost.
# Custom metric to track checkout attempts
CHECKOUT_ATTEMPTS = Counter('checkout_attempts_total', 'Total checkout attempts', ['brand'])
# Custom metric to track checkout failures
CHECKOUT_FAILURES = Counter('checkout_failures_total', 'Total failed checkouts', ['brand', 'error_code'])
def perform_checkout(cart_data):
brand = cart_data.get('brand')
CHECKOUT_ATTEMPTS.labels(brand=brand).inc()
try:
process_payment(cart_data)
except PaymentError as e:
CHECKOUT_FAILURES.labels(brand=brand, error_code=e.code).inc()
raise
By tracking these, you can easily calculate the failure rate by dividing CHECKOUT_FAILURES by CHECKOUT_ATTEMPTS. If this ratio spikes, you know immediately that you have a business-impacting issue, which helps in prioritizing incident response.
Setting Up Alerts Based on Custom Metrics
Once you have your custom metrics in place, the final step is to create alerts that actually help you. A common mistake is setting alerts that are too sensitive, leading to "alert fatigue." If your team receives 50 alerts a day, they will eventually stop paying attention to them.
Best Practices for Alerting:
- Use Thresholds Carefully: Instead of alerting on a single high value, alert on a sustained value over time. For example, "Alert if the 95th percentile latency is above 500ms for more than 5 minutes." This filters out transient blips that resolve themselves.
- Alert on Rates, Not Totals: If you alert on the total number of errors, your alert will trigger more often as your traffic grows. Always alert on the rate of errors (e.g., errors per second) to keep your alerts meaningful as your system scales.
- Include Runbooks: Every alert should have a link to a "runbook" or documentation that explains what the metric means and the typical steps to resolve the issue. If an engineer gets an alert at 3 AM, they should not have to guess what the metric represents.
Warning: Avoid "flapping" alerts. A flapping alert is one that triggers and clears rapidly due to a metric hovering right at the threshold. Use "hysteresis"—setting your alert threshold (e.g., > 80% CPU) to be higher than your clear threshold (e.g., < 60% CPU)—to ensure that the system stays stable before the alert stops firing.
Step-by-Step: Integrating Custom Metrics into a CI/CD Pipeline
To ensure that your custom metrics are always up to date and consistent, integrate their definition into your development lifecycle.
- Define in Schema: Maintain a central schema file (like a YAML or JSON file) that defines all custom metrics, their types, and their labels. This serves as the "source of truth."
- Automated Validation: Add a step in your CI pipeline that validates new code against this schema. If a developer tries to add a metric that doesn't follow the naming convention or uses a forbidden label, the build should fail.
- Documentation Generation: Use a script to automatically generate your "Metric Dictionary" documentation from this schema file. This ensures your documentation is never out of sync with your code.
- Dashboards as Code: Define your monitoring dashboards in code (e.g., using Terraform or a similar tool). This allows you to version-control your dashboards and ensure that every service has a standard set of dashboards that include your custom metrics.
Common Questions (FAQ)
Q: Should I use a custom metric for everything?
A: No. Use custom metrics for things that change frequently and need to be trended over time. For static information (like a server's serial number or OS version), use logs or inventory management systems.
Q: How many labels is "too many"?
A: A good rule of thumb is to keep the number of labels to under 5 per metric. Any more than that, and you risk hitting cardinality limits and making your dashboards unreadable.
Q: What if my metric data is delayed?
A: Monitoring systems are designed to handle slight delays. However, if your data is consistently delayed by minutes, you should investigate your ingestion pipeline. Metrics are meant to be near real-time; if they aren't, they lose their value for incident response.
Q: Can I use custom metrics to measure code performance?
A: Absolutely. Tracking the execution time of specific algorithms or database queries is one of the most common and valuable uses of custom metrics. It allows you to identify performance regressions before they become major outages.
Summary: Key Takeaways for Success
To wrap up this lesson, here are the core principles you should carry forward in your journey to master custom metrics for root cause analysis:
- Custom metrics fill the gap: While infrastructure metrics tell you that something is wrong, custom metrics explain what is happening at the application level.
- Context is king: Always use dimensions (labels) to add context to your metrics, but be wary of high-cardinality values that can overwhelm your monitoring system.
- Choose the right metric type: Always decide if you need a counter, a gauge, or a histogram before you start writing code. Using the wrong type will make analysis impossible.
- Alert on rates, not totals: To prevent alert fatigue and ensure scalability, alert on the rate of change rather than absolute cumulative values.
- Treat metrics as code: Use schemas and automated validation to keep your metric definitions consistent, documented, and reliable across your entire engineering organization.
- Build for the incident: Always design your metrics with the assumption that you will be looking at them during a high-stress outage. If the metric isn't easy to understand and quick to query, it won't help you when you need it most.
- Iterate and Refine: Your needs will change as your application grows. Regularly review your metrics, remove those that are no longer used, and add new ones as you discover new parts of your system that need better observability.
By following these practices, you will move beyond simple "up/down" monitoring and develop a sophisticated, data-driven approach to maintaining and optimizing your systems. Custom metrics are not just a monitoring tool; they are a fundamental part of the culture of engineering excellence, enabling teams to move faster, fix problems more quickly, and build more reliable services.
Continue the course
Enjoying the courses?
Everything stays free. Pro shows fewer ads, doubles your daily points limit so you progress twice as fast, and lets you read each lesson on one page.
- ✓ Fewer advertisements
- ✓ 2× daily points limit
- ✓ Distraction-free lessons