Skip to main content

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
Note: Consistent naming improves readability and maintainability of Prometheus metrics.

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"}
http_requests_total{job="api-server", endpoint="/api/v1/users", status_code="200"}
Note: Prometheus stores each unique combination of metric name and label values as a time series. Therefore, minimizing label cardinality is crucial for performance.

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)
Note: Instrumentation libraries provide a standardized way to expose metrics to Prometheus. They handle the exposition of metrics in the correct format and make it easy to integrate with various frameworks and environments.

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."
Note: The Four Golden Signals are a foundational approach to system monitoring, focusing on latency, traffic, errors, and saturation. They help ensure that systems remain responsive and healthy under varying loads.

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
Note: Use promtool check rules to validate recording rules and alerting rules for missing or invalid labels.
Warning: Metrics without any labels will not be queryable in Prometheus, leading to silent data loss.

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);
Note: When using direct instrumentation, ensure that metrics are registered before the application starts to avoid runtime errors.

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();
Note: Histograms and summaries are ideal for aggregating distribution data, while counters and gauges are used for simple value tracking.

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
Note: The 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.
Warning: Avoid creating too many recording rules, as they can increase memory usage and complexity. Only record rules that provide significant performance benefits.

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.
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
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"
Note: Ensure the runbook URL is accessible and up-to-date to provide accurate troubleshooting information.

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"]
Note: External labels are applied to all metrics scraped by Prometheus and can be used for filtering or grouping in queries.

12. Testing Configuration Changes

Example: Testing Prometheus Configuration

promtool check config /path/to/prometheus.yml
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
Note: Always test configuration changes in a staging environment before applying to production.
Warning: Invalid configurations can cause Prometheus to fail to start or scrape targets.

Testing Configuration Changes

  • Use promtool check config to validate YAML syntax
  • Validate alerting and recording rules with promtool check rules
  • Test in a non-production environment first
  • Monitor for configuration reload errors in logs

Was this article helpful?