About otel-ottl
An OTTL expression reference for OpenTelemetry users, covering syntax, functions, error handling, and performance practices for writing telemetry transformation and filtering rules.
Key features
- 01Access telemetry with path expressions
- 02Write conditional transformation and filter expressions
- 03Use converter and editor functions
- 04Set error handling modes
- 05Improve expression execution efficiency
Use cases
- 01Redact or remove sensitive telemetry attributes
- 02Drop telemetry based on conditions
- 03Normalize high-cardinality attributes
- 04Enrich telemetry with static attributes
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ dash0hq/ agent-skills --skill otel-ottlAfter installing, type/otel-ottlin Claude Code to confirm it loaded.
Bundled files
References and scripts installed along with SKILL.md.
rules/cardinality.mdcomponents.mdenrichment.mdfunction-reference.mdpatterns.mdredaction.mdDetails
From the project READMEOpenTelemetry Transformation Language (OTTL)
Components that use OTTL
OTTL is not limited to the transform and filter processors. Processors (transform, filter, tailsampling, logdedup) and connectors (routing, count, sum, signaltometrics) accept OTTL expressions. See components for the full list with use cases.
OTTL syntax
Path expressions
Navigate telemetry data using dot notation:
span.name
span.attributes["http.method"]
resource.attributes["service.name"]
Contexts (first path segment): resource, scope, span, spanevent, metric, datapoint, log.
Enumerations
Use int64 constants for enumeration fields:
span.status.code == STATUS_CODE_ERROR
span.kind == SPAN_KIND_SERVER
Operators
Assignment: = — Comparison: ==, !=, >, <, >=, <= — Logical: and, or, not
Functions
Converters (uppercase, return values):
ToUpperCase(span.attributes["http.request.method"])
Substring(log.body.string, 0, 1024)
Concat(["prefix", span.attributes["request.id"]], "-")
IsMatch(metric.name, "^k8s\\..*$")
Editors (lowercase, modify data in-place):
set(span.attributes["region"], "us-east-1")
delete_key(resource.attributes, "internal.key")
limit(log.attributes, 10, [])
See function-reference for the full list of editors and converters.
Conditional statements
Use where to apply transformations conditionally:
span.attributes["db.statement"] = "REDACTED" where resource.attributes["service.name"] == "accounting"
Nil checks
Use nil for absence checking (not null):
resource.attributes["service.name"] != nil
Validation workflow
- Validate config syntax — run
otelcol validate --config=config.yamlto catch compilation errors before starting the Collector. - Test with the debug exporter — route transformed telemetry to a
debugexporter and inspect the output:
exporters:
debug:
verbosity: detailed
service:
pipelines:
traces:
receivers: [otlp]
processors: [transform]
exporters: [debug] # swap in production exporter once validated
- Set
error_mode: ignorein production — see Error handling. - Promote to production exporters — replace
debugwith the production exporter.
Common patterns
- Set attributes
- Drop telemetry by pattern
- Drop stale data
- Backfill missing timestamps
- Filter processor example
- Transform processor example
- Defensive nil checks
- Redact sensitive data — strategies: replace, mask, hash, delete, and drop.
- Normalize high-cardinality attributes — path segments, IP masking, and attribute count/length limits.
- Enrich telemetry with static attributes
Error handling
Compilation errors
Occur during processor initialization and prevent Collector startup:
- Invalid syntax (missing quotes)
- Unknown functions
- Invalid path expressions
- Type mismatches
Runtime errors
Occur during telemetry processing:
- Accessing non-existent attributes
- Type conversion failures
- Function execution errors
Error mode configuration
Always set error_mode explicitly; the default differs by component.
The transform and filter processors default to ignore since Collector Contrib v0.153.0 and to propagate before it.
The routing and signaltometrics connectors default to propagate.
The count and sum connectors have no error_mode key and always propagate errors.
| Mode | Behavior | When to use |
|---|---|---|
ignore | Logs the error and continues to the next statement | Production and general use — the default for the transform and filter processors |
propagate | Returns the error up the pipeline, dropping the payload from the Collector | Development and strict environments where you want to catch every error |
silent | Ignores errors without logging | High-volume pipelines with known-safe transforms where error logs are noise |
processors:
transform:
error_mode: ignore
trace_statements:
- set(span.attributes["parsed"], ParseJSON(span.attributes["json_body"]))
Statements are a flat list; the Collector infers the context (span, metric, datapoint, log, and so on) from the path prefixes.
Use the object form with an explicit context: only when a statement group mixes paths that cannot be inferred to a single context, or when you need a group-level condition or error_mode.
Performance
Use where clauses to skip items early.
# BAD — runs replace_pattern on every span
replace_pattern(span.attributes["url.path"], "/\\d+", "/{id}")
# GOOD — skips spans that lack the attribute
replace_pattern(span.attributes["url.path"], "/\\d+", "/{id}") where span.attributes["url.path"] != nil