Study guide

11.1 OTTL Concepts & Syntax Grammar

OpenTelemetry Certified Associate OTCA

Track your progress as you read

Finished sections get a check mark.

1 Chapter 1: Exam Overview & Observability Foundations

1.1 OTCA Exam Structure & Observability Paradigms 1.2 Core Telemetry Signals: Traces, Metrics, Logs & Baggage 1.3 Telemetry Analysis, SLOs & High Cardinality

2 Chapter 2: Semantic Conventions & Instrumentation Strategy

2.1 OpenTelemetry Semantic Conventions & Namespaces 2.2 Resource Attributes & Entity Modeling 2.3 Zero-Code vs Code-Based Instrumentation

3 Chapter 3: OpenTelemetry Architecture & The Data Model

3.1 Architectural Separation of API and SDK 3.2 The Core Trace Model: Spans, SpanContext & Links 3.3 Span Events, Attributes & Status Codes 3.4 Composability & Extension: Plugin Interfaces, Distributions & Contrib

4 Chapter 4: Distributed Tracing & Context Propagation

4.1 In-Process & Cross-Process Context Propagation 4.2 W3C Trace Context Specification 4.3 W3C Baggage & Alternative Propagators

5 Chapter 5: OpenTelemetry Metrics API & Instruments

5.1 Synchronous Metric Instruments 5.2 Asynchronous (Observable) Metric Instruments 5.3 Metric Aggregations & Temporality

6 Chapter 6: Logs Data Model & Logging Bridge API

6.1 OpenTelemetry Logs Data Model & Architecture 6.2 The Logging Bridge API & Log Appenders

7 Chapter 7: SDK Pipelines, Processors & Exporters

7.1 Span Processors & Exporters 7.2 Metric Readers & Log Processors 7.3 Standard Exporters & OTLP Wire Protocols

8 Chapter 8: Sampling Strategies, SDK Configuration & Agents

8.1 Head-Based Sampling & Built-in Samplers 8.2 Tail-Based Sampling vs Head-Based Sampling 8.3 OpenTelemetry SDK Configuration & Environment Variables 8.4 Zero-Code Agents: Configuration, Kubernetes Injection & Troubleshooting

9 Chapter 9: OpenTelemetry Collector Architecture & Pipelines

9.1 Collector Core Architecture & Component Lifecycle 9.2 Collector Configuration Structure & Pipelines 9.3 Essential Collector Extensions

10 Chapter 10: Collector Components: Receivers, Processors & Exporters

10.1 Standard Receivers & Ingestion Protocols 10.2 Core Processors & Execution Order 10.3 Standard Exporters & Delivery Guarantees

11 Chapter 11: Data Transformation & The OpenTelemetry Transformation Language (OTTL)

11.1 OTTL Concepts & Syntax Grammar 11.2 Transform Processor In Action 11.3 Advanced Connectors

12 Chapter 12: Collector Deployment, Scaling & Resilience

12.1 Agent vs Gateway Deployment Topologies 12.2 Scaling, Load Balancing & High Availability 12.3 Resilience: Backpressure, Buffering & Persistent Queues

13 Chapter 13: Maintaining, Debugging & Schema Evolution

13.1 Debugging Collector Pipelines & Telemetry Health 13.2 Troubleshooting Broken Traces & Context Loss 13.3 Schema Management & Telemetry Evolution

11.1 OTTL Concepts & Syntax Grammar

Key Takeaways Explain This Topic AI

The OpenTelemetry Transformation Language (OTTL) is an expressive domain-specific language designed specifically for the OpenTelemetry Collector to evaluate, mutate, and filter pdata across traces, metrics, and logs.

OTTL grammar is structured around statements composed of an invocable function, target path expressions, and an optional where condition clause that acts as a predicate filter.

Every OTTL statement executes strictly within an assigned Context (such as resource, scope, span, spanevent, metric, datapoint, or log), which dictates the available path hierarchy and data fields.

