---
title: "Normalize span names with semantic conventions | Grafana Cloud documentation"
description: "Describes how Adaptive Traces rewrites span names to follow OpenTelemetry semantic conventions so that high-cardinality names collapse into a stable, queryable form."
---

> For a curated documentation index, see [llms.txt](/llms.txt). For the complete documentation index, see [llms-full.txt](/llms-full.txt).

# Normalize span names with semantic conventions

Adaptive Traces can rewrite span names to follow [OpenTelemetry semantic conventions](https://opentelemetry.io/docs/specs/semconv/) before traces are sampled and stored. It rebuilds each span name from the semantic-convention attributes already present on the span, for example combining `http.request.method` with `http.route` to produce `GET /api/users/{id}`. This gives a consistent name regardless of how each service originally labeled its spans, which matters most for generated span metrics because every unique name becomes its own time series.

Span name normalization is opt-in. When it’s enabled, Adaptive Traces processes each span during ingestion, replaces the span name, and preserves the original value in the `grafana.original_span_name` attribute so you can always recover it.

> Note
> 
> Normalization combines semantic-convention attributes that already exist on the span into a canonical name. It doesn’t parse values or infer low-cardinality forms, so the result is only low-cardinality when the source attributes already are. For an HTTP server span, for example, it builds the name from `http.route`, so your instrumentation must set `http.route` to a template such as `/api/users/{id}`, not the raw path `/api/users/7f3a`; otherwise the identifier carries straight into the new name. Other span kinds derive their name from their own attributes, such as `url.template` for HTTP client spans, `rpc.service`/`rpc.method` for RPC, and `db.operation.name` for database spans. Refer to [Examples](#examples).

## When to use normalization

Span name normalization helps most when services name their spans inconsistently, or when span names carry high-cardinality values such as raw URL paths, IDs, or query strings. Inconsistent names are harder to group in TraceQL, service graphs, and dashboards, and high-cardinality names inflate the cost of generated span metrics, because every unique name becomes its own time series.

Enable normalization when:

- Span names contain identifiers or paths that you don’t want in queries, service graphs, or span metrics.
- You generate span metrics and want to keep their cardinality predictable.

You might not need it when:

- Your instrumentation already produces clean, templated, low-cardinality span names. Normalization rebuilds names from attributes that are already on the span, so it adds little value when those names are already canonical.
- The spans you care about don’t carry the required semantic-convention attributes. Normalization only uses attributes that are already present, so spans without them are left unchanged or fall back to a coarse name such as the request method alone. Refer to [What gets normalized](#what-gets-normalized).

Either way, enabling normalization changes the names that sampling policies, TraceQL queries, dashboards, and span metrics see, so review what depends on the original names before you turn it on.

## What gets normalized

Normalization reads the semantic-convention attributes already present on each span and rewrites the span name to a canonical form. The following span kinds are covered:

Expand table

| Span kind       | Source attributes                               | Normalized span name  |
|-----------------|-------------------------------------------------|-----------------------|
| HTTP server     | `http.request.method` + `http.route`            | `GET /users/{id}`     |
| HTTP client     | `http.request.method` + `url.template`          | `GET /orders/{id}`    |
| RPC server      | `rpc.system` + `rpc.service` + `rpc.method`     | `UserService/GetUser` |
| RPC client      | `rpc.system` + `rpc.service` + `rpc.method`     | `UserService/GetUser` |
| Messaging       | `messaging.system` + operation + destination    | `publish orders`      |
| Database client | `db.system.name` + `db.operation.name` + target | `SELECT users`        |

## How it works

1. Adaptive Traces observes each span as it’s ingested.
2. If the span matches one of the supported patterns, its name is rewritten using the attributes already on the span.
3. The original span name is copied into the `grafana.original_span_name` attribute, but only on spans that were actually rewritten.
4. The normalized span continues through the rest of the Adaptive Traces pipeline, including sampling policies, rate limiting, and storage, using its new name.

## Examples

The span kind, the original name, and the attributes on the span together determine the result. The original name is copied to `grafana.original_span_name` only when the name actually changes.

Expand table

| Span kind   | Original span name    | Span attributes                                                             | Normalized span name                                      |
|-------------|-----------------------|-----------------------------------------------------------------------------|-----------------------------------------------------------|
| HTTP server | `GET /api/users/7f3a` | `http.request.method=GET`, `http.route=/api/users/{id}`                     | `GET /api/users/{id}`                                     |
| HTTP server | `GET /api/users/7f3a` | `http.request.method=GET`, `http.route=/api/users/7f3a`                     | `GET /api/users/7f3a` (no change)                         |
| HTTP server | `HTTP GET`            | `http.request.method=GET` (no `http.route`)                                 | `GET`                                                     |
| HTTP client | `GET /orders/123`     | `http.request.method=GET`, `url.template=/orders/{id}`                      | `GET /orders/{id}`                                        |
| HTTP client | `GET`                 | `http.request.method=GET`, `http.route=/api/users/{id}` (no `url.template`) | `GET` (client spans use `url.template`, not `http.route`) |

> Warning
> 
> Anything that depends on the original span name may need to be updated after you enable normalization. This includes sampling policies that match on span name (for example, OTTL conditions referencing `name`), TraceQL queries and saved Explore views, dashboard panels, alert rules, and derived span metrics. Review and update these before enabling normalization. Policies that match on span attributes such as `http.request.method` or `db.system.name` are not affected.

## Impact on stored traces

- **Span names in Tempo are the normalized values.** Queries, service graphs, and operation lists reflect the normalized form.
- **The original span name is preserved** in the `grafana.original_span_name` attribute on rewritten spans. You can always recover it through Explore or trace search.
- **Span attributes are not modified.** Only the span name changes.
- **Traces that don’t match a supported pattern are untouched**, and no `grafana.original_span_name` attribute is added.

## Enable normalization

1. Navigate to **Adaptive Traces**.
2. Open the **Overview** page.
3. Enable span name normalization.

The setting takes effect within a minute. Only spans ingested after that point are normalized; previously stored traces and metrics are not rewritten.

## Disable normalization

1. Navigate to **Adaptive Traces**.
2. Open the **Overview** page.
3. Disable span name normalization.

Span names ingested after normalization is disabled are stored as-is. Previously normalized traces are not reverted.
