Expert guidance for configuring and deploying the OpenTelemetry Collector. Use when setting up a Collector pipeline, configuring receivers, exporters, or processors, deploying a Collector to Kubernetes or Docker, or forwarding telemetry to Dash0. Triggers on requests involving collector, pipeline, OTLP receiver, exporter, or Dash0 collector setup.
100
100%
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Advisory
Suggest reviewing before use
OTTL is not limited to the transform and filter processors. Processors (transform, filter, attributes, span, tailsampling, cumulativetodelta, logdedup, lookup), connectors (routing, count, sum, signaltometrics), and the hostmetrics receiver all accept OTTL expressions. See components for the full list with use cases.
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.
Use int64 constants for enumeration fields:
span.status.code == STATUS_CODE_ERROR
span.kind == SPAN_KIND_SERVERAssignment: = — Comparison: ==, !=, >, <, >=, <= — Logical: and, or, not
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.
Use where to apply transformations conditionally:
span.attributes["db.statement"] = "REDACTED" where resource.attributes["service.name"] == "accounting"Use nil for absence checking (not null):
resource.attributes["service.name"] != nilotelcol validate --config=config.yaml to catch compilation errors before starting the Collector.debug exporter and inspect the output:exporters:
debug:
verbosity: detailed
service:
pipelines:
traces:
receivers: [otlp]
processors: [transform, batch]
exporters: [debug] # swap in production exporter once validatederror_mode: ignore in production — see Error handling.debug with the production exporter.set(resource.attributes["k8s.cluster.name"], "prod-aws-us-west-2")See redaction for strategies (replace, mask, hash, delete, drop) with examples.
IsMatch(metric.name, "^k8s\\.replicaset\\..*$")time_unix_nano < UnixNano(Now()) - 21600000000000processors:
transform:
log_statements:
- context: log
statements:
- set(log.observed_time, Now()) where log.observed_time_unix_nano == 0
- set(log.time, log.observed_time) where log.time_unix_nano == 0processors:
filter:
metrics:
datapoint:
- 'IsMatch(ConvertCase(String(metric.name), "lower"), "^k8s\\.replicaset\\.")'
service:
pipelines:
metrics:
receivers: [otlp]
processors: [filter, batch]
exporters: [debug]processors:
transform:
trace_statements:
- context: span
statements:
- set(span.status.code, STATUS_CODE_ERROR) where span.attributes["http.response.status_code"] >= 500
- set(span.attributes["env"], "production") where resource.attributes["deployment.environment"] == "prod"
service:
pipelines:
traces:
receivers: [otlp]
processors: [transform, batch]
exporters: [debug]resource.attributes["service.namespace"] != nil
and
IsMatch(ConvertCase(String(resource.attributes["service.namespace"]), "lower"), "^platform.*$")See cardinality for normalizing high-cardinality attributes (path segments, IP masking, attribute count/length limits) and enrichment for adding static resource attributes.
Occur during processor initialization and prevent Collector startup:
Occur during telemetry processing:
Always set error_mode explicitly.
| Mode | Behavior | When to use |
|---|---|---|
propagate (default) | Stops processing current item | Development and strict environments where you want to catch every error |
ignore | Logs error, continues processing | Production — set this unless you have a specific reason not to |
silent | Ignores errors without logging | High-volume pipelines with known-safe transforms where error logs are noise |
processors:
transform:
error_mode: ignore
trace_statements:
- context: span
statements:
- set(span.attributes["parsed"], ParseJSON(span.attributes["json_body"]))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