OTTL provides native support for strings, integers, floats, booleans, byte slices, lists, maps, and enums, along with explicit type conversion functions.

Path navigation uses dot notation for fixed telemetry struct fields (e.g. span.name, log.severity_number) and bracket indexing with double quotes for attribute and map lookups (e.g. attributes["http.response.status_code"]).

Last updated: September 2026

11.1 OTTL Concepts & Syntax Grammar

Quick Answer: The OpenTelemetry Transformation Language (OTTL) is a declarative domain-specific language (DSL) purpose-built for the OpenTelemetry Collector. It provides a standardized grammar to inspect, modify, enrich, and filter in-memory telemetry data ( pdata ) across traces, metrics, and logs. An OTTL statement follows the canonical form function(target, arguments...) where condition . Every statement executes within a designated Context (such as resource , scope , span , spanevent , metric , datapoint , or log ), which establishes the root of the telemetry tree and dictates which paths and metadata fields can be queried or mutated.

In modern enterprise observability architectures, raw telemetry emitted by diverse applications rarely arrives in an ideal state. Microservices may emit legacy attribute keys, sensitive credentials, unparsed JSON log payloads, or mismatched semantic conventions. Before OpenTelemetry standardized transformation, engineering teams were forced to chain multiple disparate Collector processors—such as attributesprocessor , resourceprocessor , filterprocessor , metricstransformprocessor , and logstransformprocessor —each with its own idiosyncratic configuration syntax, varying regex engines, and separate memory allocations. OTTL was introduced to replace this fragmented ecosystem with a single, highly performant, unified transformation engine.

Why OTTL Was Created: Unified Pipeline Mutation

In the OpenTelemetry Collector architecture, telemetry is represented internally using Go memory structures called pdata (Pluggable Data / Pipeline Data). pdata provides high-performance, memory-efficient abstractions over the underlying OpenTelemetry Protocol (OTLP) Protocol Buffers.

Before OTTL, transforming pdata across pipeline stages presented severe operational challenges:

Configuration Fragmentation: A team wanting to mask a credit card number in a log body had to use regex in logstransformprocessor , while dropping a span attribute required attributesprocessor , and appending a cluster identifier required resourceprocessor .

Serialization Overhead: Chaining five separate processors introduced cumulative CPU overhead, as each processor independently traversed and re-validated pipeline data structures.

Inconsistent Filtering Syntax: Filtering logic differed between traces (which used trace-specific Boolean trees) and metrics (which used metric label matching filters).

OTTL resolves these issues with one grammar shared by many components (the transform, filter, and routing components, and tail-sampling conditions). Statements are parsed once at startup into Go functions that operate directly on pdata , so no configuration is re-interpreted for each record.

The Anatomy of OTTL Grammar

An OTTL transformation is expressed through statements , expressions , paths , and predicates . +-----------------------------------------------------------------------------------+

  • | OTTL Statement Grammar |
  • +-----------------------------------------------------------------------------------+
  • | set(attributes["customer.tier"], "gold") where attributes["customer.id"] == 101 |
  • | │ │ │ │ │ |
  • | │ │ │ │ └─ Condition Predicate (Boolean) |
  • | │ │ │ └─────── Where Keyword |
  • | │ │ └─────────────── Value Expression (Literal) |
  • | │ └──────────────────────────────────────────── Target Path Expression |
  • | └──────────────────────────────────────────────── Invocable Transformation Function
  • +-----------------------------------------------------------------------------------+
  • 1. Invocable Functions: Editors and Converters

Every statement begins with an invocable function name followed by comma-separated arguments in parentheses: function_name(arg1, arg2, ...) . OTTL has two kinds of functions:

Editors have lowercase names ( set , delete_key , keep_keys , replace_pattern , merge_maps ) and change telemetry . A statement always starts with an editor.

