Collector 0.162.0
The telemetry pipeline
Follow the data
Processors
What it does
Run in order on everything that passes. memory_limiter refuses data before the Collector runs out of memory, attributes/scrub deletes attributes that must never be stored, batch groups items into fewer, larger exports.
Why it exists
Policy lives in one place instead of in every service: protection, redaction, sampling, enrichment, batching.
What flows through
The same signals, minus scrubbed attributes, grouped into batches of up to 1024 items or 2 s.
processors:
memory_limiter:
check_interval: 1s
limit_percentage: 80
attributes/scrub:
actions:
- key: user.email
action: delete
batch:
send_batch_size: 1024
timeout: 2sThree pipelines
| Pipeline | Receiver | Processors, in order | Exporters | Ends up in |
|---|---|---|---|---|
| traces | otlp | memory_limiter → attributes/scrub → batch | otlp_grpc/tempo, debug | Tempo |
| metrics | otlp | memory_limiter → batch | otlp_http/prometheus, debug | Prometheus (OTLP endpoint) |
| logs | otlp | memory_limiter → attributes/scrub → batch | otlp_http/loki, debug | Loki (OTLP endpoint) |
Order matters: memory_limiter goes first so overload is refused before work is done, batch goes last so sampling and filtering happen before batching. Tempo also derives service-graph and span metrics from the traces it receives and writes them to Prometheus.
The configuration
infra/otel/collector.yaml on GitHub →# OpenTelemetry Collector configuration (otelcol-contrib 0.162.0).
#
# Every service sends traces, metrics and logs here over OTLP. The Collector is the only thing
# that knows where telemetry ends up: swap a backend by editing this file, not the services.
#
# receivers how telemetry gets in (OTLP over gRPC :4317 and HTTP :4318)
# processors what happens to it on the way (protect memory, scrub, batch)
# exporters where it goes (Tempo, Prometheus, Loki, and the console)
# pipelines which receivers, processors and exporters each signal uses
#
# Check it without starting anything:
# docker compose run --rm otel-collector validate --config=/etc/otelcol-contrib/config.yaml
extensions:
# GET :13133/ answers 200 while the Collector is running. Docker uses it as a health check.
health_check:
endpoint: 0.0.0.0:13133
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
# First in every pipeline: refuse data before the Collector runs out of memory.
memory_limiter:
check_interval: 1s
limit_percentage: 80
spike_limit_percentage: 25
# Defence in depth: drop attributes that must never be stored, even if a service sends them.
# The services do not set these; this is the safety net for when someone eventually does.
attributes/scrub:
actions:
- key: http.request.header.authorization
action: delete
- key: http.request.header.cookie
action: delete
- key: user.email
action: delete
- key: user.password
action: delete
# Last before export: group items into batches. Fewer, larger requests to each backend.
batch:
send_batch_size: 1024
timeout: 2s
exporters:
# Traces to Tempo over OTLP/gRPC.
otlp_grpc/tempo:
endpoint: tempo:4317
tls:
insecure: true
# Metrics to Prometheus' native OTLP endpoint (Prometheus runs with --web.enable-otlp-receiver).
otlp_http/prometheus:
endpoint: http://prometheus:9090/api/v1/otlp
tls:
insecure: true
# Logs to Loki's native OTLP endpoint.
otlp_http/loki:
endpoint: http://loki:3100/otlp
tls:
insecure: true
# A one-line summary per batch in `docker compose logs otel-collector`. Set verbosity to
# `detailed` to print every span, metric and log record while you learn.
debug:
verbosity: basic
service:
extensions: [health_check]
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, attributes/scrub, batch]
exporters: [otlp_grpc/tempo, debug]
metrics:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [otlp_http/prometheus, debug]
logs:
receivers: [otlp]
processors: [memory_limiter, attributes/scrub, batch]
exporters: [otlp_http/loki, debug]
telemetry:
# The Collector's own metrics (accepted, refused, exported items), scraped by Prometheus.
metrics:
readers:
- pull:
exporter:
prometheus:
host: 0.0.0.0
port: 8888Validate it without starting anything: docker compose run --rm otel-collector validate --config=/etc/otelcol-contrib/config.yaml. Chapters 06 and 07 walk through it and change it.
Batching
Exporting 1000 spans
Educational simulation, not live data- Export requests
- 10
- Overhead (model)
- 20 ms
- Fewer requests than unbatched
- 99%
A toy cost model: 2 ms of fixed cost per request. One span per request pays the fixed cost 1000 times. Batching pays it 10 times, compresses better and makes fewer connections. The trade-off is delivery delay: an item waits until the batch fills or the timeout (2 s here) fires, and a crash loses what was still buffered.
Sampling
1000 requests, one sampling policy
Educational simulation, not live datanormal, keptslow (> 1 s), kepterror, keptdropped
- Traces stored
- 100 / 1000
- Errors and slow traces kept
- 5 / 45
- Storage vs keeping all
- 10%
Head sampling decides when a trace starts, before anyone knows whether it will fail. It is cheap and simple, and it throws away most of the traces you will want during an incident.
The lab keeps every trace by default. Chapter 12 switches the real Collector to the tail-sampling configuration (keep errors, keep traces over 1 s, 10% of the rest) so you can measure the effect on live traffic instead of trusting this simulation.