OpenTelemetry Collector
A ready-made Collector configuration that passes every Trailbeam span to Trailbeam, and optionally to your tracing backend.
The Collector is optional. The packages can export straight to Trailbeam. Use it when you already run a Collector, or when you also want SQS spans in your own tracing backend.
Install#
It needs the contrib distribution, otelcol-contrib, because the configs use the filter and tail_sampling processors. The configs are tested with 0.165.0 (CI downloads that release; the demo uses the image otel/opentelemetry-collector-contrib:0.165.0).
Two files in collector/:
| File | What it does |
|---|---|
trailbeam-collector.yaml |
Receives OTLP. Sends every span with instrumentation scope trailbeam to Trailbeam, unsampled. Sends nothing else to Trailbeam. |
backend.yaml |
Optional overlay. Also sends traces to your backend: every SQS span (Trailbeam and stock AWS SDK) unsampled, everything else tail-sampled. |
Run it#
Trailbeam only:
TRAILBEAM_STORE_ENDPOINT=http://localhost:4319 otelcol-contrib --config trailbeam-collector.yamlTrailbeam plus your backend:
TRAILBEAM_BACKEND_ENDPOINT=http://jaeger:4318 \
otelcol-contrib --config trailbeam-collector.yaml --config backend.yamlPoint the packages at the Collector. The default already matches when it runs on the same host:
TRAILBEAM_OTLP_TRACES_ENDPOINT=http://localhost:4318/v1/tracesIn Docker#
Listen on all interfaces inside a container. The demo builds a small image from demo/collector-image/Dockerfile:
FROM otel/opentelemetry-collector-contrib:0.165.0
COPY collector/trailbeam-collector.yaml /etc/trailbeam/trailbeam-collector.yaml
CMD ["--config", "/etc/trailbeam/trailbeam-collector.yaml"]and sets its environment in demo/compose.yaml:
collector:
build: { context: .., dockerfile: demo/collector-image/Dockerfile }
environment:
TRAILBEAM_COLLECTOR_HTTP_ENDPOINT: 0.0.0.0:4318
TRAILBEAM_COLLECTOR_GRPC_ENDPOINT: 0.0.0.0:4317
TRAILBEAM_STORE_ENDPOINT: http://trailbeam:4318
depends_on: [trailbeam]Trailbeam runs in its own container (trailbeam) on 4318, so there is no port clash. Services export to http://collector:4318/v1/traces.
Options#
All settings are environment variables read by the YAML with ${env:NAME:-default}.
| Option | Type | Default | What it does |
|---|---|---|---|
TRAILBEAM_COLLECTOR_HTTP_ENDPOINT |
host:port |
localhost:4318 |
OTLP/HTTP listen address. Use 0.0.0.0:4318 in a container. |
TRAILBEAM_COLLECTOR_GRPC_ENDPOINT |
host:port |
localhost:4317 |
OTLP/gRPC listen address. |
TRAILBEAM_STORE_ENDPOINT |
URL | http://localhost:4319 |
Base URL of Trailbeam. The exporter appends /v1/traces. |
TRAILBEAM_BACKEND_ENDPOINT |
URL | none, required by backend.yaml |
Base OTLP/HTTP URL of your tracing backend. |
TRAILBEAM_BACKEND_SAMPLING_PERCENTAGE |
number | 10 |
Percentage of non-SQS traces kept for the backend (probabilistic tail sampling). |
How the pipelines work#
trailbeam-collector.yaml#
otlp receiver ─▶ filter/trailbeam_only ─▶ batch ─▶ otlp_http/trailbeam_storefilter/trailbeam_onlydrops every span whereinstrumentation_scope.name != "trailbeam". Trailbeam only readstrailbeamspans, so nothing else is sent.otlp_http/trailbeam_storeexports withcompression: gzip. Trailbeam acceptsContent-Encoding: gzip.retry_on_failureis on withmax_elapsed_time: 0s: it retries for as long as Trailbeam is down.sending_queueis in memory withqueue_size: 5000batches.
There is no sampling step. The packages already use an always-on tracer provider, so your app's head sampling never drops Trailbeam spans, and this pipeline keeps them too.
backend.yaml overlay#
It adds two pipelines from the same otlp receiver:
| Pipeline | Processors | Exporter | Result |
|---|---|---|---|
traces/backend_sqs |
filter/sqs_only, batch |
otlp_http/backend |
Every SQS span, unsampled |
traces/backend_rest |
filter/drop_sqs, tail_sampling/rest, batch |
otlp_http/backend |
Everything else, sampled at TRAILBEAM_BACKEND_SAMPLING_PERCENTAGE |
A span counts as SQS when any of these is true:
instrumentation_scope.name == "trailbeam"attributes["rpc.service"] == "SQS"attributes["rpc.system"] == "aws-api"andattributes["rpc.service"] == "Sqs"attributes["messaging.system"]matches^(aws.sqs|aws_sqs|AmazonSQS)$attributes["messaging.system.name"] == "aws.sqs"
filter/drop_sqs uses the exact same expression, inverted, so each span goes to exactly one backend pipeline. tail_sampling/rest uses decision_wait: 5s, num_traces: 100000 and one probabilistic policy.
The trade-off: in your backend, a sampled-out trace still shows its SQS spans, without their parent HTTP or database spans. Trailbeam pipeline is unchanged by the overlay. See Keep every SQS span when you sample.
What it emits#
The Collector creates no spans. It forwards the five Trailbeam span kinds unchanged: trailbeam send, trailbeam receive, trailbeam delete, trailbeam change_visibility, trailbeam outcome. See the span contract.
Verify it works#
- Start Trailbeam on 4319 and the Collector with
trailbeam-collector.yaml. - Run an instrumented service with
TRAILBEAM_OTLP_TRACES_ENDPOINT=http://localhost:4318/v1/traces. - Check Trailbeam's counters:
curl -s http://127.0.0.1:4319/api/health{"status": "ok", "version": "0.1.0.dev0",
"ingest": {"requests": 4, "inserted": 120, "duplicates": 0, "ignored": 0, "rejected": 0},
"store": {"spans": {"send": 40, "receive": 40, "delete": 40}, "messages": 40, "unjoined_settles": 0},
"config": {"stuck_after_s": 300, "lambda_timeout_s": 900, "max_receive_counts": {}}}ingest.ignored stays at 0 behind the Collector, because the filter already removed non-Trailbeam spans. If it grows, something is exporting to Trailbeam directly.
The repo's test, collector/tests/test_collector.py, runs the real binary between two fake OTLP endpoints and checks that Trailbeam gets exactly the Trailbeam spans, and that with backend.yaml at 0% sampling the backend still gets every Trailbeam and stock SQS span:
pip install pytest opentelemetry-proto
TRAILBEAM_OTELCOL_BIN=/path/to/otelcol-contrib pytest collector/testsWithout a binary the tests are skipped.
Common errors#
| What you see | Cause | Fix |
|---|---|---|
Collector rejects the config: the filter or tail_sampling processor is unknown |
Core otelcol distribution, which lacks them |
Use otelcol-contrib. |
| Collector or Trailbeam cannot bind 4318 | Both on one host with default ports | Run Trailbeam with --port 4319. |
| Trailbeam logs nothing, services get connection refused | Collector listens on localhost inside a container |
Set TRAILBEAM_COLLECTOR_HTTP_ENDPOINT=0.0.0.0:4318. |
Store answers 404 not found to the Collector |
TRAILBEAM_STORE_ENDPOINT already ends in /v1/traces, so the path is doubled |
Use the base URL, e.g. http://trailbeam:4318. |
| Backend exporter has no endpoint | TRAILBEAM_BACKEND_ENDPOINT not set; it has no default |
Set it to your backend's base OTLP/HTTP URL. |
| Steps missing after Trailbeam was down | Sending queue filled up; spans dropped | Keep Trailbeam up; size outages against 5,000 batches. |
| Backend traces show SQS spans with no parent | Expected trade-off of the split | Raise TRAILBEAM_BACKEND_SAMPLING_PERCENTAGE if you need more full traces. |
Related#
Collector
OpenTelemetry terms in Vocabulary
Span
OpenTelemetry terms in Vocabulary
OTLP
OpenTelemetry terms in Vocabulary
Instrumentation scope
trailbeam.OpenTelemetry terms in Vocabulary
Sampling
OpenTelemetry terms in Vocabulary
Trailbeam span
trailbeam. Trailbeam reads only these.Queue