Converters have UpperCamelCase names ( Concat , ParseJSON , IsMatch , Int , ExtractPatterns ) and return a value . They appear as arguments to editors or inside where conditions and never change data on their own.

2. Path Expressions

A Path points to a specific field or map entry within the pdata hierarchy. OTTL paths adhere to strict formatting conventions:

Dot Notation ( . ) : Used to traverse fixed struct fields defined in the OpenTelemetry data model (e.g., span.name , span.kind , log.severity_number , status.code ).

Bracket Notation ( ["key"] ) : Used to access key-value collections such as attribute maps or resource metadata (e.g., attributes["http.response.status_code"] , resource.attributes["k8s.pod.name"] ).

Context-Qualified Paths : Current OTTL also accepts paths that start with the context name, such as span.attributes["region"] , log.body , or resource.attributes["service.name"] . With these, the transform processor's basic configuration can infer the context of each statement automatically.

Keys inside bracket notation must be enclosed in double quotes .

3. Conditions (Predicates) and the where Clause

A statement may optionally include a where clause. When present, the function executes only if the condition evaluates to true for the record being processed. If the condition evaluates to false, the record passes through unmodified without error.

OTTL supports rich logical and relational operators:

  • Equality / Inequality: == , !=
  • Relational Comparisons: < , <= , > , >= (applicable to numbers and timestamps)
  • Logical Operators: and , or , not
  • Grouping: Parentheses ( ... ) to enforce precedence
  • Nil Evaluation: Checking whether an attribute exists: attributes["auth.token"] != nil

Example of a compound condition: set(attributes["environment"], "production") where attributes["env"] == "prod" or (resource.attributes["cloud.provider"] == "aws" and attributes["region"] == "us-east-1")

The OTTL Execution Context Hierarchy

In OpenTelemetry, telemetry is structured in a strict object hierarchy: a batch contains Resources , each Resource contains Instrumentation Scopes , each Scope contains Telemetry Items (Spans, Metric DataPoints, or Logs), and Spans/Metrics contain nested sub-items (SpanEvents, Histogram Buckets).

An OTTL statement must operate within a designated Context . The context sets the local operational root. Paths resolve relative to this context, while parent objects remain accessible via explicit qualifiers. +-----------------------------------------------------------------------------------+

  • | OTTL Context Resolution Hierarchy |
  • +-----------------------------------------------------------------------------------+
  • | Resource Context |
  • | └── resource.attributes, resource.dropped_attributes_count |
  • | │ |
  • | ├── Scope Context |
  • | │ └── instrumentation_scope.name, instrumentation_scope.version |
  • | │ │ |
  • | │ ├── Span Context |
  • | │ │ ├── name, kind, trace_id, span_id, status.code, attributes[...] |
  • | │ │ └── SpanEvent Context |
  • | │ │ └── name, time_unix_nano, attributes[...] |
  • | │ │ |
  • | │ ├── Metric / DataPoint Context |
  • | │ │ ├── metric.name, metric.unit, metric.type |
  • | │ │ └── value_int, value_double, time_unix_nano, attributes[...] |
  • | │ │ |
  • | │ └── Log Context |
  • | │ └── body, severity_number, severity_text, attributes[...] |
  • +-----------------------------------------------------------------------------------+
  • The Seven Standard OTTL Contexts

resource Context:

Scope: Operates once per Resource entity across all signals.

Local Paths: attributes , dropped_attributes_count .

Use Case: Normalizing host, container, cloud, or cluster metadata before downstream signal processing.

scope Context:

Scope: Operates at the Instrumentation Scope boundary (the library or package that emitted the telemetry).

Local Paths: name , version , attributes , dropped_attributes_count .

Use Case: Overriding instrumentation library versions or scoping flags.

span Context:

Scope: Operates on individual distributed trace spans.

Local Paths: name , kind , start_time_unix_nano , end_time_unix_nano , trace_id , span_id , parent_span_id , trace_state , status.code , status.message , attributes .

