Implementing Best Practices
1. Following Naming Conventions
| Rule | Description | Example |
|---|---|---|
| Metric Names | Must be lowercase, with words separated by underscores | http_requests_total |
| Label Names | Must be lowercase, with words separated by underscores | http_method |
| Unit Suffixes | Use standard unit suffixes for clarity (e.g., _seconds, _bytes) |
process_resident_memory_bytes |
| Metric Types | Append type suffixes to metric names (e.g., _count, _sum, _bucket) |
http_request_duration_seconds_bucket |
Example: Naming Convention in Practice
# Correct naming conventions
http_requests_total
process_resident_memory_bytes
http_request_duration_seconds_sum
cache_hits_total
# Avoid these
HTTPRequestTotal
processResidentMemoryBytes
httpRequestDurationSecondsSum
2. Designing Low-Cardinality Labels
Label cardinality refers to the number of unique label value combinations for a given metric. High cardinality can lead to performance issues and increased memory usage in Prometheus.
| Label Name | Cardinality Impact | Best Practice |
|---|---|---|
| instance | high |
Include instance labels to identify specific targets, but avoid high-cardinality values like IP addresses or timestamps |
| job | medium |
Use job labels to group related targets, ensuring consistent naming across services |
| status_code | high |
Avoid using status codes as labels if they have many unique values; consider aggregating into buckets or ranges |
| user_id | very high |
Never use user IDs or session IDs as labels; these create extremely high cardinality and degrade performance |
Example: High Cardinality Label (Avoid)
http_requests_total{user_id="1234567890", endpoint="/api/v1/users"}
Example: Low Cardinality Label (Recommended)
http_requests_total{job="api-server", endpoint="/api/v1/users", status_code="200"}
Key Principles
- Use labels only for grouping and filtering, not for storing unique identifiers
- Avoid labels with many unique values (e.g., user IDs, session IDs)
- Prefer static labels over dynamic ones when possible
- Aggregate or bucket high-cardinality values into ranges or categories
3. Using Instrumentation Libraries
Prometheus provides official instrumentation libraries for various programming languages to help you instrument your applications effectively. These libraries simplify the process of exposing metrics to Prometheus by handling the low-level details of metric collection and exposition.
| Language | Library | Repository | Notes |
|---|---|---|---|
| Go | go-kit/kit/metrics/prometheus |
go-kit | Part of the go-kit framework; provides Prometheus-compatible metrics. |
| Java | io.prometheus.client |
client_java | Official Prometheus client for Java; supports both Micrometer and direct usage. |
| Python | prometheus_client |
client_python | Official Prometheus client for Python; supports both sync and async usage. |
| Node.js | prom-client |
prom-client | Popular client for Node.js; supports both Express and standalone usage. |
| Ruby | prometheus-client |
client_ruby | Official Prometheus client for Ruby; supports various frameworks like Rails. |
| C++ | prometheus-cpp |
prometheus-cpp | Official C++ client; supports both pull and push models. |
| Rust | prometheus |
client_rust | Official Rust client; supports both Tokio and async runtime. |
| PHP | promphp/prometheus_client_php |
prometheus_client_php | Official Prometheus client for PHP; supports PSR-7 and PSR-15. |
Example: Instrumenting a Go Application
package main
import (
"log"
"net/http"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
var (
httpRequestCount = promauto.NewCounterVec(prometheus.CounterOpts{
Name: "http_requests_total",
Help: "Total number of HTTP requests.",
}, []string{"method", "code"})
)
func main() {
http.HandleFunc("/metrics", func(w http.ResponseWriter, r *http.Request) {
promhttp.Handler().ServeHTTP(w, r)
})
http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
httpRequestCount.WithLabelValues(r.Method, "200").Inc()
w.WriteHeader(http.StatusOK)
w.Write([]byte("Hello, World!"))
})
log.Fatal(http.ListenAndServe(":8080", nil))
}
Example: Instrumenting a Python Application
from prometheus_client import Counter, start_http_server
import time
# Create a counter metric
http_requests = Counter("http_requests_total", "Total number of HTTP requests.", [ "method", "code" ])
def handle_request():
# Increment the counter when handling a request
http_requests.labels(method="GET", code="200").inc()
return "Hello, World!"
if __name__ == "__main__":
start_http_server(8000)
while True:
handle_request()
time.sleep(1)
4. Implementing Four Golden Signals
| Signal | Description | Metrics | Best Practices |
|---|---|---|---|
| Request Rate | Number of requests per second to your system | rate(http_requests_total[1m]) |
Monitor with high cardinality, alert on sudden spikes or drops |
| Error Rate | Percentage of failed requests | rate(http_requests_total{status=~"5xx"}[1m]) / rate(http_requests_total[1m]) |
Set alerts for error rates above 1% or 5xx errors increasing rapidly |
| Latency | Time taken to serve requests | histogram_quantile(0.95, sum by(le) (rate(http_request_duration_seconds_bucket[5m]))) |
Use quantiles (e.g., 95th, 99th) to detect performance degradation |
| Traffic Volume | Overall volume of requests or data flow | rate(http_requests_total[1m]) |
Track trends over time to understand usage patterns and capacity planning |
Example: Alerting on Error Rate
groups:
- name: http-errors
rules:
- alert: HighErrorRate
expr: rate(http_requests_total{status=~"5xx"}[1m]) / rate(http_requests_total[1m]) > 0.01
for: 5m
labels:
severity: page
annotations:
summary: "High error rate detected"
description: "Error rate is above 1% for more than 5 minutes."
5. Avoiding Missing Metrics
Example: Labeling best practices to prevent missing data
# Prometheus recording rule to ensure all metrics have required labels
groups:
- name: example_metrics
rules:
- record: instance_job:up:rate1m
expr: up{job!=""} == 1
| Problem | Solution | Notes |
|---|---|---|
| Missing labels | Use consistent label names across all metrics | Prevents gaps in query results when filtering by labels |
| Unlabeled metrics | Always include at least one label (e.g., job, instance) |
Ensures metrics can be uniquely identified and queried |
| Inconsistent label values | Enforce label value constraints via validation rules | Avoids missing data in queries due to unexpected label values |
promtool check rules to validate recording rules and alerting rules for missing or invalid labels.
Best Practices Summary
- Always define at least one label per metric
- Use consistent label names across services and jobs
- Validate metric labels with
promtool - Implement recording rules to ensure label completeness
6. Using Direct Instrumentation
Direct instrumentation involves manually adding metrics to your application code using Prometheus client libraries. This approach gives you fine-grained control over metric collection and is essential for capturing application-specific data.
| Metric Type | Description | Usage Example | Notes |
|---|---|---|---|
| Counter | Tracks a monotonically increasing value, typically for counts of events. | const counter = new client.Counter({name: 'http_requests_total',help: 'Total number of HTTP requests',labelNames: ['method', 'status']}); |
Use for tracking request counts, error counts, etc. |
| Gauge | Represents a single numerical value that can be set or modified. | const gauge = new client.Gauge({name: 'memory_usage_bytes',help: 'Current memory usage in bytes'}); |
Use for tracking current values like memory usage or queue length. |
| Histogram | Tracks the distribution of events, typically for latency or size measurements. | const histogram = new client.Histogram({name: 'request_duration_seconds',help: 'Duration of requests in seconds',buckets: [0.05, 0.1, 0.2, 0.5, 1, 2, 5]}); |
Use for tracking latency, response sizes, etc. |
| Summary | Similar to Histogram but provides quantile estimates. | const summary = new client.Summary({name: 'request_duration_summary',help: 'Summary of request durations',percentiles: [0.5, 0.9, 0.99]}); |
Use for tracking percentiles of latency or other distributions. |
Example: Instrumenting a Web Server
const client = require('prom-client');
const express = require('express');
// Create metrics
const httpRequestCounter = new client.Counter({
name: 'http_requests_total',
help: 'Total number of HTTP requests',
labelNames: ['method', 'status']
});
const httpRequestDuration = new client.Histogram({
name: 'http_request_duration_seconds',
help: 'Duration of HTTP requests in seconds',
buckets: [0.05, 0.1, 0.2, 0.5, 1, 2, 5]
});
const app = express();
app.use((req, res, next) => {
const end = httpRequestDuration.startTimer();
res.on('finish', () => {
end({ method: req.method, status: res.statusCode });
httpRequestCounter.inc({ method: req.method, status: res.statusCode });
});
next();
});
app.get('/', (req, res) => {
res.send('Hello World');
});
app.listen(3000);
7. Implementing Metric Aggregation
| Metric Type | Description | Aggregation Method | Use Case |
|---|---|---|---|
| counter | Tracks a monotonically increasing value | Summation | Request counts, error counts |
| gauge | Represents a single numerical value that can be modified | Last Value | Memory usage, CPU load |
| histogram | Tracks the distribution of values in buckets | Buckets + Sum | Response times, latency |
| summary | Tracks the distribution of values with quantiles | Quantiles + Sum | Latency percentiles, error rates |
Example: Histogram Aggregation
const histogram = new Histogram({
name: 'http_request_duration_seconds',
help: 'Duration of HTTP requests in seconds',
buckets: [0.05, 0.1, 0.2, 0.5, 1, 2, 5, 10]
});
// Record a sample
histogram.observe(0.3);
Example: Counter Aggregation
const counter = new Counter({
name: 'http_requests_total',
help: 'Total number of HTTP requests'
});
// Increment the counter
counter.inc();
8. Using Recording Rules Wisely
Recording rules in Prometheus allow you to precompute frequently used or expensive queries and store the results as new time series. This can improve query performance and reduce load on the Prometheus server.
| Rule Type | Description | Use Case | Best Practice |
|---|---|---|---|
| Recording Rules | Precompute and store results of expressions as new time series. | Reduce query load, improve dashboard performance, simplify complex queries. | Use meaningful names and labels to ensure clarity and maintainability. |
Example: Recording Rule for CPU Usage
groups:
- name: cpu_rules
rules:
- record: node_cpu:usage
expr: rate(node_cpu_seconds_total[5m]) * 100
rate() function is used to calculate the per-second rate of increase of the counter, which is then multiplied by 100 to get a percentage.
Best Practices for Recording Rules
- Performance: Use recording rules to precompute expensive queries.
- Maintainability: Name rules clearly and consistently.
- Resource Usage: Monitor memory and CPU usage to avoid overloading the Prometheus server.
- Labeling: Use descriptive labels to make rules easy to understand and manage.
9. Following Alert Design Patterns
| Pattern | Description | Best Practice |
|---|---|---|
| Silence Alerts | Use silence to suppress alerts during maintenance windows or known issues. | Define silence rules with clear labels and durations to avoid alert fatigue. |
| Alert Prioritization | Classify alerts by severity (e.g., critical, warning, info). | Use alerting rules with appropriate severity levels to ensure timely response. |
| Notification Deduplication | Prevent multiple notifications for the same alert event. | Implement deduplication logic in alertmanager to reduce noise and improve clarity. |
Example: Alertmanager Silence Configuration
silences:
- matchers:
- name: alertname
value: InstanceDown
startsAt: "2026-01-01T00:00:00Z"
endsAt: "2026-01-01T06:00:00Z"
comment: "Scheduled maintenance window"
Key Principles for Alert Design
- Relevance: Ensure alerts are actionable and relevant to the team.
- Clarity: Use descriptive alert names and messages to convey the issue clearly.
- Frequency: Avoid alert storms by grouping or suppressing related alerts.
10. Implementing Runbook Links
| Link Type | Description | Usage |
|---|---|---|
| runbook_url | URL to the runbook documentation | Used in alert annotations to provide context and remediation steps |
| runbook_text | Text to display as a link in the alert UI | Defaults to "Runbook" if not specified |
Example: Alert with Runbook Link
name: HighErrorRate
expr: rate(errors[5m]) > 10
annotations:
summary: "High error rate detected"
description: "The service is experiencing a high number of errors. See the runbook for troubleshooting steps."
runbook_url: "https://internal.docs.example.com/troubleshooting/service-errors"
runbook_text: "View Troubleshooting Guide"
11. Using External Labels
| Label Name | Description | Usage | Example |
|---|---|---|---|
| job | Identifies the job that is scraping the target. | Required for all scrape targets | job="prometheus" |
| instance | Uniquely identifies a specific instance within a job. | Used for grouping metrics by instance | instance="localhost:9090" |
| environment | Denotes the environment where the target is running (e.g., dev, staging, prod). | Used for filtering and grouping metrics | environment="production" |
| region | Represents the geographic region where the target is located. | Used for cross-region monitoring | region="us-west-1" |
Example: Setting External Labels in Prometheus Configuration
global:
external_labels:
environment: production
region: us-west-1
scrape_configs:
- job_name: "prometheus"
static_configs:
- targets: ["localhost:9090"]
12. Testing Configuration Changes
| Command | Description | Notes |
|---|---|---|
| promtool check config | Validates Prometheus configuration files | Checks syntax and rule validity |
| promtool check rules | Validates recording and alerting rules | Ensures rule syntax is correct |