Transformations

Uptrace allows to transform the ingested data using a YAML-based language. You can use it to rename/delete attribute keys, parse/change attribute values, sample/drop spans, etc.

Example

For example, to rename an attribute:

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

You can use the YAML above to create an operation by following these steps:

  1. In the navigation menu on the left, click "Project" -> "Transformations".
  2. Click "New Operation" -> "New operation from YAML".
  3. Paste the YAML and click "Create".

Scope

Each operation must have a scope field that limits the operation to a particular data type (aka signal). The scope is an array and must contain one of the following values:

  • spans
  • logs
  • events
  • datapoints

Conditions

You can further narrow down the telemetry data to be transformed using if expression:

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"

Uptrace uses Expr language for writing expressions.

Execution

Uptrace executes operations one by one after normalising attribute names, i.e. service.name is normalised to service_name so you should use the normalised name.

Execution will stop if the signal is dropped by a drop operation, but otherwise Uptrace will execute all operations regardless of errors.

To change the execution order, use the priority field.

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

Uptrace executes transformations before it processes the data so you can't use certain attributes set by Uptrace, for example, the _display_name column is not available and instead you should use spanName(), eventName(), logName(), and log_message.

For the same reason, you also can't use attributes extracted from structured logs and SQL queries, such as db_arg_* attributes.

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

Operations

Rename attribute

To rename attr from service to service_name:

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

Set overwrite: true to overwrite the target attribute if it already exists:

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

Delete attributes

To delete attrs:

yaml
name: Delete foo and bar
scope: [spans, logs, events, datapoints]
type: delete_attrs
keys:
  - foo
  - bar

To delete attrs using a regular expression:

yaml
name: Delete foo and bar
scope: [spans, logs, events, datapoints]
type: delete_attrs
regexp: ^(foo|bar)$

To delete attrs in the specific metric:

yaml
name: Delete foo and bar
scope: [datapoints]
type: delete_attrs
keys:
  - foo
  - bar
if: metricName() == "my_metric_name"

Keep attributes

To keep some attributes and delete others:

yaml
name: Keep foo and bar
scope: [spans, logs, events, datapoints]
type: keep_attrs
keys:
  - foo
  - bar

To keep attributes matching a regular expression:

yaml
name: Keep foo and bar
scope: [spans, logs, events, datapoints]
type: keep_attrs
regexp: ^(foo|bar)$

Drop

To drop logs that match if expression:

yaml
name: Drop hello logs
scope: [logs]
type: drop
if: attr("log_message") contains "hello"

To drop spans that match if expression:

yaml
name: Drop Redis evalsha
scope: [spans]
type: drop
if: spanName() == "evalsha" && spanStatusCode() != "error"

The if condition is required for the drop operation.

Sample

To sample 50% of logs that match if expression:

yaml
name: Sample 50% hello logs
scope: [logs]
type: sample
fraction: 0.5
if: attr("log_message") startsWith "hello"

The fraction must be between 0 and 1. For spans, sampling is based on the trace ID so all spans in the same trace are either kept or dropped together. The if condition is optional — when omitted, the sampling applies to all signals in scope.

Change attribute type

To convert an attribute value to a different type:

yaml
name: Convert elapsed_ms to float
scope: [spans]
type: change_attr_type
key: elapsed_ms
type: float

Supported types: string, int, float, bool.

change_attr_type is not supported for datapoints. Use the script operation with type parsing functions instead.

Flatten Map

By default, Uptrace does not index attributes that contain maps. For example, the following attribute cannot be queried using the object.nested.foo path:

json
{
  "object": {
    "nested": {
      "foo": "bar",
      "hello": "world"
    }
  }
}

To flatten such attributes into dot-separated top-level attributes, use the flatten_map operation:

yaml
name: Flatten the object attribute
scope: [spans]
type: flatten_map

# Map attributes to flatten.
maps: ['object']

# Map paths to include.
include_paths: ['object.nested.foo']
include_regexp: '^object.nested\.'

# Map paths to exclude.
exclude_paths: ['object.nested.hello']
exclude_regexp: 'secret'

If you don't specify any paths, the operation will index the entire map but will stop after 32 paths. This limit exists to keep the number of indexed attributes low and improve performance.

IP Geo Attributes

The ip_geo_attrs operation enriches IP address attributes with GeoIP data. For each specified key, it looks up the IP address and adds {key}_country_code, {key}_country_name, and {key}_city attributes.

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

client_address and client_socket_address are automatically processed by Uptrace and should not be added manually.

ip_geo_attrs is not supported for datapoints.

Text Index

The text_index operation collects selected attribute values into a full-text index column, enabling fast text search across attribute values using the *:value search syntax.

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

The include field accepts glob patterns with . as the separator:

PatternDescription
fooIndexes the foo attribute value directly
foo.**Indexes all nested keys in the foo map
foo.*Indexes one level of nested keys in the foo map
foo?.barMatches attribute keys like foo1, foo2, etc.

The exclude field uses the same glob patterns to remove specific paths from indexing.

For attributes of type map or array, the value is serialized as JSON. The search is case-insensitive and token-based, so substring matches are not supported. The maximum indexed text size per span/log is 4096 bytes.

Text Index is billed separately based on the number of indexed bytes. See the pricing page for details.

Once configured, you can search indexed content using *:value in the search bar:

text
*:hello
*:error|warning
-*:debug

Script

Uptrace allows to write simple scripts that can modify telemetry data, for example:

