OpenTelemetry Transformation Language (OTTL)
OTTL transforms or selects telemetry inside Collector components. This skill is pinned to
collector-contrib v0.160.0. Function, path, default, and feature-gate availability varies by
release; when the user's version differs, verify against the matching upstream tag.
Workflow
- Choose the component.
transform rewrites, filter drops, tail_sampling decides whether
to retain traces, and routing sends telemetry to pipelines. A component controls its available
contexts and functions.
- Choose the lowest usable context. Lower contexts can read their parents (for example, a span
can read
resource.attributes), but parents cannot read children. Use datapoint for point
attributes instead of traversing metric.data_points.
- Verify every emitted function, then write the statement. Confirm each function's exact
identifier and signature in references/functions.md. If an identifier
is absent, treat it as unsupported instead of deriving or substituting a plausible name. An
editor such as
set or delete_key mutates data and may have a where condition. Converters
such as ParseJSON and IsMatch return values; they do not mutate.
- Set error behavior deliberately.
ignore logs statement errors and continues; silent
continues without logging; propagate returns the error and can cause the component to drop the
payload. In v0.160, transform and filter default to ignore; their stable default-error gates
remain registered even though their metadata names v0.159 as the end version. Routing also
defaults to ignore while its beta default-error feature gate is enabled. For routing, ignore
sends an errored payload to default_pipelines; configure that fallback or the payload is dropped.
- Verify end to end. Validate the exact Collector version, then send known telemetry and inspect
file-exporter output. Use the
telemetrygen recipe.
set(span.attributes["env"], "prod") where resource.attributes["env"] == nil
Load only what the task needs
- Contexts — exact paths, hierarchy, enums, and request metadata.
- Functions — editor/converter signatures and release availability.
- Quick reference — component YAML, recipes, escaping,
troubleshooting, and safe skeletons.
For a single path or function, read only the relevant section instead of loading the full catalogs.
Safety and correctness gates
- Guard optional or polymorphic input before conversion:
where x != nil, IsString(x), or the
appropriate type check.
- For JSON-object-only work, guard both the type and shape before calling
ParseJSON, for example
IsString(log.body) and IsMatch(log.body.string, "(?s)^\\s*\\{.*\\}\\s*$"). The RE2 (?s)
flag admits pretty-printed objects containing newlines. Checking IsMap after
parsing does not prevent arrays or scalar JSON from being parsed.
- On a version-pinned request, confirm every chosen path and function against that release tag;
do not assume a function listed for this skill's v0.160 anchor exists in an older release.
For v0.156 JSON-object parsing,
ParseJSON, IsString, and IsMatch are available without the
v0.157 alpha lambda feature gate.
- Request metadata is read-only and may contain credentials. Copy only explicitly allowlisted,
non-sensitive keys. OTLP metadata routing requires
include_metadata: true on the receiver.
HTTP/client header spelling may retain its form (otelcol.client.metadata["X-Tenant"][0]);
gRPC metadata keys are lowercase (otelcol.grpc.metadata["x-tenant"][0]).
- The routing
request context is deprecated as of v0.156; use otelcol.client.metadata or
otelcol.grpc.metadata.
- Log-record-specific rewrites of shared resource or scope data require
flatten_data: true and the
alpha transform.flatten.logs gate. This copies and regroups data; do not enable it accidentally.
- In v0.160,
set(target, nil) remains a no-op by default. The alpha ottl.set.allowNil gate passes
nil to the target instead; target behavior then varies from clearing a value to returning an error.
Use a where source != nil guard when the destination must remain unchanged for missing input.
- Hashing an identifier does not necessarily anonymize it. Apply the organization's data-handling
policy before retaining deterministic hashes of personal data.
Frequent syntax traps
- In Collector YAML, write an OTTL replacement backreference
${1} as $${1}. A replacement such
as $1REDACTED is literal and silently fails to substitute the capture.
- Go RE2 rejects large counted repetitions such as
(.{1024}).*; use Substring with a nil/type
guard and Len, or truncate_all for a map.
- Current span-event paths use
spanevent.*, not span_event.*. Cache paths are context-qualified,
such as span.cache["parsed"].
- Quote any OTTL statement containing a map literal when YAML includes a space after
:, for
example 'set(log.attributes["a"], {"foo": "bar"})'; otherwise YAML parses : as a mapping.
- Since v0.159, polymorphic
pcommon.Value paths compare by their underlying type; maps and slices
support only equality and inequality, while primitive values also support ordering.
- Use
Decode(value, "base64"); Base64Decode is deprecated.
- Regex escapes inside OTTL strings are doubled (
\\d, \\s, \\.).
Upstream sources
1---2name: otel-ottl3description: OpenTelemetry Transformation Language (OTTL) expert for writing and debugging telemetry transformations in the OpenTelemetry Collector. Use when authoring or reviewing `transform`, `filter`, `tail_sampling` processor configs or `routing` connector configs, debugging OTTL syntax or semantics, transforming traces, metrics, logs, or profiles, or converting data-processing requirements into OTTL statements.4---56# OpenTelemetry Transformation Language (OTTL)78OTTL transforms or selects telemetry inside Collector components. This skill is pinned to9collector-contrib **v0.160.0**. Function, path, default, and feature-gate availability varies by10release; when the user's version differs, verify against the matching upstream tag.1112## Workflow13141. **Choose the component.** `transform` rewrites, `filter` drops, `tail_sampling` decides whether15 to retain traces, and `routing` sends telemetry to pipelines. A component controls its available16 contexts and functions.172. **Choose the lowest usable context.** Lower contexts can read their parents (for example, a span18 can read `resource.attributes`), but parents cannot read children. Use `datapoint` for point19 attributes instead of traversing `metric.data_points`.203. **Verify every emitted function, then write the statement.** Confirm each function's exact21 identifier and signature in [references/functions.md](references/functions.md). If an identifier22 is absent, treat it as unsupported instead of deriving or substituting a plausible name. An23 editor such as `set` or `delete_key` mutates data and may have a `where` condition. Converters24 such as `ParseJSON` and `IsMatch` return values; they do not mutate.254. **Set error behavior deliberately.** `ignore` logs statement errors and continues; `silent`26 continues without logging; `propagate` returns the error and can cause the component to drop the27 payload. In v0.160, transform and filter default to `ignore`; their stable default-error gates28 remain registered even though their metadata names v0.159 as the end version. Routing also29 defaults to `ignore` while its beta default-error feature gate is enabled. For routing, `ignore`30 sends an errored payload to `default_pipelines`; configure that fallback or the payload is dropped.315. **Verify end to end.** Validate the exact Collector version, then send known telemetry and inspect32 file-exporter output. Use the33 [telemetrygen recipe](../otel-telemetrygen/SKILL.md#verify-collector-behavior).3435```ottl36set(span.attributes["env"], "prod") where resource.attributes["env"] == nil37```3839## Load only what the task needs4041- [Contexts](references/contexts.md) — exact paths, hierarchy, enums, and request metadata.42- [Functions](references/functions.md) — editor/converter signatures and release availability.43- [Quick reference](references/quick-reference.md) — component YAML, recipes, escaping,44 troubleshooting, and safe skeletons.4546For a single path or function, read only the relevant section instead of loading the full catalogs.4748## Safety and correctness gates4950- Guard optional or polymorphic input before conversion: `where x != nil`, `IsString(x)`, or the51 appropriate type check.52- For JSON-object-only work, guard both the type and shape before calling `ParseJSON`, for example53 `IsString(log.body) and IsMatch(log.body.string, "(?s)^\\s*\\{.*\\}\\s*$")`. The RE2 `(?s)`54 flag admits pretty-printed objects containing newlines. Checking `IsMap` after55 parsing does not prevent arrays or scalar JSON from being parsed.56- On a version-pinned request, confirm every chosen path and function against that release tag;57 do not assume a function listed for this skill's v0.160 anchor exists in an older release.58 For v0.156 JSON-object parsing, `ParseJSON`, `IsString`, and `IsMatch` are available without the59 v0.157 alpha lambda feature gate.60- Request metadata is read-only and may contain credentials. Copy only explicitly allowlisted,61 non-sensitive keys. OTLP metadata routing requires `include_metadata: true` on the receiver.62 HTTP/client header spelling may retain its form (`otelcol.client.metadata["X-Tenant"][0]`);63 gRPC metadata keys are lowercase (`otelcol.grpc.metadata["x-tenant"][0]`).64- The routing `request` context is deprecated as of v0.156; use `otelcol.client.metadata` or65 `otelcol.grpc.metadata`.66- Log-record-specific rewrites of shared resource or scope data require `flatten_data: true` and the67 alpha `transform.flatten.logs` gate. This copies and regroups data; do not enable it accidentally.68- In v0.160, `set(target, nil)` remains a no-op by default. The alpha `ottl.set.allowNil` gate passes69 nil to the target instead; target behavior then varies from clearing a value to returning an error.70 Use a `where source != nil` guard when the destination must remain unchanged for missing input.71- Hashing an identifier does not necessarily anonymize it. Apply the organization's data-handling72 policy before retaining deterministic hashes of personal data.7374## Frequent syntax traps7576- In Collector YAML, write an OTTL replacement backreference `${1}` as `$${1}`. A replacement such77 as `$1REDACTED` is literal and silently fails to substitute the capture.78- Go RE2 rejects large counted repetitions such as `(.{1024}).*`; use `Substring` with a nil/type79 guard and `Len`, or `truncate_all` for a map.80- Current span-event paths use `spanevent.*`, not `span_event.*`. Cache paths are context-qualified,81 such as `span.cache["parsed"]`.82- Quote any OTTL statement containing a map literal when YAML includes a space after `:`, for83 example `'set(log.attributes["a"], {"foo": "bar"})'`; otherwise YAML parses `: ` as a mapping.84- Since v0.159, polymorphic `pcommon.Value` paths compare by their underlying type; maps and slices85 support only equality and inequality, while primitive values also support ordering.86- Use `Decode(value, "base64")`; `Base64Decode` is deprecated.87- Regex escapes inside OTTL strings are doubled (`\\d`, `\\s`, `\\.`).8889## Upstream sources9091- [OTTL package](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.160.0/pkg/ottl)92- [Transform processor](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.160.0/processor/transformprocessor)93- [Filter processor](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.160.0/processor/filterprocessor)94- [Routing connector](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.160.0/connector/routingconnector)