
opentelemetry-ottl
OpenTelemetry Transformation Language (OTTL) for OTel Collector pipelines. Use when writing or debugging OTTL statements in the transform processor, filter processor, or routing connector in any collector configuration — context selection, path expression mistakes (attributes vs resource.attributes), errormode, cardinality reduction with keepkeys, JSON body handling with ParseJSON, PII redaction with SHA256 / replacepattern, and span naming. Not for receiver/exporter connectivity, DNS, pipeline wiring, or telemetry that never reaches the processor — those are infra problems, not OTTL problems.
- Installs
- 41
- Last 24h
- —
- Trending rank
- —
- Security
- Not audited
Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/coralogix/cx-skills --skill opentelemetry-ottl- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads opentelemetry-ottl automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
SKILL.md
The instructions your agent reads when the skill is triggered.
OTTL
OpenTelemetry Transformation Language (OTTL) transforms, filters, and routes telemetry inside an OTel Collector pipeline without modifying application code. Load this skill when writing or debugging OTTL statements in the transform processor, filter processor, or routing connector.
When to Use This Skill
| Use case | What to do |
|---|---|
| Change values or fields conditionally | transform processor with the correct context |
| Drop telemetry entirely (match = drop) | filter processor |
| Set static resource attributes everywhere | resource processor — simpler than OTTL |
| Copy a resource field down to spans or logs | transform with the correct context |
| Route telemetry to different pipelines | routing connector |
| Reduce metric or trace cardinality | transform with keep_keys / delete_matching_keys → references/cardinality.md |
| Extract histogram _sum/_count into standalone metrics, then drop the histogram | transform with extract_sum_metric / extract_count_metric in context: metric, then filter to drop the original |
| Redact or pseudonymize PII | transform with SHA256 / replace_all_patterns → references/redaction.md |
| Debug no data, DNS, receiver/exporter issues, or pipeline wiring | Not an OTTL problem — say so before going further |
Reference material for each topic lives under references/ and is listed in the References footer at the bottom of this file. When the question involves a specific processor, context, or function you have not recently reviewed — or when the user pastes a Collector error — consult the matching reference file before answering. One or two targeted reads beat guessing from memory.
Key Concepts
Contexts and path expressions
context: determines what attributes means. In context: resource, attributes["k"] is a resource attribute. In context: log or context: span, attributes["k"] is the record-level attribute — use resource.attributes["k"] to reach the resource. Wrong context = silent nil, no error. → references/contexts.md
| Signal | Valid contexts |
|---|---|
| logs | resource, scope, log |
| traces | resource, scope, span, spanevent |
| metrics | resource, scope, metric, datapoint |
Metric-level edits (name, description, unit) belong in context: metric; per-series label/attribute edits belong in context: datapoint. Mixing the two in one block means one of them silently no-ops. → references/contexts.md
Log event timestamps can be changed in context: log by setting time with Time(...)
or time_unix_nano with an integer nanosecond value. Span status checks belong in
context: span; prefer STATUS_CODE_ERROR, STATUS_CODE_OK, and STATUS_CODE_UNSET
over raw numeric comparisons. → references/transformations.md
Error modes
| Mode | Behavior |
|---|---|
propagate (default) | Any OTTL runtime error halts the pipeline — causes data loss |
ignore | Log the error, skip the statement, continue the pipeline — use in production |
silent | Skip the statement and suppress error logging |
Set error_mode explicitly on every transform and filter processor — the default
(propagate) halts the whole pipeline on the first runtime error (missing optional
field, wrong type, indexing a nil), silently dropping every subsequent record. The fix
for a pipeline that "goes silent after one failure" is almost always the missing
error_mode key:
processors:
transform:
error_mode: ignore # log, skip the statement, continue the pipeline
log_statements:
- context: log
statements:
- set(attributes["env"], resource.attributes["deployment.environment"])
Error patterns
Map the Collector message to a root cause before proposing a fix:
| Collector message | Root cause | First action |
|---|---|---|
INVALID_ARGUMENT | Type mismatch, invalid function input, or invalid path for the active context | Add nil/type guards; confirm active context |
... cannot be indexed | Indexing a non-map value — string or empty body | Add IsMap(body) guard before body indexing |
segment "..." is not a valid path | Wrong context or field not available in the chosen context | Switch to the correct context; check path reference |
one or more paths were modified to include their context prefix | Bare attributes[...] where explicit prefixes are required | Rewrite with resource.attributes, datapoint.attributes, etc. |
statement has invalid syntax: ... invalid quoted string | YAML + OTTL quoting collision — the string was consumed by the YAML parser before reaching OTTL | Use YAML single quotes outside and OTTL double quotes inside → references/processors.md |
| Statement loads but has no visible effect | Condition never matches, wrong signal block, or processor in the wrong pipeline stage | Surface debug attributes to prove matching; verify pipeline placement |
Canonical pipeline shape
Full annotated example of a transform + filter pipeline (log/trace/metric
statements, error_mode, conditions: [IsMap(body)], filter-before-transform
ordering) lives in references/processors.md.
Common Workflows
1. Debug an OTTL statement
- Confirm the problem is actually OTTL — not component choice, pipeline wiring, or infrastructure.
- Identify the signal (logs / traces / metrics) and the specific context.
- Match the exact error text against the Error patterns table above.
- Check for missing nil or type guards (
IsMap,IsString,!= nil). - Check for the wrong context prefix (
attributesvsresource.attributes). - Check
conditions:semantics or tail sampling policy ordering. - Only then propose the corrected statement and minimal YAML.
2. Promote a JSON log body to attributes
When the body arrives as a raw JSON string (IsMap(body) is false), guard with
IsString(body) and parse with ParseJSON. Prefer IsString(body) over
not IsMap(body) — it is the affirmative check and avoids matching empty/nil
bodies.
- context: log
conditions:
- IsString(body) # body is a JSON string (affirmative guard)
statements:
# Promote every top-level JSON field into attributes
- merge_maps(attributes, ParseJSON(body), "insert")
# Or lift specific fields only:
- set(attributes["user_id"], ParseJSON(body)["user_id"]) where ParseJSON(body)["user_id"] != nil
- set(attributes["request_id"], ParseJSON(body)["request_id"]) where ParseJSON(body)["request_id"] != nil
ParseJSON is a Converter — it returns a value but has no side effect of its own,
so it must be wrapped in an Editor (set, merge_maps). A standalone
ParseJSON(body) line loads without errors and does nothing. → references/transformations.md
3. Reduce metric cardinality
- context: datapoint
statements:
- keep_keys(attributes, ["service.name", "http.route", "http.response.status_code"])
- delete_matching_keys(attributes, "^k8s\\.pod\\.uid$")
Prefer keep_keys (allowlist) over many delete_key calls (blocklist). → references/cardinality.md
4. Feed an exporter that reads resource attributes
Exporters that pick a destination from attributes — most notably the Coralogix exporter's
application_name_attributes and subsystem_name_attributes — read from resource
attributes, not log-record or span attributes. If the source value lives on the record,
copy it up to the resource from context: log (or context: span) with
set(resource.attributes[...], attributes[...]). Don't rename the field in the
application and don't change the exporter config.
Full pattern (both attribute pairs, error_mode, and pipeline ordering) is in
→ references/contexts.md.
5. Extract histogram aggregations and drop the source metric
To keep _sum and _count for average latency calculations while dropping raw bucket data:
processors:
transform:
error_mode: ignore
metric_statements:
- context: metric
conditions:
- type == METRIC_DATA_TYPE_HISTOGRAM
statements:
- extract_sum_metric(true) # creates <name>_sum as a new Sum metric; true = monotonic
- extract_count_metric(true) # creates <name>_count as a new Counter metric
filter:
error_mode: ignore
metrics:
metric:
- 'type == METRIC_DATA_TYPE_HISTOGRAM and name == "http.server.request.duration"'
extract_sum_metric(monotonic) and extract_count_metric(monotonic) are OTTL Editor functions that run in context: metric and append new standalone metrics to the pipeline output — the original histogram is still present until the filter processor drops it. The transform processor must come before filter in the pipeline so the new metrics exist before the histogram is removed. Pass true for cumulative/monotonic counters, false for delta.
6. Redact PII across attributes
- context: log
statements:
- set(attributes["user.id"], SHA256(attributes["user.id"])) where attributes["user.id"] != nil
- replace_all_patterns(attributes, "value", "(?i)bearer\\s+[a-z0-9._-]+", "bearer ***")
SHA256 preserves correlation without exposing the raw identifier. replace_all_patterns redacts across every attribute value without listing each key. → references/redaction.md
Best Practices
Pipeline and ordering
- Filter before transform. Don't spend CPU transforming records that will be dropped.
- Set
error_mode: ignoreexplicitly. The defaultpropagatecauses data loss on any runtime error. - Use
conditions:to scope a block. Statements run when any listed condition matches (OR semantics) — cheaper than awhereclause on every statement. For strict AND, combine witha and bor use per-statementwhere.
Defensive OTTL
- Guard before indexing.
IsMap(body)before map access,IsString(body)before string operations,where attributes["x"] != nilbefore reading optional fields. An unguarded indexing into the wrong type raisesINVALID_ARGUMENTand (with the defaulterror_mode) halts the pipeline. - Convert numeric-looking strings before comparing. If an attribute can be
"5"or5, branch withIsString/IsIntand useInt(...)orDouble(...)before>/<comparisons. conditions:is OR, not AND. For strict AND, combine into one boolean (a and b) or addwhereon each statement.
Authoring style
- Identify signal and context first. Keep traces, metrics, and logs separate; use the context that matches the referenced fields.
- Prefer several short statements over one dense expression. Show exact path prefixes when clarity matters.
- Prefer
keep_keysover manydelete_keycalls. An allowlist is shorter and self-documenting.
Limitations
OTTL is not the fix for: receiver connectivity, exporter connectivity, DNS or Kubernetes service discovery, load balancing, gateway reachability, pipeline wiring mistakes, or telemetry that never reaches the processor. Say so before going further.
Scope fences:
- OTTL can only use fields exposed in the active signal and context model.
- The Collector sees telemetry payloads, not raw incoming requests — upstream HTTP headers are usually not available after receiver ingest.
- If a field is not in the active context, OTTL cannot infer or reconstruct it.
The OTTL function list evolves each Collector release. This skill covers patterns and common misuses — for current function signatures, consult the upstream docs in the References footer below.
References
- references/contexts.md — OTTL contexts, path expressions, and the most common context mistakes
- references/processors.md —
transform,filter, androutingconnector configuration - references/filtering.md — Dropping logs, metrics, and spans with the filter processor
- references/transformations.md — Span naming, semconv migration, body operations,
ParseJSON, fallback chains - references/cardinality.md — Reducing metric and trace cardinality with
keep_keys,delete_matching_keys,replace_pattern - references/redaction.md — PII masking,
SHA256pseudonymization, token/card/auth header redaction
Upstream:
FAQ
What does the opentelemetry-ottl skill do?
OpenTelemetry Transformation Language (OTTL) for OTel Collector pipelines. Use when writing or debugging OTTL statements in the transform processor, filter processor, or routing connector in any collector configuration — context selection, path expression mistakes (attributes vs resource.attributes), errormode, cardinality reduction with keepkeys, JSON body handling with ParseJSON, PII redaction with SHA256 / replacepattern, and span naming. Not for receiver/exporter connectivity, DNS, pipeline wiring, or telemetry that never reaches the processor — those are infra problems, not OTTL problems.
How do I install opentelemetry-ottl?
Run npx skills add https://github.com/coralogix/cx-skills --skill opentelemetry-ottl in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/opentelemetry-ottl/ (personal) or .claude/skills/opentelemetry-ottl/ (project).
Which AI agents can use opentelemetry-ottl?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is opentelemetry-ottl safe to install?
No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.
Who maintains opentelemetry-ottl?
It is published by coralogix in coralogix/cx-skills. It has 41 recorded installs.
More from coralogix/cx-skills
opentelemetry-collector
coralogix/cx-skills
Open the skill to read its SKILL.md instructions.
opentelemetry-instrumentation
coralogix/cx-skills
Open the skill to read its SKILL.md instructions.
Popular code quality & workflow skills
grill-me
mattpocock/skills
A relentless interview to sharpen a plan or design.
improve-codebase-architecture
mattpocock/skills
Scan a codebase for deepening opportunities, present them as a visual HTML report, then grill through whichever one you pick.
handoff
mattpocock/skills
Compact the current conversation into a handoff document for another agent to pick up.
domain-modeling
mattpocock/skills
Build and sharpen a project's domain model. Use when discussing codebase terminology, writing or editing a GLOSSARY.md, or recording or editing an ADR.
codebase-design
mattpocock/skills
Shared vocabulary for designing deep modules. Use when the user wants to design or improve a module's interface, find deepening opportunities, decide where a seam goes, make code more testable or AI-navigable, or when another skill needs the deep-module vocabulary.
diagnosing-bugs
mattpocock/skills
Diagnosis loop for hard bugs and performance regressions. Use when the user says "diagnose"/"debug this", or reports something broken/throwing/failing/slow.