Transformations

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.

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.

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:

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 run_order to control order when two operations must run in a specific sequence. The lowest value runs first, and an absent value is 0:

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

run_order replaces the deprecated priority key. Uptrace still reads priority when run_order is absent.

Debugging the pipeline

Click Debug on the Project → Transformations page, or Debug in the operation editor, to open the pipeline debugger. It is a separate page with its own link. Select a real recent record and see how each operation changes it. When you open the debugger from the editor, the unsaved operation is placed in the pipeline, so you can test it before you save it.

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

Uptrace columnReplacement
_display_namespanName(), eventName(), attr("log_message")
db_arg_*attr("db_statement") contains "something"
http_response_status_classhttp_response_status_code
user_agent_*user_agent_original

Next steps