Skip to main content
On the BEAM, OpenTelemetry is configured entirely in application config — no initialisation code. One config block sends logs and traces to Bronto: the SDK installs a kernel logger handler that captures every Logger / :logger call, and framework instrumentation libraries produce spans for Phoenix, Ecto, and friends. The application exports to an OpenTelemetry Collector, which forwards to Bronto. If you don’t run a Collector, export directly to Bronto instead — only the endpoint and an API key header change.
Metrics on the BEAM live in the separate opentelemetry_experimental package and the instrument API is still unstable. Logs and traces, covered below, are stable. See metrics at the end of this page.

Prerequisites

  • Erlang/OTP 24 or later, or Elixir 1.13 or later
  • An OTel Collector reachable from your application, with logs, metrics, and traces pipelines forwarding to Bronto — see Connect OpenTelemetry Collector to Bronto

Install dependencies

See Erlang/Elixir instrumentation libraries for the full list, including LiveView and Absinthe.

Configure the SDK

That’s the whole setup. otlp_endpoint is a base URL — the exporter appends /v1/logs and /v1/traces itself. The kernel logger handler is installed when the opentelemetry application starts, so make sure it is listed in extra_applications (Elixir) or applications (Erlang) and starts before your own. Two resource attributes determine how Bronto organises your data:
http://localhost:4318 is the standard OTLP/HTTP address for a Collector on the same host. Use the address reachable from your application if the Collector runs in another container, pod, or host. No authentication is needed between the application and the Collector — the Collector holds the Bronto API key.

Instrument your application

Existing Logger / :logger calls need no changes — the OTel handler runs alongside your console and file handlers. For spans around your own business logic:
Any log emitted inside an active span automatically carries that span’s trace_id and span_id, so you can jump from a log line to its trace in Bronto.

Verify

Run your application, then check each signal in Bronto, filtering by the service.name you set:
  • Logs — the Search page, in the dataset named after your service
  • Traces — the Explore Traces page
If nothing arrives:
  • Confirm the Collector is running and reachable at otlp_endpoint, and that its pipelines include an otlp receiver and the Bronto exporters.
  • Confirm the opentelemetry application starts before your own application.
  • Port 4318 is OTLP/HTTP and 4317 is OTLP/gRPC. If you point at 4317, set otlp_protocol: :grpc.
For signal-specific reference material, see Send Traces to Bronto.

Metrics

BEAM metrics require the opentelemetry_experimental package, which provides the meter and instrument API and a periodic reader. Reuse the same resource and OTLP exporter config as above, and check the OpenTelemetry Erlang/Elixir documentation for the instrument API in the release you deploy. An established alternative on the BEAM is to export existing telemetry metrics through TelemetryMetricsPrometheus and scrape them with the Collector’s prometheus receiver, which forwards them to Bronto through the same pipeline. See Send Metrics to Bronto.

Direct export to Bronto

Without a Collector, the application exports straight to Bronto over OTLP/HTTP. Point the endpoint at your Bronto region and add your API key:
See API Keys for how to create a key with ingestion permissions.
Prefer the Collector for multi-service environments — it batches, filters, and enriches telemetry, and keeps the API key out of every application. Direct export suits single services and simple architectures.

GenAI semantic conventions

If your application calls an LLM, OpenTelemetry’s GenAI semantic conventions define gen_ai.* span attributes for model, token usage, and prompt and response content.

Manual spans

There is no first-party GenAI instrumentation for Erlang or Elixir, so set the attributes yourself around each model call:
Message content (gen_ai.input.messages / gen_ai.output.messages) is opt-in by convention and off by default in the languages that have auto-instrumentation. Since you are setting attributes by hand, apply the same discipline — gate prompt and response content behind your own config flag rather than always sending it. See the GenAI span conventions for the full attribute list. For the recommended attribute set and Bronto search queries, see LLM Observability.