# Send through the OpenTelemetry Collector

> A standard Collector config that receives OTLP gRPC/HTTP from your apps and forwards to Belvador with otlphttp, a memory limit, batching, a queue and retries.

The [OpenTelemetry Collector](https://opentelemetry.io/docs/collector/) is a standard process that receives telemetry from your apps and forwards it. Use it when an exporter speaks only gRPC, when a runtime's exporter does not retry, when you want one place that holds the Belvador key, or for any language Belvador has not run itself.

> **Tested on 2026-10-27** with otelcol-contrib 0.163.1, telemetrygen v0.163.0: traces passed, logs passed, metrics passed.

## Configure

Use the **contrib** distribution (`otelcol-contrib`). This configuration uses standard components only:

```yaml title="collector.yaml"
receivers:
  otlp:
    protocols:
      grpc:
        endpoint: localhost:4317
      http:
        endpoint: localhost:4318

processors:
  memory_limiter:
    check_interval: 1s
    limit_percentage: 80
    spike_limit_percentage: 20
  batch: {}

exporters:
  otlphttp/belvador:
    endpoint: ${env:BELVADOR_OTLP_ENDPOINT}
    headers:
      Authorization: Bearer ${env:BELVADOR_INGEST_KEY}
    compression: gzip
    retry_on_failure:
      enabled: true
    sending_queue:
      enabled: true

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [memory_limiter, batch]
      exporters: [otlphttp/belvador]
    logs:
      receivers: [otlp]
      processors: [memory_limiter, batch]
      exporters: [otlphttp/belvador]
    metrics:
      receivers: [otlp]
      processors: [memory_limiter, batch]
      exporters: [otlphttp/belvador]

```

- **Receivers** accept OTLP over gRPC (`4317`) and HTTP (`4318`) from your apps. They listen on `localhost`; in a container, use `0.0.0.0:4317` and `0.0.0.0:4318` so other containers can reach them, and do not publish those ports to the internet.
- **`memory_limiter`** refuses new data before the Collector runs out of memory, so your apps' exporters back off instead of the Collector crashing.
- **`batch`** groups records into fewer, larger requests (well under the 4 MiB request limit).
- **`otlphttp/belvador`** sends to Belvador with gzip; `retry_on_failure` retries `429`, `502`, `503` and `504` with backoff, and `sending_queue` holds data in memory meanwhile.

## Run

```bash
export BELVADOR_OTLP_ENDPOINT=https://ingest.belvador.dev/otlp/<project-id>
export BELVADOR_INGEST_KEY=<your-ingest-key>
otelcol-contrib --config collector.yaml
```

With Docker, use the `otel/opentelemetry-collector-contrib` image with the same file mounted at `/etc/otelcol-contrib/config.yaml` and the two variables passed with `-e`.

## Point your apps at it

Apps send to the Collector without a key:

```bash
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf   # or grpc with port 4317
export OTEL_SERVICE_NAME=my-service
export OTEL_RESOURCE_ATTRIBUTES=deployment.environment.name=production
export OTEL_METRICS_EXPORTER=none   # otlp to send metrics too (key with Metrics)
```

Each app keeps its own `OTEL_SERVICE_NAME` and environment; the Collector passes resource attributes through unchanged.

## Surviving restarts

The queue above lives in memory: if the Collector restarts while Belvador is unreachable, queued data is lost. To keep it on disk, add the contrib `file_storage` extension and set `sending_queue.storage` to it. Belvador's run does not cover that setup.

## Log files

Apps that only write log files can be read by the contrib `filelog` receiver and sent through the same `logs` pipeline. Records read from files carry no trace id unless your log lines contain one and you parse it out; this receiver is not part of what is marked tested.
