OTLP Exporter Configuration for OpenTelemetry Erlang/Elixir

This document shows how to configure the OpenTelemetry Erlang/Elixir SDK to export telemetry data to Uptrace using the OTLP exporter. For the getting started guide, see Monitor OpenTelemetry Erlang/Elixir with Uptrace.

OTLP exporter configuration

The OpenTelemetry Erlang/Elixir SDK uses the OTLP exporter to send telemetry data to Uptrace. You can configure it using application configuration or environment variables.

For more details on the OpenTelemetry APIs, see:

Exporting traces

Configure the OTLP trace exporter to send distributed traces to Uptrace:

elixir Elixir
# config/config.exs or config/runtime.exs
config :opentelemetry,
  span_processor: :batch,
  traces_exporter: :otlp

config :opentelemetry_exporter,
  otlp_protocol: :grpc,
  otlp_compression: :gzip,
  otlp_endpoint: "https://api.uptrace.dev:4317",
  otlp_headers: [{"uptrace-dsn", System.get_env("UPTRACE_DSN")}]
erlang Erlang
%% sys.config
[
 {opentelemetry,
  [{span_processor, batch},
   {traces_exporter, otlp}]},

 {opentelemetry_exporter,
  [{otlp_protocol, grpc},
   {otlp_compression, gzip},
   {otlp_endpoint, "https://api.uptrace.dev:4317"},
   {otlp_headers, [{"uptrace-dsn", os:getenv("UPTRACE_DSN")}]}]}
].

HTTP/protobuf protocol

You can also use the HTTP/protobuf protocol instead of gRPC:

elixir Elixir
config :opentelemetry_exporter,
  otlp_protocol: :http_protobuf,
  otlp_compression: :gzip,
  otlp_endpoint: "https://api.uptrace.dev",
  otlp_headers: [{"uptrace-dsn", System.get_env("UPTRACE_DSN")}]
erlang Erlang
{opentelemetry_exporter,
 [{otlp_protocol, http_protobuf},
  {otlp_compression, gzip},
  {otlp_endpoint, "https://api.uptrace.dev"},
  {otlp_headers, [{"uptrace-dsn", os:getenv("UPTRACE_DSN")}]}]}

Exporting metrics

Configure the OTLP metrics exporter to send OpenTelemetry metrics to Uptrace:

elixir Elixir
# config/runtime.exs
config :opentelemetry_experimental,
  metrics_exporter: :otlp

config :opentelemetry_exporter,
  otlp_protocol: :grpc,
  otlp_compression: :gzip,
  otlp_endpoint: "https://api.uptrace.dev:4317",
  otlp_headers: [{"uptrace-dsn", System.get_env("UPTRACE_DSN")}]
erlang Erlang
%% sys.config
[
 {opentelemetry_experimental,
  [{metrics_exporter, otlp}]},

 {opentelemetry_exporter,
  [{otlp_protocol, grpc},
   {otlp_compression, gzip},
   {otlp_endpoint, "https://api.uptrace.dev:4317"},
   {otlp_headers, [{"uptrace-dsn", os:getenv("UPTRACE_DSN")}]}]}
].

Environment variables

You can configure the OTLP exporter using environment variables, which is particularly useful for containerized deployments:

bash
# gRPC protocol (recommended)
export OTEL_EXPORTER_OTLP_ENDPOINT="https://api.uptrace.dev:4317"
export OTEL_EXPORTER_OTLP_PROTOCOL="grpc"
export OTEL_EXPORTER_OTLP_COMPRESSION="gzip"
export OTEL_EXPORTER_OTLP_HEADERS="uptrace-dsn=<YOUR_UPTRACE_DSN>"

# HTTP/protobuf protocol
export OTEL_EXPORTER_OTLP_ENDPOINT="https://api.uptrace.dev"
export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
export OTEL_EXPORTER_OTLP_COMPRESSION="gzip"
export OTEL_EXPORTER_OTLP_HEADERS="uptrace-dsn=<YOUR_UPTRACE_DSN>"

Signal-specific configuration

You can configure different endpoints for traces and metrics:

