> ## 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.

# Labels

> Explains using Prometheus labels to track per-path and per-method HTTP request metrics, advantages over per-path metrics, aggregation examples, and warnings about label cardinality.

As your application grows to expose multiple endpoints—for example, endpoints for cars (`/cars`) and boats (`/boats`)—you may want both a global request count and per-path counts. Incrementing a single global counter for every endpoint tracks total traffic, but it doesn't tell you how that traffic is distributed across paths.

Example endpoints:

```python theme={null}
@app.get("/cars")
def get_cars():
    REQUESTS.inc()
    return ["toyota", "honda", "mazda", "lexus"]

@app.post("/cars")
def create_cars():
    REQUESTS.inc()
    return "Create Car"

@app.get("/boats")
def get_boats():
    REQUESTS.inc()
    return ["boat1", "boat2", "boat3"]

@app.post("/boats")
def create_boat():
    REQUESTS.inc()
    return "Create Boat"
```

If you want per-path totals, one naive solution is creating a distinct Counter for each path and incrementing the appropriate one:

```python theme={null}
from prometheus_client import Counter

CAR_REQUESTS = Counter(
    "requests_cars_total",
    "Total number of requests for /cars path"
)
BOATS_REQUESTS = Counter(
    "requests_boats_total",
    "Total number of requests for /boats path"
)

@app.get("/cars")
def get_cars():
    CAR_REQUESTS.inc()
    return ["toyota", "honda", "mazda", "lexus"]

@app.post("/cars")
def create_cars():
    CAR_REQUESTS.inc()
    return "Create Car"

@app.get("/boats")
def get_boats():
    BOATS_REQUESTS.inc()
    return ["boat1", "boat2", "boat3"]

@app.post("/boats")
def create_boat():
    BOATS_REQUESTS.inc()
    return "Create Boat"
```

This approach works, but it scales poorly. You end up with one metric name per path, making application-wide aggregations awkward: queries must reference every metric name and you must remember to update instrumentation whenever you add or remove endpoints. This increases maintenance overhead and makes queries error-prone.

A better practice is to use labels (also called dimensions). Define a single metric and add a `path` label at metric creation time. Then, when incrementing, provide the label value.

```python theme={null}
from prometheus_client import Counter

REQUESTS = Counter(
    "http_requests_total",
    "Total number of requests",
    ["path"]
)

@app.get("/cars")
def get_cars():
    REQUESTS.labels("/cars").inc()
    return ["toyota", "honda", "mazda", "lexus"]

@app.post("/cars")
def create_cars():
    REQUESTS.labels("/cars").inc()
    return "Create Car"

@app.get("/boats")
def get_boats():
    REQUESTS.labels("/boats").inc()
    return ["boat1", "boat2", "boat3"]

@app.post("/boats")
def create_boat():
    REQUESTS.labels("/boats").inc()
    return "Create Boat"
```

With this pattern, each distinct `path` value produces a separate time series under the same metric name `http_requests_total`. That makes filtering and aggregation simple and reliable.

<Frame>
  <img src="https://mintcdn.com/kodekloud-c4ac6d9a/EOkHqyG4hdv97La7/images/Prep-Course-Prometheus-Certified-Associate-PCA-Certification/Application-Instrumentation/Labels/dark-themed-command-line-interface-labels.jpg?fit=max&auto=format&n=EOkHqyG4hdv97La7&q=85&s=b1ce971b61c72fb88a0c08d369660f72" alt="The image shows a dark-themed command-line interface with a text input area and the label &#x22;Labels&#x22; at the top. There is a small icon of an eye on the bottom right." width="1920" height="1080" data-path="images/Prep-Course-Prometheus-Certified-Associate-PCA-Certification/Application-Instrumentation/Labels/dark-themed-command-line-interface-labels.jpg" />
</Frame>

Prometheus will expose each labeled time series. For example:

```bash theme={null}
$ http_requests_total{path="/cars"}
http_requests_total{path="/cars"} 5.0

$ http_requests_total{path="/boats"}
http_requests_total{path="/boats"} 2.0
```

Listing the metric without selectors returns all label variations:

```bash theme={null}
$ http_requests_total
http_requests_total{path="/cars"} 5.0
http_requests_total{path="/boats"} 2.0
```

To compute the total across all paths use PromQL aggregation:

```bash theme={null}
$ sum(http_requests_total)
# => 7.0
```

You can also add multiple labels to capture more dimensions (for example, `path` and HTTP `method`):

```python theme={null}
from prometheus_client import Counter

REQUESTS = Counter(
    "http_requests_total",
    "Total number of requests",
    ["path", "method"]
)

@app.get("/cars")
def get_cars():
    REQUESTS.labels("/cars", "GET").inc()
    return ["toyota", "honda", "mazda", "lexus"]

@app.post("/cars")
def create_cars():
    REQUESTS.labels("/cars", "POST").inc()
    return "Create Car"

@app.get("/boats")
def get_boats():
    REQUESTS.labels("/boats", "GET").inc()
    return ["boat1", "boat2"]

@app.post("/boats")
def create_boat():
    REQUESTS.labels("/boats", "POST").inc()
    return "Create Boat"
```

Label order matters: the first label value corresponds to `path`, the second to `method`, as declared in the metric.

Query examples with multiple labels:

```bash theme={null}
$ http_requests_total{method="GET"}
http_requests_total{path="/cars",method="GET"} 5.0
http_requests_total{path="/boats",method="GET"} 2.0
```

And combining selectors still works:

```bash theme={null}
$ http_requests_total{path="/cars"}
http_requests_total{path="/cars",method="GET"} 5.0
http_requests_total{path="/cars",method="POST"} 1.0
```

Use labels to keep metrics organized and queryable, but be mindful of cardinality: labels that can take many unique values (like user IDs, session tokens, or request IDs) cause a large number of time series and can harm performance and storage.

| Approach                                    | Pros                                                    | Cons                                                                   | When to use                                                        |
| ------------------------------------------- | ------------------------------------------------------- | ---------------------------------------------------------------------- | ------------------------------------------------------------------ |
| One metric per path (multiple metric names) | Simple to implement                                     | Hard to aggregate, high maintenance, error-prone when adding endpoints | Very small projects with few endpoints and no need for aggregation |
| Single metric with labels (recommended)     | Easy aggregation, fewer metric names, flexible querying | Risk of high cardinality if labels are unbounded                       | Most web services; use for per-path, per-method, per-status counts |

<Callout icon="lightbulb" color="#1CB2FE">
  Use labels to keep metrics organized and queryable. However, be cautious about label cardinality: labels with high cardinality (for example, user IDs, session IDs, or other highly unique values) can produce a huge number of time series and cause performance and storage problems. Restrict labels to a small, well-defined set of values.
</Callout>

Links and references

* [Prometheus — Overview](https://prometheus.io/docs/introduction/overview/)
* [Prometheus — Querying basics (PromQL)](https://prometheus.io/docs/prometheus/latest/querying/basics/)
* [prometheus\_client (Python) documentation](https://github.com/prometheus/client_python)

<CardGroup>
  <Card title="Watch Video" icon="video" cta="Learn more" href="https://learn.kodekloud.com/user/courses/prometheus-certified-associate-pca/module/0c0155c7-00c8-4ca2-a061-e66baa1a3216/lesson/c0e01897-34ab-4c6f-a288-e25b645e8c5c" />
</CardGroup>
