OpenTelemetry Erlang/Elixir for Uptrace

This document explains how to configure the OpenTelemetry Erlang/Elixir SDK to export spans (traces) and metrics to Uptrace using OTLP/gRPC.

Quick Start Guide

Follow these steps to get your first trace running in 5 minutes:

Step 1: Create an Uptrace Project

Create an Uptrace project to obtain a DSN (Data Source Name), for example, https://<secret>@api.uptrace.dev?grpc=4317.

Step 2: Install dependencies

Add the necessary OpenTelemetry packages to your project dependencies:

elixir Elixir
# mix.exs
defp deps do
  [
    {:opentelemetry, "~> 1.3"},
    {:opentelemetry_api, "~> 1.2"},
    {:opentelemetry_exporter, "~> 1.6"}
  ]
end
erlang Erlang
%% rebar.config
{deps, [
  {opentelemetry, "~> 1.3"},
  {opentelemetry_api, "~> 1.2"},
  {opentelemetry_exporter, "~> 1.6"}
]}.

Run the appropriate command to install dependencies:

bash
# For Elixir projects
mix deps.get

# For Erlang projects
rebar3 compile

Step 3: Basic Configuration

Configure the OpenTelemetry SDK to export traces to Uptrace. Replace <YOUR_UPTRACE_DSN> with your actual Uptrace DSN from the project settings.

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", "<YOUR_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", "<YOUR_UPTRACE_DSN>"}]}]}
].

Step 4: Create Your First Trace

Create a simple example to verify the setup:

elixir Elixir
# lib/my_app/example.ex
defmodule MyApp.Example do
  require OpenTelemetry.Tracer

  def main do
    # Create a tracer
    OpenTelemetry.Tracer.with_span "main-operation" do
      OpenTelemetry.Tracer.set_attribute("user.id", "12345")

      # Create a child span
      OpenTelemetry.Tracer.with_span "child-operation" do
        OpenTelemetry.Tracer.set_attributes(%{
          "http.method" => "GET",
          "http.url" => "http://localhost:8080/api/users"
        })

        # Simulate some work
        :timer.sleep(100)
      end
    end

    IO.puts("Trace sent to Uptrace!")
  end
end
erlang Erlang
%% src/my_app_example.erl
-module(my_app_example).
-export([main/0]).
-include_lib("opentelemetry_api/include/otel_tracer.hrl").

main() ->
    ?with_span(<<"main-operation">>, #{}, fun() ->
        ?set_attribute(<<"user.id">>, <<"12345">>),

        ?with_span(<<"child-operation">>, #{}, fun() ->
            ?set_attributes([
                {<<"http.method">>, <<"GET">>},
                {<<"http.url">>, <<"http://localhost:8080/api/users">>}
            ]),

            %% Simulate some work
            timer:sleep(100)
        end)
    end),

    io:format("Trace sent to Uptrace!~n").

Step 5: Run Your Application

Run the code with your Uptrace DSN set:

bash
# For Elixir
UPTRACE_DSN="<FIXME>" mix run -e "MyApp.Example.main()"

# For Erlang
UPTRACE_DSN="<FIXME>" rebar3 shell -s my_app_example main

Step 6: View Your Trace

Open the Uptrace dashboard to view your trace. You should see the main-operation span with the child-operation nested inside it.

Basic trace

Environment Variables

You can also configure OpenTelemetry using environment variables, which is useful for containerized deployments:

bash
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>"

For more configuration options, see the OTLP Exporter Configuration guide.

Configuration Options

The following configuration options are available for the OpenTelemetry Erlang/Elixir SDK:

OptionDescription
span_processorSpan processor type: :batch (recommended) or :simple
traces_exporterTraces exporter: :otlp for OTLP, {:otel_exporter_stdout, []} for stdout
otlp_protocolOTLP protocol: :grpc (recommended) or :http_protobuf
otlp_endpointOTLP endpoint URL. For Uptrace: https://api.uptrace.dev:4317 (gRPC) or https://api.uptrace.dev (HTTP)
otlp_compressionCompression: :gzip (recommended) or :none
otlp_headersList of headers to include with each request. Include {"uptrace-dsn", "<YOUR_DSN>"}
resourceMap of resource attributes like service.name and deployment.environment
samplerSampling configuration to control trace collection rate
resource_detectorsList of resource detectors to automatically detect environment info

Application Configuration

Configure OpenTelemetry to start as a temporary application to prevent crashes from affecting your main application:

elixir Elixir
# mix.exs
def project do
  [
    app: :my_app,
    # ... other config
    releases: [
      my_app: [
        applications: [opentelemetry: :temporary]
      ]
    ]
  ]
end
erlang Erlang
%% rebar.config
{relx, [
  {release, {my_app, "0.1.0"}, [
    {opentelemetry, temporary},
    my_app
  ]}
]}.

This ensures that if OpenTelemetry terminates, your main application continues running.

Phoenix Framework Integration

To instrument Phoenix applications, add the Phoenix instrumentation library:

elixir
# mix.exs
defp deps do
  [
    # ... existing deps
    {:opentelemetry_phoenix, "~> 1.1"},
    {:opentelemetry_cowboy, "~> 0.2"},
    {:opentelemetry_ecto, "~> 1.2"}  # For database instrumentation
  ]
end

Configure Phoenix instrumentation in your application start function:

elixir
# lib/my_app/application.ex
def start(_type, _args) do
  # Setup instrumentation 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

This provides automatic instrumentation for HTTP requests, database queries, and Phoenix channels.

Troubleshooting

If traces don't appear in Uptrace:

  1. Verify DSN: Double-check your Uptrace DSN in the configuration
  2. Check connectivity: Ensure your application can reach https://api.uptrace.dev:4317
  3. Review logs: Look for OpenTelemetry error messages in your application logs
  4. Test configuration: Use the stdout exporter for local testing:
elixir
# config/dev.exs
config :opentelemetry,
  traces_exporter: {:otel_exporter_stdout, []}

What's Next?

Instrument more operations to get a detailed picture of your application. Prioritize network calls, database queries, errors, and logs.

By Use Case

I want to...Read this
Configure OTLP exporterOTLP Exporter
Instrument my code with spansTracing API
Collect application metricsMetrics API
Send logs to UptraceLogs integration
Auto-detect cloud environmentResource detectors
Enable distributed tracingContext propagation
Reduce costs in productionSampling strategies

Framework Guides