yaml
name: Update status code if span has exception* attributes
scope: [spans]
type: script
if: hasAttr("exception_type") && hasAttr("exception_message")
then:
  - setSpanStatusCode("error")
  - setSpanStatusMessage(attr("exception_message"))
else:
  - setSpanStatusCode("ok")

The if condition is required for the script operation. The then and else lists contain expressions that are executed when the condition is true or false respectively.

To parse a string attribute value as a float:

yaml
name: Parse elapsed_ms
scope: [spans]
type: script
if: hasAttr("elapsed_ms")
then:
  - setAttr("elapsed_ms", parseFloat(attr("elapsed_ms")))

To reduce cardinality of an attribute:

yaml
name: Reduce http_target cardinality
scope: [spans]
type: script
if: metricName() startsWith "http_server_" && hasAttr("http_target")
then:
  - setAttr("http_target", replaceGlob(attr("http_target"), "/user/*/list/*", "/user/{userId}/list/{listId}"))

To replace all numbers in a string attribute using a regular expression:

yaml
name: Replace numbers in foo
scope: [spans]
type: script
if: hasAttr("foo")
then:
  - setAttr("foo", replaceAllRegexp(attr("foo"), "(\\d+)", "<number>"))

To extract all numbers from a string attribute using a regular expression:

text
name: Extract numbers from foo
scope: [spans]
type: script
if: hasAttr("foo")
then:
  - setAttr("foo", extractAllRegexp(attr("foo"), "(\\d+)", "$1"))

To convert a log into a span:

yaml
name: Convert logs with elapsed_ms to spans
scope: [logs]
type: script
if: hasAttr("elapsed_ms")
then:
  - setSpanName("operation-name")
  - setLogName("") # must zero the log name
  - setSpanDuration(parseFloat(attr("elapsed_ms")) * 1e6)
  - setSpanStatusCode("ok")

Functions reference

In addition to built-in functions provided by Expr, Uptrace supports the following functions. All functions can be used in if conditions and script expressions.

Span, log, and event functions

These functions are available when the scope includes spans, logs, or events.

Getter functions

FunctionScopeDescription
spanName()spans, logs, eventsReturns the span name or an empty string.
eventName()eventsReturns the event name or an empty string.
logName()logsReturns the log name (alias for eventName).
spanKind()spansReturns the span kind (internal, server, client, producer, consumer).
spanDuration()spansReturns the span duration as a time.Duration (nanosecond precision).
spanStatusCode()spans, logs, eventsReturns the span status code (unset, ok, error).
spanStatusMessage()spans, logs, eventsReturns the span status message.

Note that spanName() and eventName() return the original value as reported by OpenTelemetry, e.g. eventName() usually contains only a single word like message, log, etc.

Setter functions

FunctionScopeDescription
setSpanName(string)spansSets the span name.
setEventName(string)eventsSets the event name.
setLogName(string)logsSets the log name.
setSpanKind(string)spansSets the span kind. Must be one of: internal, server, client, producer, consumer.
setSpanDuration(nanoseconds)spansSets the span duration in nanoseconds. Accepts int, int64, or time.Duration.
setSpanStatusCode(string)spansSets the span status code.
setSpanStatusMessage(string)spansSets the span status message.

Attribute functions

FunctionScopeDescription
attr(key)spans, logs, eventsReturns the attribute value by key.
attrType(key)spans, logs, eventsReturns the attribute type: nil, string, int, float, bool, array, or map.
hasAttr(key)spans, logs, eventsReturns true if the attribute exists.
setAttr(key, value)spans, logs, eventsSets the attribute value.
deleteAttr(key)spans, logs, eventsDeletes the attribute.
renameAttr(oldKey, newKey)spans, logs, eventsRenames the attribute.
renameAttr(oldKey, newKey, overwrite)spans, logs, eventsRenames the attribute, optionally overwriting if the target exists.

Type parsing functions

FunctionScopeDescription
parseInt(value)spans, logs, eventsParses the value as int64.
parseUint(value)spans, logs, eventsParses the value as uint64.
parseFloat(value)spans, logs, eventsParses the value as float64.
parseBool(value)spans, logs, eventsParses the value as bool. Accepts strings like "true", "false", "1", "0".

Datapoint functions

These functions are available when the scope includes datapoints.

Getter functions

FunctionDescription
metricName()Returns the metric name.
metricUnit()Returns the metric unit.
metricInstrument()Returns the metric instrument type.
libraryName()Returns the instrumentation library name.
libraryVersion()Returns the instrumentation library version.

Setter functions

FunctionDescription
setMetricName(string)Sets the metric name.
setMetricUnit(string)Sets the metric unit.
setMetricInstrument(string)Sets the metric instrument type.

Attribute functions

FunctionDescription
attr(key)Returns the attribute value as a string.
hasAttr(key)Returns true if the attribute exists.
setAttr(key, value)Sets the attribute value.
deleteAttr(key)Deletes the attribute.
renameAttr(oldKey, newKey)Renames the attribute.
renameAttr(oldKey, newKey, overwrite)Renames the attribute, optionally overwriting.

Common functions

These functions are available for all signal types.

FunctionDescription
replaceGlob(src, pattern, repl)Returns repl if src matches glob pattern, otherwise returns src.
replaceAllRegexp(src, pattern, repl)Replaces all regexp matches in src with repl.
extractAllRegexp(src, pattern, repl)Extracts and transforms all regexp matches from src using repl template.
parseDuration(string)Parses a duration string (e.g. "1h30m", "500ms") as time.Duration.
parseByteSize(string)Parses a byte size string (e.g. "1GB", "512MB") as int64 bytes.