> ## 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 Async Gauge

> Demonstrates adding an observable gauge to a Flask app using OpenTelemetry and psutil to poll memory usage, with MeterProvider setup and periodic console metric exporting.

In this lesson we add a new metric to track memory usage for a Flask API ("shopping-app") using OpenTelemetry for Python. We choose an observable (polled) gauge implemented with psutil and export metrics to the console via a PeriodicExportingMetricReader. This demonstrates setting up a MeterProvider, registering a polled gauge callback, and using synchronous counters to instrument requests.

Why choose an observable gauge?

* Counters only increase; they are not suitable for values that go up and down.
* Up-down counters are for values your code explicitly increments/decrements (e.g., concurrent requests).
* Memory usage fluctuates and is independent of prior values, so a gauge is the correct metric type.
* An observable (async) gauge lets the SDK poll the current memory values periodically, so you do not need to manually update the metric inside your application code.

<Callout icon="lightbulb" color="#1CB2FE">
  Observable gauges are polled by the metrics SDK (via the metric reader). You must provide a callback function that returns the current observations; the SDK will call that callback periodically according to the metric reader's export interval.
</Callout>

## Example: Flask app with an observable gauge (polled memory)

This self-contained example shows:

* Meter/provider setup with a ConsoleMetricExporter and 5s periodic exporting.
* Synchronous metrics: request counters and an up-down counter for concurrent requests.
* An observable gauge that reports RSS and VMS memory values for the running process.

```python theme={null}
# main.py
import os
import time

import psutil
from flask import Flask, request

from opentelemetry.metrics import set_meter_provider, get_meter_provider
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import ConsoleMetricExporter, PeriodicExportingMetricReader

# --- Metric provider / exporter setup ---
exporter = ConsoleMetricExporter()
reader = PeriodicExportingMetricReader(exporter, export_interval_millis=5000)
resource = Resource.create({
    "telemetry.sdk.language": "python",
    "telemetry.sdk.name": "opentelemetry",
    "telemetry.sdk.version": "1.37.0",
    "service.name": "shopping-app"
})
provider = MeterProvider(metric_readers=[reader], resource=resource)
set_meter_provider(provider)
meter = get_meter_provider().get_meter(name="shopping-app", version="0.1.2")

# --- Synchronous metrics (example) ---
requests_counter = meter.create_counter(
    "http_requests_total",
    description="Total number of requests processed by the application",
    unit="1"
)
concurrent_requests = meter.create_up_down_counter(
    "concurrent_requests",
    description="Total number of requests in progress",
    unit="1"
)

# --- Observable gauge (polled) for memory usage ---
process = psutil.Process(os.getpid())

def memory_usage_callback(options):
    """
    This callback will be called periodically by the metric reader.
    It must return an iterable of Observation objects or (value, attributes) tuples.
    Each observation holds a numeric value and optional attributes.
    """
    mem_info = process.memory_info()  # returns an object with rss and vms (and more)
    observations = [
        (mem_info.rss, {"type": "rss"}),  # resident set size (bytes)
        (mem_info.vms, {"type": "vms"}),  # virtual memory size (bytes)
    ]
    return observations

meter.create_observable_gauge(
    "application_memory_usage",
    description="Memory usage of application",
    unit="By",
    callbacks=[memory_usage_callback]
)

# --- Flask app (example endpoints) ---
app = Flask(__name__)

@app.before_request
def before_request():
    concurrent_requests.add(1)

@app.after_request
def after_request(response):
    concurrent_requests.add(-1)
    return response

@app.get("/products")
def get_products():
    requests_counter.add(1, {"route": "/products", "method": request.method})
    # Simulate some work
    time.sleep(1)
    return "Get All Products"

@app.get("/products/<int:id>")
def get_product(id):
    requests_counter.add(1, {"route": "/products/<id>", "method": request.method})
    return f"Get product {id}"

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

Install psutil (used to read the process memory):

```bash theme={null}
pip install psutil
```

### Example console-exported metric snapshot

The ConsoleMetricExporter prints resource and scope metrics as JSON-like structures. With the periodic reader configured to 5000 ms, the observable gauge will be polled every 5 seconds and you will see observations for `rss` and `vms` within the exported metrics.

```json theme={null}
{
  "resource_metrics": [
    {
      "resource": {
        "attributes": {
          "telemetry.sdk.language": "python",
          "telemetry.sdk.name": "opentelemetry",
          "telemetry.sdk.version": "1.37.0",
          "service.name": "shopping-app"
        }
      },
      "schema_url": "",
      "scope_metrics": [
        {
          "scope": {
            "name": "shopping-app",
            "version": "0.1.2"
          },
          "metrics": [
            {
              "name": "application_memory_usage",
              "description": "Memory usage of application",
              "unit": "By",
              "data_points": [
                { "value": 34526720, "attributes": { "type": "rss" } },
                { "value": 12163456, "attributes": { "type": "vms" } }
              ]
            },
            {
              "name": "http_requests_total",
              "data_points": [ /* ... */ ]
            }
          ]
        }
      ]
    }
  ]
}
```

## Quick reference: metric types and when to use them

| Metric Type      | Use Case                                               | When to use                                           |
| ---------------- | ------------------------------------------------------ | ----------------------------------------------------- |
| Counter          | Monotonic counts of events                             | `http_requests_total` (increment on each request)     |
| Up-down counter  | Values you explicitly increase/decrease in code        | `concurrent_requests` (add 1 / add -1)                |
| Observable gauge | Polled values that can increase/decrease independently | `application_memory_usage` (RSS/VMS polled by psutil) |

## Notes and best practices

* Use `rss` (resident set size) to monitor actual memory resident in RAM. Use `vms` to inspect the total virtual address space requested by the process.
* The callback receives an `options` parameter (not used in this example). Always return an iterable (e.g., a list) of Observation objects or `(value, attributes)` tuples.
* The periodic exporter polls your observable callbacks at the configured interval (5 seconds in this example).
* For production, run behind a proper WSGI server (e.g., Gunicorn, uWSGI) and replace the ConsoleMetricExporter with a production-ready exporter such as OTLP.

<Callout icon="warning" color="#FF6B6B">
  Do not use Flask's development server in production. Configure a production WSGI server and a proper exporter (e.g., OTLP) for reliable metric delivery and performance.
</Callout>

## Links and references

* OpenTelemetry Python metrics: [https://opentelemetry.io/docs/instrumentation/python/](https://opentelemetry.io/docs/instrumentation/python/)
* OpenTelemetry metrics SDK: [https://opentelemetry.io/docs/reference/specification/metrics/](https://opentelemetry.io/docs/reference/specification/metrics/)
* psutil (process utilities for Python): [https://pypi.org/project/psutil/](https://pypi.org/project/psutil/)
* Flask documentation: [https://flask.palletsprojects.com/](https://flask.palletsprojects.com/)
* OTLP exporter information: [https://github.com/open-telemetry/opentelemetry-specification/tree/main/specification/protocol/otlp](https://github.com/open-telemetry/opentelemetry-specification/tree/main/specification/protocol/otlp)

<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/e7a4e55e-dafc-4c51-b19f-f20e08b0eaee" />
</CardGroup>
