OpenTelemetry Collector deployment, instrumentation (Java/Python/Node.js/.NET/Go), and OTTL pipeline transforms for Coralogix — coralogix exporter config, Helm chart selection, Kubernetes topology, ECS/EKS/GKE deployments, SDK setup, APM transactions, and OTTL cardinality/PII/routing.
98
97%
Does it follow best practices?
Impact
99%
1.13xAverage score across 81 eval scenarios
Advisory
Suggest reviewing before use
High cardinality is the most common reason customers add OTTL processors to their pipelines. These patterns are drawn from real configurations.
keep_keys removes every attribute NOT in the list. Safer than many delete_key calls when
you know exactly what you need.
processors:
transform:
error_mode: ignore
metric_statements:
- context: resource
statements:
# Keep only the labels needed for dashboards/alerts; drop everything else
- keep_keys(attributes, ["service.name", "k8s.namespace.name", "k8s.deployment.name"])
- context: datapoint
statements:
# Scope to specific metrics using where — don't trim labels on all metrics
- keep_keys(attributes, ["span.name", "service.name", "span.kind", "status_code", "http.method", "le"]) where name == "calls" or name == "duration"Use when you want to remove a category of labels (e.g. all process or OS attributes) without listing every individual key.
processors:
transform:
error_mode: ignore
metric_statements:
- context: resource
statements:
# Remove all process, OS, host, telemetry SDK metadata — common cardinality sources
- delete_matching_keys(attributes, "(?i)^(process|os|host|telemetry|container)\\.")
- delete_matching_keys(attributes, "(?i)^(aws|azure|gcp)\\.")
# Remove specific high-cardinality keys
- delete_key(attributes, "service.instance.id")
- delete_key(attributes, "service.version")
- delete_key(attributes, "process.command_args")
- delete_key(attributes, "process.command")
- context: datapoint
statements:
# Datapoint attributes may have different keys than resource
- delete_key(attributes, "process.command_args")
- delete_key(attributes, "url.scheme")
- delete_key(attributes, "network.protocol.version")Dynamic path segments (UUIDs, numeric IDs, MongoDB ObjectIds) cause unbounded cardinality in
span names and http.url / http.target attributes.
processors:
transform:
error_mode: ignore
trace_statements:
- context: span
statements:
# Replace UUIDs, MongoDB ObjectIds, and numeric IDs with a placeholder
- replace_pattern(attributes["http.url"], "/([0-9a-fA-F]{24,32}|[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|[0-9]+)(/|$|\\?)", "/:id$2")
# Strip query strings from URLs entirely
- replace_pattern(attributes["http.url"], "\\?.*$", "")
# Replace numeric IDs in span name path segments
- replace_pattern(name, "/[0-9]+(/|$)", "/:id$1")Use delete_matching_keys when a whole family of span attributes should be removed, such as
captured request headers. Run it in context: span so attributes means span attributes, not
resource or metric datapoint attributes.
processors:
transform/remove-span-headers:
error_mode: ignore
trace_statements:
- context: span
statements:
- delete_matching_keys(attributes, "^http\\.request\\.header\\.")GraphQL spans produce names like graphql.resolve User.profile.name, graphql.resolve User.posts[0].
Truncate to the first field access:
trace_statements:
- context: span
statements:
- replace_pattern(name, "^(graphql\\.resolve\\s+[a-zA-Z\\.]+).*$", "$1")otel-integration Helm presetIf the customer is setting these patterns through opentelemetry-agent.presets.spanMetrics.spanNameReplacePattern in values.yaml rather than writing the transform processor directly, the same two escape rules that apply to any collector config apply here:
$1 / $2 backreferences as $$1 / $$2. The collector's envprovider expands $... references at startup; $$ is the literal-$ escape. This is a collector rule, not a Helm rule — Helm passes $ through unchanged.Symptom when Rule 1 is wrong: statement has invalid syntax: 1:28: invalid quoted string ... invalid syntax on helm upgrade, even though the regex is valid in a tester. Symptom when Rule 2 is wrong: no startup error, but replacements produce empty output.
Verify with helm template -f values.yaml | grep -A 20 transform/span_name before upgrading. Worked example: skills/opentelemetry/opentelemetry-collector/references/setup-kubernetes.md — "Escape layers in collector config YAML".
Raw database query strings have near-infinite cardinality. Reduce to query type:
processors:
transform:
error_mode: silent # attribute-missing errors are expected here
trace_statements:
- context: span
conditions:
- attributes["db.query.text"] != nil
statements:
- set(attributes["db.query.text"], "INSERT") where IsMatch(attributes["db.query.text"], "(?i)^insert\\s")
- set(attributes["db.query.text"], "UPDATE") where IsMatch(attributes["db.query.text"], "(?i)^update\\s")
- set(attributes["db.query.text"], "DELETE") where IsMatch(attributes["db.query.text"], "(?i)^delete\\s")
- set(attributes["db.query.text"], "SELECT_AGGREGATE") where IsMatch(attributes["db.query.text"], "(?i)select.*(count|sum|max|min|exists)\\s*\\(")
- set(attributes["db.query.text"], "SELECT_JOIN") where IsMatch(attributes["db.query.text"], "(?i)select.*\\s+join\\s+")
- set(attributes["db.query.text"], "SELECT_DISTINCT") where IsMatch(attributes["db.query.text"], "(?i)^select\\s+distinct\\s+")
- set(attributes["db.query.text"], "SELECT") where IsMatch(attributes["db.query.text"], "(?i)^select\\s")When log bodies are structured maps (e.g. JSON-parsed k8s events), keep only the fields
you need. Always guard with IsMap(body) — indexing a non-map body causes INVALID_ARGUMENT.
processors:
transform:
error_mode: ignore
log_statements:
- context: log
conditions:
- IsMap(body)
statements:
- keep_keys(body, ["type", "action", "reason", "note", "metadata", "regarding", "eventTime"])OTTL keep_keys reduces attributes before the spanmetrics connector sees them. Apply in
the pipeline that feeds into spanmetrics, not after:
processors:
transform/pre-spanmetrics:
error_mode: ignore
trace_statements:
- context: resource
statements:
- keep_keys(attributes, ["service.name", "k8s.deployment.name", "k8s.namespace.name"])
- context: span
statements:
- keep_keys(attributes, ["http.method", "http.route", "http.response.status_code", "db.system", "span.kind", "status_code"])
service:
pipelines:
traces/spanmetrics:
receivers: [forward/spans]
processors: [transform/pre-spanmetrics]
exporters: [spanmetrics]You can also configure the spanmetrics connector's dimensions: directly to limit which
attributes become metric labels — that is the preferred approach when the connector supports it.
evals
scenario-1
scenario-2
scenario-3
scenario-4
scenario-5
scenario-6
scenario-7
scenario-8
scenario-9
scenario-10
scenario-11
scenario-12
scenario-13
scenario-14
scenario-15
scenario-16
scenario-17
scenario-18
scenario-19
scenario-20
scenario-21
scenario-22
scenario-23
scenario-24
scenario-25
scenario-26
scenario-27
scenario-28
scenario-29
scenario-30
scenario-31
scenario-32
scenario-33
scenario-34
scenario-35
scenario-36
scenario-37
scenario-38
scenario-39
scenario-40
scenario-41
scenario-42
scenario-43
scenario-44
scenario-45
scenario-46
scenario-47
scenario-48
scenario-49
scenario-50
scenario-51
scenario-52
scenario-53
scenario-54
scenario-55
scenario-56
scenario-57
scenario-58
scenario-59
scenario-60
scenario-61
scenario-62
scenario-63
scenario-64
scenario-65
scenario-66
scenario-67
scenario-68
scenario-69
scenario-70
scenario-71
scenario-72
scenario-73
scenario-74
scenario-75
scenario-76
scenario-77
scenario-78
scenario-79
scenario-80
scenario-81
skills
opentelemetry
opentelemetry-collector
references
opentelemetry-instrumentation
opentelemetry-ottl