- configuring an OpenTelemetry MeterProvider with a console exporter and periodic reader,
- creating a Counter metric,
- incrementing the counter on incoming requests,
- adding attributes to break metrics down by route and method,
- centralizing instrumentation using a
before_requesthook.
This example uses the
ConsoleMetricExporter with a PeriodicExportingMetricReader configured for a 5s export interval so you can see metrics printed to the console while testing.The Flask development server is not suitable for production. Use a production WSGI server such as
gunicorn or uwsgi for deployments.1. Configure the meter and console exporter
Create a function to configure the MeterProvider and attach a periodic exporting reader that prints metrics every 5 seconds. Then get a named meter for your application:shopping-app which you’ll use to create instruments.
2. Create a Counter metric
A Counter is appropriate for values that only increase over time (like request counts). Create a Counter and store it:"1" for counts, "By" for bytes, or "s" for seconds when appropriate.
3. Instrument routes by incrementing the counter
A simple approach is to callrequests_counter.add(1) in every route handler. Example handlers:
PeriodicExportingMetricReader collects metrics and the ConsoleMetricExporter prints them every 5 seconds.
Example run output for the Flask dev server:
4. Add attributes to break metrics down by route and method
To see per-route and per-method counts while retaining a single canonical metric name, add attributes when calling.add(...). Attributes attach metadata to metric data points. Example:
route="/products" and method="POST". Example (abridged):
5. Why use attributes instead of separate metrics per route?
Creating separate metrics likeproducts_requests_total and cart_requests_total is possible but has downsides:
| Approach | Pros | Cons |
|---|---|---|
http_requests_total with attributes (route, method) | Single canonical metric; easy aggregation across routes; flexible breakdowns | Requires careful attribute cardinality control |
Separate metrics per route (products_requests_total) | Simple per-route metric names | Proliferation of metrics, harder to aggregate across all routes |
6. Centralize instrumentation with a before_request hook
Instead of invokingrequests_counter.add(...) in every handler, increment the counter once for all requests using Flask’s before_request hook. This reduces duplication and ensures consistent instrumentation:
7. Complete example (main.py)
A minimal, complete example showing configuration, centralized instrumentation, and a couple of routes:8. Summary and best practices
- Use a Counter (
http_requests_total) to count incoming HTTP requests. - Attach attributes such as
routeandmethod({"route": route, "method": request.method}) to break metrics down while preserving a single canonical metric name. - Centralize counting with
@app.before_requestto avoid repeating instrumentation across handlers. - During development,
ConsoleMetricExporter+PeriodicExportingMetricReaderhelps you see metric output. For production, configure a proper exporter/backend. - Avoid using high-cardinality attribute values (e.g., raw user IDs or full URLs). Prefer route patterns (
/products/<int:id>) to keep cardinality manageable.
Links and References
- OpenTelemetry Python Metrics: https://opentelemetry.io/
- Flask Documentation: https://flask.palletsprojects.com/
- Gunicorn (production WSGI server): https://gunicorn.org/
- Prometheus (metrics backend): https://prometheus.io/