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:

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:

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 Expropen in new window language for writing expressions. In addition to built-in functions provided by Expr, Uptrace also supports the following functions:

FunctionScopeComment
spanName()spans, logs, eventsReturns the span name or empty string.
logName()logsReturns the event name or empty string.
eventName()eventsReturns the log name or empty string.
spanKind()spansReturns the span kind.
spanDuration()spansReturns the span duration in nanoseconds.
spanStatusCode()spans, logs, eventsReturns the span status code.
spanStatusMessage()spans, logs, eventsReturns the span status message.
metricName()datapointsReturns the metric name.
metricUnit()datapointsReturns the metric unit.
metricInstrument()datapointsReturns the metric instrument.
attr(string)allReturns the attribute value.
hasAttr(string)allReturns true if the attribute exists.

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.

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 attribute 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 AttributeReplacement
display_namespanName(), eventName(), logName(), and 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:

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

Delete attributes

To delete attrs:

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

To delete attrs using a regular expression:

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

To delete attrs in the specific metric:

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:

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

To keep attributes matching a regular expression:

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

Drop

To drop logs that match if expression:

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

To drop spans that match if expression:

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

Sample

To sample 50% of logs that match if expression:

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

Script

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

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

You can use the following functions to change the telemetry data:

FunctionScopeComment
setSpanName(string)spansSets the span name.
setLogName(string)logsSets the log name.
setEventName(string)eventsSets the event name.
setSpanKind(string)spansSets the span kind.
setSpanDuration(nanoseconds)spansSets the span duration in nanoseconds.
setSpanStatusCode(string)spansSets the span status code.
setSpanStatusMessage(string)spansSets the span status message.
setMetricName(string)datapointsSets the datapoint metric name.
setMetricUnit(string)datapointsSets the datapoint metric unit.
setAttr(key, value)allSets the attribute value.
deleteAttr(key)allDeletes the attribute.
renameAttr(oldKey, newKey)allRenames the attribute.
parseInt(value)spans, logs, eventsParses the value as int.
parseUint(value)spans, logs, eventsParses the value as uint.
parseFloat(value)spans, logs, eventsParses the value as float.
parseBool(value)spans, logs, eventsParses the value as bool.
parseDuration(string)spans, logs, eventsParses the string value as time.Duration, e.g. parseDuration("1s").
parseBytes(string)spans, logs, eventsParses the string value as int64 bytes , e.g. parseBytes("1mb").
replaceGlob(src, pattern, repl)allReplaces src with repl if src matches glob pattern.
replaceAllRegexp(src, pattern, repl)allReplaces src with repl if src matches regexp pattern.
extractAllRegexp(src, pattern, repl)allExtracts repl if src matches regexp pattern.

To parse a string attribute value as a float:

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:

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:

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:

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:

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")
Last Updated: