All timings and bucket boundaries in this article are expressed in milliseconds (ms). Python’s
time.time() returns seconds — multiply by 1000 to get milliseconds before recording to a histogram that uses "ms" as its unit.What we’ll add
- A MeterProvider and Meter configured with a ConsoleMetricExporter (for local testing).
- Instruments:
- a counter for total requests,
- an up-down counter for concurrent in-flight requests,
- a histogram to record request durations.
- A minimal Flask app that records durations in
after_requestand updates the concurrent counter inbefore_request. - How to customize histogram bucket boundaries using a View and
ExplicitBucketHistogramAggregation.
Basic meter and instruments
Create the meter and instruments used by the Flask app. Save this asmetrics_setup.py.
| Instrument | Use case | Example (attributes or recorded value) |
|---|---|---|
| Counter | Track total number of requests | requests_counter.add(1, {"route": "/products", "method": request.method}) |
| UpDownCounter | Track number of requests currently in-flight | concurrent_requests.add(1) and concurrent_requests.add(-1) |
| Histogram | Record request duration in milliseconds | total_request_duration.record(latency_ms, {"route": request.path}) |
Calculating per-request latency in Flask
Use Flask’sbefore_request to capture the request start time and increment the concurrent counter. Use after_request to compute the latency, record it to the histogram, and decrement the concurrent counter.
Note: store the start time with time.time() (seconds) and convert to milliseconds when recording to the histogram (since the histogram’s unit is "ms").
- Restart the application and send multiple requests so the histogram sees a variety of latencies:
- The
ConsoleMetricExporterwill periodically print the aggregated metrics (here every 5 seconds). The histogram export containscount,sum,bucket_counts, andexplicit_bounds.
countis the total number of recorded observations.sumis the sum of all latencies in milliseconds.bucket_countsis cumulative: each element is the count of observations <= corresponding bound inexplicit_bounds.- For example, the count for a 75ms bucket includes observations from the 0, 5, 10, 25, 50, and 75ms buckets.
- Use min/max to quickly spot extreme values.
Make sure
request.start_time is set before after_request tries to compute latency. If your app raises an exception and after_request doesn’t run, consider registering teardown_request or an exception handler to ensure counters are adjusted and latencies are recorded or cleaned up appropriately.Customizing histogram buckets
Default SDK histogram buckets may not match your application’s latency profile. You can provide explicit bucket boundaries using a View andExplicitBucketHistogramAggregation. This example shows how to configure a MeterProvider with custom buckets (in milliseconds). Save as metrics_setup_with_buckets.py.
explicit_bounds equal to the buckets you provided:
| Bucket set (ms) | Typical use case |
|---|---|
[0, 5, 10, 25, 50, 75, 100, 250] | Low-latency services where most requests are under 250ms |
[0, 20, 30, 60, 100, 250, 2000] | Services with mixed latencies (quick endpoints and occasional long ops) |
[0, 100, 300, 1000, 3000, 10000] | Latency-sensitive background jobs or APIs with long tails |
When recording histogram values, ensure the recorded unit matches the histogram unit. This example records latencies in milliseconds because the histogram’s
unit is set to "ms".Quick troubleshooting & tips
- If you see all observations falling into a single bucket, your boundaries likely don’t match your observed latencies — widen or shift the buckets.
- Use attributes (e.g.,
route,method) to filter or split the histogram for more granular insights. For example:total_request_duration.record(latency_ms, {"route": request.path, "method": request.method})
- For production, replace
ConsoleMetricExporterwith a metrics backend exporter (Prometheus, OTLP, etc.) appropriate to your observability stack.
Links and references
- Flask: https://flask.palletsprojects.com/
- OpenTelemetry Python Metrics: https://opentelemetry.io/docs/instrumentation/python/metrics/
- OpenTelemetry SDK: https://github.com/open-telemetry/opentelemetry-python
- ConsoleMetricExporter (example used for local testing): https://opentelemetry-python.readthedocs.io/