Exporters

Send telemetry to the OpenTelemetry Collector to make sure it’s exported correctly. Using the Collector in production environments is a best practice. To visualize your telemetry, export it to a backend such as Jaeger, Zipkin, Prometheus, or a vendor-specific backend.

Available exporters

The registry contains a list of exporters for Erlang/Elixir.

Among exporters, OpenTelemetry Protocol (OTLP) exporters are designed with the OpenTelemetry data model in mind, emitting OTel data without any loss of information. Furthermore, many tools that operate on telemetry data support OTLP (such as Prometheus, Jaeger, and most vendors), providing you with a high degree of flexibility when you need it. To learn more about OTLP, see OTLP Specification.

This page covers the main OpenTelemetry Erlang/Elixir exporters and how to set them up.

Exporting to the OpenTelemetry Collector

The Collector provides a vendor agnostic way to receive, process and export telemetry data. The package opentelemetry_exporter provides support for both exporting over both HTTP (the default) and gRPC to the collector, which can then export Spans to a self-hosted service like Zipkin or Jaeger, as well as commercial services. For a full list of available exporters, see the registry.

Setting up the Collector

For testing purposes, you can start with the following Collector configuration at the root of your project:

# otel-collector-config.yaml

# OpenTelemetry Collector config that receives OTLP and exports to Jager
receivers:
  otlp:
    protocols:
      grpc:
        endpoint: '0.0.0.0:4317'
      http:
        endpoint: '0.0.0.0:4318'
processors:
  batch:
    send_batch_size: 1024
    timeout: 5s
exporters:
  otlp/jaeger:
    endpoint: jaeger-all-in-one:4317
    tls:
      insecure: true
service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [logging, otlp/jaeger]

For a more detailed example, you can view the config that opentelemetry-erlang uses for testing.

For the purposes of this tutorial, we’ll start the Collector as a docker image along side our app. For this tutorial, we’ll continue along with the Dice Roll example from the Getting Started guide

Add this docker-compose file to the root of your app:

# docker-compose.yml
version: '3'
services:
  otel:
    image: otel/opentelemetry-collector-contrib:0.98.0
    command: ['--config=/conf/otel-collector-config.yaml']
    ports:
      - 4317:4317
      - 4318:4318
    volumes:
      - ./otel-collector-config.yaml:/conf/otel-collector-config.yaml
    links:
      - jaeger-all-in-one

  jaeger-all-in-one:
    image: jaegertracing/all-in-one:latest
    ports:
      - '16686:16686'

This configuration is used in docker-compose.yml to start the Collector with receivers for both HTTP and gRPC that then export to Zipkin also run by docker-compose.

To export to the running Collector the opentelemetry_exporter package must be added to the project’s dependencies:

{deps, [{opentelemetry_api, "~> 1.2"},
        {opentelemetry, "~> 1.3"},
        {opentelemetry_exporter, "~> 1.6"}]}.
def deps do
  [
    {:opentelemetry_api, "~> 1.2"},
    {:opentelemetry, "~> 1.3"},
    {:opentelemetry_exporter, "~> 1.6"}
  ]
end

It should then be added to the configuration of the Release before the SDK Application to ensure the exporter’s dependencies are started before the SDK attempts to initialize and use the exporter.

Example of Release configuration in rebar.config and for mix’s Release task:

%% rebar.config
{relx, [{release, {my_instrumented_release, "0.1.0"},
         [opentelemetry_exporter,
	      {opentelemetry, temporary},
          my_instrumented_app]},

       ...]}.
# mix.exs
def project do
  [
    releases: [
      my_instrumented_release: [
        applications: [opentelemetry_exporter: :permanent, opentelemetry: :temporary]
      ],

      ...
    ]
  ]
end

Finally, the runtime configuration of the opentelemetry and opentelemetry_exporter Applications are set to export to the Collector. The configurations below show the defaults that are used if none are set, which are the HTTP protocol with endpoint of localhost on port 4318. Note:

  • If using grpc for the otlp_protocol the endpoint should be changed to http://localhost:4317.
  • If you’re using the docker compose file from above, you should replace localhost with otel.
%% config/sys.config.src
[
 {opentelemetry,
  [{span_processor, batch},
   {traces_exporter, otlp}]},

 {opentelemetry_exporter,
  [{otlp_protocol, http_protobuf},
   {otlp_endpoint, "http://localhost:4318"}]}]}
].
# config/config.exs
config :opentelemetry,
  resource: %{service: %{name: "roll_dice_app"}},
  span_processor: :batch,
  traces_exporter: :otlp

config :opentelemetry_exporter,
  otlp_protocol: :http_protobuf,
  otlp_endpoint: "http://localhost:4318"
  # otlp_endpoint: "http://otel:4318" if using docker compose file

You can see your traces by running docker compose up in one terminal, then mix phx.server in another. After sending some requests through the app, go to http://localhost:16686 and select roll_dice_app from the Service drop down, then click “Find Traces”.

Gotchas

Some environments do not allow containers to execute as root users. If you work in an environment like this, you can add user: "1001" as a top-level key/value to the otel service in the docker-compose.yml file used in this tutorial.