# Transformations

> Use YAML-based transformations and Expr filters to rename attributes, parse payloads, sample or drop telemetry, and build full-text indexes before data reaches Uptrace.

Transformations let you modify ingested telemetry before it reaches storage. Each operation is a small YAML rule applied to spans, logs, events, or metric datapoints — in the order you define. Use them to clean up noisy data, normalize attribute names, reduce cardinality, and control costs.

<video autoPlay="true" loop="true" muted="true" playsInline="true">
<source src="/features/transformations/transformations-overview.mp4" type="video/mp4" />
</video>

To create an operation:

1. Go to **Project → Transformations** in the left navigation.
2. Click **New Operation → New operation from YAML**.
3. Paste a YAML rule and click **Create**.

## Common recipes

### Drop health check spans

```yaml
name: Drop healthcheck spans
scope: [spans]
type: drop
if: spanName() == "GET /healthz" && spanStatusCode() != "error"
```

### Sample successful requests

```yaml
name: Sample 10% of successful spans
scope: [spans]
type: sample
fraction: 0.1
if: spanStatusCode() != "error"
```

Sampling for spans is trace-aware: all spans in the same trace are either kept or dropped together, preserving trace completeness.

### Rename a legacy attribute

```yaml
name: Rename service to service_name
scope: [spans, logs, events, datapoints]
type: rename_attr
old_key: service
new_key: service_name
```

### Reduce URL cardinality

```yaml
name: Normalize user profile URLs
scope: [spans]
type: script
if: hasAttr("http_target")
then:
  - setAttr("http_target", replaceGlob(attr("http_target"), "/user/*/profile", "/user/{id}/profile"))
```

### Build a full-text index

```yaml
name: Index request and exception attributes
scope: [spans, logs, events]
type: text_index
include:
  - log_message
  - exception_message
  - http.request.**
exclude:
  - http.request.headers.**
```

Once configured, search indexed content with `*:value` in the [search bar](/features/searching#searching-indexed-content).

### Enrich IP addresses with geo data

```yaml
name: Enrich client IP with geo data
scope: [spans, logs, events]
type: ip_geo_attrs
keys:
  - custom_ip_address
```

Adds `{key}_country_code`, `{key}_country_name`, and `{key}_city` attributes for each specified key.

## How operations work

### Scope

Every operation requires a `scope` field that limits it to specific signal types:

- `spans`
- `logs`
- `events`
- `datapoints`

### Conditions

Narrow which records an operation applies to using an `if` expression written in [Expr](https://expr-lang.org/docs/language-definition):

```yaml
name: Rename service to service_name
scope: [spans, logs, events, datapoints]
type: rename_attr
old_key: service
new_key: service_name
if: attr("deployment_environment") == "prod"
```

### Execution order

Uptrace runs all operations in order after normalising attribute names (e.g. `service.name` → `service_name`). A `drop` operation stops execution for that record. All other operations always run to completion regardless of errors.

Use `priority` to control order when two operations must run in a specific sequence:

```yaml
name: Rename service to service_name
scope: [spans, logs, events, datapoints]
priority: 100
type: rename_attr
old_key: service
new_key: service_name
```

Transformations run **before** Uptrace processes data internally, so certain computed columns are not yet available. Use these replacements:

<table>
<thead>
  <tr>
    <th>
      Uptrace column
    </th>
    
    <th>
      Replacement
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        _display_name
      </code>
    </td>
    
    <td>
      <code>
        spanName()
      </code>
      
      , <code>
        eventName()
      </code>
      
      , <code>
        attr("log_message")
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        db_arg_*
      </code>
    </td>
    
    <td>
      <code>
        attr("db_statement") contains "something"
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        http_response_status_class
      </code>
    </td>
    
    <td>
      <code>
        http_response_status_code
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        user_agent_*
      </code>
    </td>
    
    <td>
      <code>
        user_agent_original
      </code>
    </td>
  </tr>
</tbody>
</table>

## Next steps

- [Operations & functions reference](/features/transformations/reference) — all operation types and built-in functions
- [Searching](/features/searching#searching-indexed-content) — use `*:value` after configuring a text index
- [Observability as Code](/features/observability-as-code#transformations) — manage transformation rules as version-controlled YAML files
