DEV Community

Modern Observability Practices: Instrumenting Python Apps with OpenTelemetry & Prometheus

By Renzo Fernando Loyola Vilca

The landscape of software development has shifted. We no longer just "monitor" applications to see if they are up or down; we demand observability to understand why they behave the way they do in production.

As distributed systems become more complex, relying on closed, vendor-specific monitoring tools is a thing of the past. In 2026, the industry standard revolves around open-source tools and open standards. In this article, we will explore core observability practices and demonstrate how to instrument a Python application using OpenTelemetry and Prometheus.

What is Observability?

Observability is a measure of how well internal states of a system can be inferred from knowledge of its external outputs. In software, this relies heavily on three primary pillars (telemetry data):

  1. Logs: Immutable records of discrete events that happened over time.
  2. Metrics: Numeric data measured over time (e.g., CPU usage, active users, error rates).
  3. Traces: The lifecycle of a request as it moves through multiple services.

The modern best practice is Observability as Code—treating telemetry configuration just like application code, standardizing it across environments, and ensuring we don't end up with vendor lock-in. This is where OpenTelemetry (OTel) shines.

Real-World Example: Python + OpenTelemetry + Prometheus

Instead of manually injecting specific vendor SDKs into our code, we can use OpenTelemetry to generate metrics and Prometheus to scrape and store them.

Imagine we are building a simple dice-rolling microservice. We want to track how many times specific numbers are rolled.

1. The Setup

First, you need to install the required Python packages. OpenTelemetry provides standard libraries, and we will use the Prometheus exporter.

pip install flask opentelemetry-api opentelemetry-sdk opentelemetry-exporter-prometheus prometheus-client
Enter fullscreen mode Exit fullscreen mode


bash

  1. The Implementation Code Here is how you instrument a simple Flask application. We are setting up a local Prometheus metric reader that will expose our application metrics on a dedicated port.

from random import randint
from flask import Flask
from opentelemetry import metrics
from opentelemetry.exporter.prometheus import PrometheusMetricReader
from opentelemetry.metrics import set_meter_provider
from opentelemetry.sdk.metrics import MeterProvider
from prometheus_client import start_http_server

1. Start Prometheus metrics server on port 8000

start_http_server(8000)

2. Configure OpenTelemetry to use the Prometheus Metric Reader

reader = PrometheusMetricReader()
provider = MeterProvider(metric_readers=[reader])
set_meter_provider(provider)

3. Acquire a meter to record measurements

meter = metrics.get_meter("diceroller.meter")

4. Create a Counter metric

roll_counter = meter.create_counter(
"dice.rolls",
description="The number of rolls by roll value",
)

app = Flask(name)

@app.route("/rolldice")
def roll_dice():
result = randint(1, 6)

# Add 1 to the counter, labeling it with the specific rolled value
roll_counter.add(1, {"roll.value": str(result)})

return f"You rolled a {result}!\n"
Enter fullscreen mode Exit fullscreen mode

if name == "main":
# Run the main app on port 8080
app.run(port=8080)

  1. How It Works Prometheus Server Integration: The start_http_server(8000) function spins up a secondary endpoint. Prometheus will periodically scrape this endpoint (e.g., http://localhost:8000/) to collect the metric data.

The Meter and Counter: We initialize roll_counter. Every time a user hits the /rolldice endpoint, the counter increments by 1.

Dimensionality (Labels): Notice the {"roll.value": str(result)} dictionary. Instead of creating six different metrics for each dice side, we create one metric and tag it with an attribute. This is a crucial practice for high-cardinality data analytics.

Top comments (0)