Parent Paths: Full read/write access to resource.attributes and instrumentation_scope.name .

Use Case: Redacting sensitive URLs in span attributes, fixing span names, updating span status.

spanevent Context:

Scope: Operates on discrete events embedded within a span.

Local Paths: name , time_unix_nano , attributes , dropped_attributes_count .

Parent Paths: Access to the parent span's fields via span.* and resource.* .

Use Case: Scrubbing stack trace arguments or masking parameters recorded in span exception events.

metric Context:

Scope: Operates on metric metadata across all data points of that metric.

Local Paths: name , description , unit , type .

Parent Paths: Access to resource.attributes .

Use Case: Renaming metric instruments or standardizing metric unit strings (e.g., converting ms to s ).

datapoint Context:

Scope: Operates on individual metric measurements (Gauge, Sum, Histogram, Summary).

Local Paths: value_int , value_double , start_time_unix_nano , time_unix_nano , flags , attributes , exemplars .

Parent Paths: Access to metric metadata via metric.* and resource metadata via resource.* .

Use Case: Adding or dropping label dimensions on specific metric data points, converting values.

log Context:

Scope: Operates on individual log records.

Local Paths: time_unix_nano , observed_time_unix_nano , severity_number , severity_text , body , flags , trace_id , span_id , attributes .

Parent Paths: Access to resource.attributes and instrumentation_scope.name .

Use Case: Parsing raw string bodies into structured JSON attributes, mapping severity levels, masking PII.

OTTL Context & Path Reference Matrix

The following reference table summarizes the contexts, target signals, accessible paths, and common field paths:

Context Target Signal Local Target Fields Parent Accessible Paths Common Field References

resource All (Traces, Metrics, Logs) attributes , dropped_attributes_count None attributes["service.name"] , attributes["k8s.namespace.name"]

scope All (Traces, Metrics, Logs) name , version , attributes resource.* name , version , attributes["custom.scope.flag"]

span Traces name , kind , status.code , attributes resource.* , instrumentation_scope.* name , attributes["http.response.status_code"] , status.code

spanevent Traces name , time_unix_nano , attributes span.* , resource.* name , attributes["exception.message"] , span.name

metric Metrics name , description , unit , type resource.* , instrumentation_scope.* name , unit , description

datapoint Metrics value_int , value_double , attributes metric.* , resource.* value_double , attributes["http.request.method"] , metric.name

log Logs body , severity_number , attributes resource.* , instrumentation_scope.* body , severity_number , attributes["user.id"]

OTTL Data Types and Literal Representation

OTTL is a strictly typed language. When executing comparisons or setting values, literals and types must conform to supported Go primitives:

  • Data Type Syntax / Literal Example Supported Operations Common Type Conversion Functions
  • String "production" , "/api/v1/checkout" Concatenation, regex matching, casing String(val)
  • Integer 200 , -1 , 8080 Arithmetic, relational comparisons Int(val)
  • Float / Double 3.14159 , 0.05 Floating-point math, comparisons Double(val)
  • Boolean true , false Logical evaluation ( and , or , not ) Bool(val)
  • Byte Slice 0x1a2b3c4d Hashing, binary decoding Bytes(val)
  • List / Array ["prod", "staging", "qa"] Element lookup Split(str, delimiter) returns a list
  • Map {"env": "prod", "tier": 1} Key indexing map["key"] ParseJSON(str)
  • Enum SPAN_KIND_SERVER , STATUS_CODE_ERROR Equality checks on telemetry kinds N/A
  • Nil nil Existence check ( == nil , != nil ) N/A
  • Important Exam Distinction: Nil vs. Empty String

In OTTL, an attribute key that does not exist evaluates to nil . An attribute that exists with an empty string value evaluates to "" .

To check if a key is missing: attributes["user.email"] == nil

To check if a key is present and contains text: attributes["user.email"] != nil and attributes["user.email"] != ""

Loading diagram... OTTL Context Hierarchy and Field Resolution