Skip to main content
In this lesson we apply the transform processor to logs and metrics. The core ideas are the same as for traces: use the transform processor with signal-specific *_statements and, when needed, conditions to selectively modify telemetry. Key takeaway: the transform processor only affects telemetry for the pipeline it is attached to (traces, metrics, or logs). Make sure you wire it into the correct pipeline.
When you change the transform configuration, restart the collector and the apps sending telemetry so you can observe the effects.

Quick recap: trace transformations

A typical transform configuration that updates resource attributes, spans, and span events looks like this:
Collector debug output for a span will show the modified resource and span attributes, for example:

Logs

We’ll generate logs using a small log.py script that prints logs at different severities. Before you add transforms, inspect the raw records using the collector’s debug exporter. Example transform configuration that updates log attributes, severity, and body:
What to remember:
  • context: log operates on the LogRecord.
  • Use attributes[...] to add/modify log attributes.
  • Overwrite severity_text to change severity label (e.g., INFO, ERROR).
  • Overwrite body to replace the log message.
  • Without conditions, statements apply to every log record.
Example collector debug output (before transform):
After applying the transform above, you will see the updated body, severity, and added attribute:
Make sure the transform processor is included in the logs pipeline:

Additional log contexts

The transform processor supports other contexts for logs besides log, such as resource and scope. These allow you to modify resource-level attributes and instrumentation scope metadata. Example:
Resulting debug output shows updated resource attributes and instrumentation scope:

Metrics

Metrics use metric_statements and support contexts like metric, datapoint, and resource. You can modify metric descriptors (name, description, unit), and operate on individual datapoints. Example: change a metric descriptor only when the metric name matches current_temperature_fahrenheit:
Attach the transform processor to the metrics pipeline:
After wiring, current_temperature_fahrenheit will be transformed and appear as new.metric.name with updated descriptor fields:

Data point context

Use context: datapoint to modify individual datapoints — add datapoint attributes, override timestamps, or change values. Example:
Collector debug will show modified datapoint timestamps, values, and datapoint-level attributes:
Use datapoint transformations for unit conversions, value overrides, or adding metadata to datapoints.

Deleting or keeping keys (attribute filtering)

You can delete specific attribute keys or keep only a whitelist. The same APIs exist for traces, logs, and metrics — below examples use trace/span context. Delete a key:
This removes the bank attribute from spans where the statement executes. To run only when that key exists, add a condition such as attributes["bank"] != nil. Keep only a whitelist of keys:
keep_keys(attributes, [...]) removes all other keys from the attributes map except those specified. Example debug output after keeping only username and account_number:
If you applied keep_keys(attributes, ["username", "account_number"]), other keys such as some-key would be removed.

Transform contexts quick reference

Context typeSignals supportedWhat you can modifyExample statement
resourcetraces, logs, metricsResource attributesset(attributes["platform"], "kubernetes")
span / spaneventtracesSpan attributes, eventsset(attributes["some-key"], "some-value")
loglogsLogRecord attributes, body, severity_textset(body, "new body")
scopelogs, tracesInstrumentation scope name/attributesset(name, "my.scope.name")
metricmetricsMetric descriptor (name, description, unit)set(name, "new.metric.name")
datapointmetricsDatapoint attributes, timestamps, valuesset(value_int, 100)

Final checklist and best practices

  • Add the transform processor to the correct pipeline(s): traces, metrics, and/or logs.
  • Confirm your YAML structure: each context block must include statements: and, optionally, conditions: (as a list).
  • Use valid OTTL expressions inside conditions:, for example:
    • attributes["bank"] != nil
    • name == "current_temperature_fahrenheit"
  • When iterating on rules, use the collector debug exporter and restart both the collector and telemetry-generating processes so changes take effect.
  • Test incrementally: apply simple transformations first (e.g., set a single attribute), verify output, then expand to more complex rules.
Use the debug exporter in the collector to inspect the transformed telemetry while you iterate on OTTL rules.

Watch Video

Practice Lab