bash
# Traces
export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT="https://api.uptrace.dev:4317"
export OTEL_EXPORTER_OTLP_TRACES_PROTOCOL="grpc"
export OTEL_EXPORTER_OTLP_TRACES_COMPRESSION="gzip"
export OTEL_EXPORTER_OTLP_TRACES_HEADERS="uptrace-dsn=<YOUR_UPTRACE_DSN>"

# Metrics
export OTEL_EXPORTER_OTLP_METRICS_ENDPOINT="https://api.uptrace.dev:4317"
export OTEL_EXPORTER_OTLP_METRICS_PROTOCOL="grpc"
export OTEL_EXPORTER_OTLP_METRICS_COMPRESSION="gzip"
export OTEL_EXPORTER_OTLP_METRICS_HEADERS="uptrace-dsn=<YOUR_UPTRACE_DSN>"

Batch processor configuration

The batch processor buffers spans before sending them to the exporter. Configure it for optimal performance:

elixir Elixir
config :opentelemetry,
  span_processor: :batch,
  bsp_scheduled_delay_ms: 5000,      # Delay between exports (default: 5000)
  bsp_max_queue_size: 10000,         # Max spans in queue (default: 2048)
  bsp_max_export_batch_size: 512,    # Max spans per batch (default: 512)
  bsp_exporting_timeout_ms: 30000    # Export timeout (default: 30000)
erlang Erlang
{opentelemetry,
 [{span_processor, batch},
  {bsp_scheduled_delay_ms, 5000},
  {bsp_max_queue_size, 10000},
  {bsp_max_export_batch_size, 512},
  {bsp_exporting_timeout_ms, 30000}]}

Complete example

Here's a complete configuration example for a Phoenix application:

elixir
# config/runtime.exs
import Config

# OpenTelemetry SDK configuration
config :opentelemetry,
  span_processor: :batch,
  traces_exporter: :otlp,
  resource: %{
    service: %{
      name: System.get_env("OTEL_SERVICE_NAME", "my-phoenix-app"),
      version: "1.0.0"
    },
    deployment: %{
      environment: System.get_env("MIX_ENV", "development")
    }
  }

# OTLP exporter configuration
config :opentelemetry_exporter,
  otlp_protocol: :grpc,
  otlp_compression: :gzip,
  otlp_endpoint: System.get_env("OTEL_EXPORTER_OTLP_ENDPOINT", "https://api.uptrace.dev:4317"),
  otlp_headers: [
    {"uptrace-dsn", System.get_env("UPTRACE_DSN")}
  ]
elixir
# lib/my_app/application.ex
defmodule MyApp.Application do
  use Application

  def start(_type, _args) do
    # Setup OpenTelemetry instrumentations before starting supervision tree
    :opentelemetry_cowboy.setup()
    OpentelemetryPhoenix.setup(adapter: :cowboy2)
    OpentelemetryEcto.setup([:my_app, :repo])

    children = [
      MyApp.Repo,
      {Phoenix.PubSub, name: MyApp.PubSub},
      MyAppWeb.Endpoint
    ]

    opts = [strategy: :one_for_one, name: MyApp.Supervisor]
    Supervisor.start_link(children, opts)
  end
end

Troubleshooting

Testing with stdout exporter

Use the stdout exporter to verify spans are being created correctly:

elixir Elixir
# config/dev.exs
config :opentelemetry,
  traces_exporter: {:otel_exporter_stdout, []}
erlang Erlang
{opentelemetry,
 [{traces_exporter, {otel_exporter_stdout, []}}]}

Connection issues

If traces don't appear in Uptrace:

  1. Verify DSN: Check that your Uptrace DSN is correctly set
  2. Check connectivity: Ensure your application can reach api.uptrace.dev:4317 (gRPC) or api.uptrace.dev (HTTP)
  3. Review logs: Look for OpenTelemetry error messages in your application logs
  4. Check firewall: Ensure outbound connections on port 4317 (gRPC) or 443 (HTTP) are allowed

Verifying export

Add logging to confirm spans are being exported:

elixir
# In application.ex, add after OpenTelemetry setup
require Logger
Logger.info("OpenTelemetry configured with exporter: #{inspect(Application.get_env(:opentelemetry, :traces_exporter))}")

What's next?