> ## Documentation Index
> Fetch the complete documentation index at: https://notes.kodekloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Demo Updown Counter

> Explains adding an up down counter to Flask using OpenTelemetry to track concurrent in-progress requests alongside a cumulative requests counter

Previously we added a cumulative request counter to measure the total number of requests handled by an application and broke it down by attributes such as route and method. In this article we'll add a second metric to track how many requests are actively being processed at any given moment — i.e., concurrent in-progress requests.

Goals

* Keep the cumulative total requests metric (`http_requests_total`).
* Add a metric that represents the number of concurrent requests currently being processed (a value that can increase and decrease).

Why a different metric type is needed
A traditional counter only increases, which is ideal for cumulative totals. To represent in-progress concurrency — a value that should increment when a request starts and decrement when it finishes — we need a metric type that can go up and down.

Compare the options:

| Metric type     | Behavior                                           | When to use                                                                              |
| --------------- | -------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| Counter         | Monotonic (only increases)                         | Total number of events processed (e.g., requests completed).                             |
| Gauge           | Arbitrary instantaneous value                      | Point-in-time measurements that aren't relative to previous values (e.g., memory usage). |
| Up-down counter | Increment and decrement relative to previous value | Active/in-progress counts that should rise and fall (e.g., concurrent requests).         |

<Callout icon="lightbulb" color="#1CB2FE">
  For tracking concurrent requests, an up-down counter is the preferred choice because its value moves up and down relative to the previous state.
</Callout>

Complete Flask example

* Configures a MeterProvider with a console exporter.
* Creates a cumulative `http_requests_total` counter (incremented when responses are sent).
* Creates a `concurrent_requests` up-down counter (incremented at request start, decremented after response).
* Uses Flask `before_request` and `after_request` hooks to manage the concurrent counter.
* Adds a slow route to let you observe concurrent requests.

```python theme={null}
# app.py
import time
from flask import Flask, request
from opentelemetry.metrics import set_meter_provider, get_meter_provider
from opentelemetry.sdk.metrics.export import ConsoleMetricExporter, PeriodicExportingMetricReader
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.resources import Resource

def configure_meter():
    exporter = ConsoleMetricExporter()
    reader = PeriodicExportingMetricReader(exporter, export_interval_millis=5000)
    provider = MeterProvider(metric_readers=[reader], resource=Resource.create())
    set_meter_provider(provider)
    return get_meter_provider().get_meter(name="shopping-app", version="0.1.2")

meter = configure_meter()

# Cumulative counter: total requests processed (completed)
requests_counter = meter.create_counter(
    "http_requests_total",
    description="Total number of requests processed by the application",
    unit="1"
)

# Up-down counter: concurrent requests in progress
concurrent_requests = meter.create_up_down_counter(
    "concurrent_requests",
    description="Total number of requests in progress",
    unit="1"
)

app = Flask(__name__)

@app.before_request
def before_request():
    # Increment concurrent requests when a request starts.
    # Add attributes so we can break down by route/method if desired.
    concurrent_requests.add(1, {"route": request.path, "method": request.method})

@app.after_request
def after_request(response):
    # Decrement concurrent requests when the response is about to be sent.
    concurrent_requests.add(-1, {"route": request.path, "method": request.method})

    # Increment total requests processed (completed) here so we count only finished requests.
    requests_counter.add(1, {"route": request.path, "method": request.method})

    return response

@app.get("/products")
def get_products():
    return "Get All Products"

@app.get("/products/<int:id>")
def get_product(id):
    return f"Getting product detail for {id}"

@app.post("/products")
def create_product():
    # Simulate a slow route to observe concurrent requests.
    time.sleep(10)
    return "Creating Product", 201

if __name__ == "__main__":
    app.run(debug=True)
```

How it works

* `before_request` runs before Flask dispatches to the route handler. We increment `concurrent_requests` there (with `route` and `method` attributes) to indicate a request has started.
* `after_request` runs after the route handler returns but before the response is sent. We decrement `concurrent_requests` and increment `http_requests_total` here, so the total only counts completed responses.
* The slow `POST /products` route uses `time.sleep(10)` so you can make multiple concurrent requests and observe `concurrent_requests` rising while requests are still in-flight.

Testing with curl

* Fast route:

```bash theme={null}
curl -X GET http://127.0.0.1:5000/products
```

* Slow route (takes \~10 seconds to return):

```bash theme={null}
curl -X POST http://127.0.0.1:5000/products
```

Example console metric exports (sample)

```json theme={null}
{
  "attributes": {
    "route": "/products",
    "method": "POST"
  },
  "start_time_unix_nano": 1757999681879945400,
  "time_unix_nano": 1757997074651432000,
  "value": 1,
  "exemplars": []
}
```

```json theme={null}
{
  "attributes": {
    "route": "/products",
    "method": "GET"
  },
  "start_time_unix_nano": 1757996981879945000,
  "time_unix_nano": 1757996981948106000,
  "value": 2,
  "exemplars": []
}
```

Notes and practical tips

* Place the total requests increment in `after_request` so you count only completed responses.
* Attach attributes like `route` and `method` with each metric call to enable dimensional filtering and per-endpoint breakdowns. For example: `{"route": request.path, "method": request.method}`.
* Export interval controls how often metrics are pushed to the exporter. In this example the reader is configured with `export_interval_millis=5000` to export every 5 seconds.
* If your application can raise exceptions during request handling, consider ensuring the decrement always runs (see warning below).

<Callout icon="warning" color="#FF6B6B">
  If a request handler raises an exception, `after_request` may not always run. To guarantee the concurrent counter is decremented in all cases, consider using Flask's `teardown_request` (which runs for both successful and failed requests) or ensure exception paths also decrement the up-down counter.
</Callout>

Configuration summary

| Setting                    | Purpose                                    | Example                                                                |
| -------------------------- | ------------------------------------------ | ---------------------------------------------------------------------- |
| Exporter                   | Sends metric data to a destination         | `ConsoleMetricExporter()`                                              |
| Reader                     | Controls export frequency                  | `PeriodicExportingMetricReader(exporter, export_interval_millis=5000)` |
| Meter name/version         | Identify the instrumenting library/service | `get_meter(name="shopping-app", version="0.1.2")`                      |
| Attributes on metric calls | For dimensional metrics and filtering      | `{"route": request.path, "method": request.method}`                    |

Links and references

* Flask documentation: [https://flask.palletsprojects.com/](https://flask.palletsprojects.com/)
* OpenTelemetry Python metrics: [https://opentelemetry.io/docs/instrumentation/python/](https://opentelemetry.io/docs/instrumentation/python/)
* OpenTelemetry metrics SDK: [https://github.com/open-telemetry/opentelemetry-python](https://github.com/open-telemetry/opentelemetry-python)

This pattern gives you both a cumulative throughput signal (total requests processed) and a live concurrency signal (current in-progress requests), enabling better observability and capacity planning for your web service.

<CardGroup>
  <Card title="Watch Video" icon="video" cta="Learn more" href="https://learn.kodekloud.com/user/courses/prep-course-opentelemetry-certified-associate-certification-otca/module/6fce855c-4275-48c0-9297-a7f98a292285/lesson/5fbd3985-9c0d-489f-86a8-88c10f4724ff" />
</CardGroup>
