# Instrumentation

> Instrumentation for OpenTelemetry Erlang/Elixir

---


[Instrumentation](/docs/concepts/instrumentation/) is the act of adding
observability code to an app yourself.

If you're instrumenting an app, you need to use the OpenTelemetry SDK for your
language. You'll then use the SDK to initialize OpenTelemetry and the API to
instrument your code. This will emit telemetry from your app, and any library
you installed that also comes with instrumentation.

If you're instrumenting a library, only install the OpenTelemetry API package
for your language. Your library will not emit telemetry on its own. It will only
emit telemetry when it is part of an app that uses the OpenTelemetry SDK. For
more on instrumenting libraries, see
[Libraries](/docs/concepts/instrumentation/libraries/).

For more information about the OpenTelemetry API and SDK, see the
[specification](/docs/specs/otel/).
{{__hugo_ctx/}}


## Setup

Add the following dependencies to your project:

- `opentelemetry_api`: contains the interfaces you'll use to instrument your
  code. Things like `Tracer.with_span` and `Tracer.set_attribute` are defined
  here.
- `opentelemetry`: contains the SDK that implements the interfaces defined in
  the API. Without it, all the functions in the API are no-ops.

```elixir
# mix.exs
def deps do
  [
    {:opentelemetry, "~> 1.3"},
    {:opentelemetry_api, "~> 1.2"},
  ]
end
```

## Traces

### Initialize Tracing

To start [tracing](/docs/concepts/signals/traces/) a
[`TracerProvider`](/docs/concepts/signals/traces/#tracer-provider) is required
for creating a [`Tracer`](/docs/concepts/signals/traces/#tracer). When the
OpenTelemetry SDK Application (`opentelemetry`) boots, it starts and configures
a global `TracerProvider`. A `Tracer` for each loaded OTP Application is created
once the `TracerProvider` has started.

If a TracerProvider is not successfully created (for example, the
`opentelemetry` application is not booted or fails to boot), the OpenTelemetry
APIs for tracing will use a no-op implementation and will not generate data.

### Acquiring a Tracer

Each OTP Application has a `Tracer` created for it when the `opentelemetry`
Application boots. The name and version of each `Tracer` is the same as the name
and version of the OTP Application the module using the `Tracer` is in. If the
call to use a `Tracer` is not in a module, for example when using the
interactive shell, a `Tracer` with a blank name and version is used.

The created `Tracer`'s record can be looked up by the name of a module in the
OTP Application:

   <ul class="nav nav-tabs" id="tabs-1" role="tablist">
  <li class="nav-item">
      <button class="nav-link active"
          id="tabs-01-00-tab" data-bs-toggle="tab" data-bs-target="#tabs-01-00" role="tab"
          data-td-tp-persist="erlang" aria-controls="tabs-01-00" aria-selected="true">
        Erlang
      </button>
    </li><li class="nav-item">
      <button class="nav-link"
          id="tabs-01-01-tab" data-bs-toggle="tab" data-bs-target="#tabs-01-01" role="tab"
          data-td-tp-persist="elixir" aria-controls="tabs-01-01" aria-selected="false">
        Elixir
      </button>
    </li>
</ul>

<div class="tab-content" id="tabs-1-content">
    <div class="tab-body tab-pane fade show active"
        id="tabs-01-00" role="tabpanel" aria-labelled-by="tabs-01-00-tab" tabindex="1">
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="nn">opentelemetry</span><span class="p">:</span><span class="nf">get_application_tracer</span><span class="p">(</span><span class="o">?</span><span class="nv">MODULE</span><span class="p">)</span>
</span></span></code></pre></div>
    </div>
    <div class="tab-body tab-pane fade"
        id="tabs-01-01" role="tabpanel" aria-labelled-by="tabs-01-01-tab" tabindex="1">
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-elixir" data-lang="elixir"><span class="line"><span class="cl"><span class="ss">:opentelemetry</span><span class="o">.</span><span class="n">get_application_tracer</span><span class="p">(</span><span class="n">__MODULE__</span><span class="p">)</span>
</span></span></code></pre></div>
    </div>
</div>


This is how the Erlang and Elixir macros for starting and updating `Spans` get a
`Tracer` automatically without need for you to pass the variable in each call.

### Create Spans

Now that you have [Tracer](/docs/concepts/signals/traces/#tracer)s initialized,
you can create [Spans](/docs/concepts/signals/traces/#spans).

   <ul class="nav nav-tabs" id="tabs-2" role="tablist">
  <li class="nav-item">
      <button class="nav-link active"
          id="tabs-02-00-tab" data-bs-toggle="tab" data-bs-target="#tabs-02-00" role="tab"
          data-td-tp-persist="erlang" aria-controls="tabs-02-00" aria-selected="true">
        Erlang
      </button>
    </li><li class="nav-item">
      <button class="nav-link"
          id="tabs-02-01-tab" data-bs-toggle="tab" data-bs-target="#tabs-02-01" role="tab"
          data-td-tp-persist="elixir" aria-controls="tabs-02-01" aria-selected="false">
        Elixir
      </button>
    </li>
</ul>

<div class="tab-content" id="tabs-2-content">
    <div class="tab-body tab-pane fade show active"
        id="tabs-02-00" role="tabpanel" aria-labelled-by="tabs-02-00-tab" tabindex="2">
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="o">?</span><span class="n">with_span</span><span class="p">(</span><span class="n">main</span><span class="p">,</span> <span class="p">#{},</span> <span class="k">fun</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">                        <span class="c">%% do work here.
</span></span></span><span class="line"><span class="cl">                        <span class="c">%% when this function returns the Span ends
</span></span></span><span class="line"><span class="cl">                      <span class="k">end</span><span class="p">).</span>
</span></span></code></pre></div>
    </div>
    <div class="tab-body tab-pane fade"
        id="tabs-02-01" role="tabpanel" aria-labelled-by="tabs-02-01-tab" tabindex="2">
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-elixir" data-lang="elixir"><span class="line"><span class="cl"><span class="kn">require</span> <span class="nc">OpenTelemetry.Tracer</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">...</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nc">OpenTelemetry.Tracer</span><span class="o">.</span><span class="n">with_span</span> <span class="ss">:main</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># do work here</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># when the block ends the Span ends</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div>
    </div>
</div>


The above code sample shows how to create an active Span, which is the most
common kind of Span to create.

### Create Nested Spans

   <ul class="nav nav-tabs" id="tabs-3" role="tablist">
  <li class="nav-item">
      <button class="nav-link active"
          id="tabs-03-00-tab" data-bs-toggle="tab" data-bs-target="#tabs-03-00" role="tab"
          data-td-tp-persist="erlang" aria-controls="tabs-03-00" aria-selected="true">
        Erlang
      </button>
    </li><li class="nav-item">
      <button class="nav-link"
          id="tabs-03-01-tab" data-bs-toggle="tab" data-bs-target="#tabs-03-01" role="tab"
          data-td-tp-persist="elixir" aria-controls="tabs-03-01" aria-selected="false">
        Elixir
      </button>
    </li>
</ul>

<div class="tab-content" id="tabs-3-content">
    <div class="tab-body tab-pane fade show active"
        id="tabs-03-00" role="tabpanel" aria-labelled-by="tabs-03-00-tab" tabindex="3">
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="nf">parent_function</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="o">?</span><span class="n">with_span</span><span class="p">(</span><span class="n">parent</span><span class="p">,</span> <span class="p">#{},</span> <span class="k">fun</span> <span class="n">child_function</span><span class="o">/</span><span class="mi">0</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">child_function</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="c">%% this is the same process, so the span parent set as the active
</span></span></span><span class="line"><span class="cl">    <span class="c">%% span in the with_span call above will be the active span in this function
</span></span></span><span class="line"><span class="cl">    <span class="o">?</span><span class="n">with_span</span><span class="p">(</span><span class="n">child</span><span class="p">,</span> <span class="p">#{},</span>
</span></span><span class="line"><span class="cl">               <span class="k">fun</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">                   <span class="c">%% do work here. when this function returns, child will complete.
</span></span></span><span class="line"><span class="cl">               <span class="k">end</span><span class="p">).</span>
</span></span></code></pre></div>
    </div>
    <div class="tab-body tab-pane fade"
        id="tabs-03-01" role="tabpanel" aria-labelled-by="tabs-03-01-tab" tabindex="3">
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-elixir" data-lang="elixir"><span class="line"><span class="cl"><span class="kn">require</span> <span class="nc">OpenTelemetry.Tracer</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">def</span> <span class="n">parent_function</span><span class="p">()</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="nc">OpenTelemetry.Tracer</span><span class="o">.</span><span class="n">with_span</span> <span class="ss">:parent</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">        <span class="n">child_function</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">def</span> <span class="n">child_function</span><span class="p">()</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># this is the same process, so the span :parent set as the active</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># span in the with_span call above will be the active span in this function</span>
</span></span><span class="line"><span class="cl">    <span class="nc">OpenTelemetry.Tracer</span><span class="o">.</span><span class="n">with_span</span> <span class="ss">:child</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">        <span class="c1">## do work here. when this function returns, :child will complete.</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div>
    </div>
</div>


### Spans in Separate Processes

The examples in the previous section were Spans with a child-parent relationship
within the same process where the parent is available in the process dictionary
when creating a child Span. Using the process dictionary this way isn't possible
when crossing processes, either by spawning a new process or sending a message
to an existing process. Instead, the context must be manually passed as a
variable.

To pass Spans across processes we need to start a Span that isn't connected to
particular process. This can be done with the macro `start_span`. Unlike
`with_span`, the `start_span` macro does not set the new span as the currently
active span in the context of the process dictionary.

Connecting a span as a parent to a child in a new process can be done by
attaching the context and setting the new span as currently active in the
process. The whole context should be attached in order to not lose other
telemetry data like [baggage](/docs/specs/otel/baggage/api/).

   <ul class="nav nav-tabs" id="tabs-4" role="tablist">
  <li class="nav-item">
      <button class="nav-link active"
          id="tabs-04-00-tab" data-bs-toggle="tab" data-bs-target="#tabs-04-00" role="tab"
          data-td-tp-persist="erlang" aria-controls="tabs-04-00" aria-selected="true">
        Erlang
      </button>
    </li><li class="nav-item">
      <button class="nav-link"
          id="tabs-04-01-tab" data-bs-toggle="tab" data-bs-target="#tabs-04-01" role="tab"
          data-td-tp-persist="elixir" aria-controls="tabs-04-01" aria-selected="false">
        Elixir
      </button>
    </li>
</ul>

<div class="tab-content" id="tabs-4-content">
    <div class="tab-body tab-pane fade show active"
        id="tabs-04-00" role="tabpanel" aria-labelled-by="tabs-04-00-tab" tabindex="4">
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="nv">SpanCtx</span> <span class="o">=</span> <span class="o">?</span><span class="n">start_span</span><span class="p">(</span><span class="n">child</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">Ctx</span> <span class="o">=</span> <span class="nn">otel_ctx</span><span class="p">:</span><span class="nf">get_current</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nn">proc_lib</span><span class="p">:</span><span class="nb">spawn_link</span><span class="p">(</span><span class="k">fun</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">                        <span class="nn">otel_ctx</span><span class="p">:</span><span class="nf">attach</span><span class="p">(</span><span class="nv">Ctx</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                        <span class="o">?</span><span class="n">set_current_span</span><span class="p">(</span><span class="nv">SpanCtx</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">                        <span class="c">%% do work here
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">                        <span class="o">?</span><span class="n">end_span</span><span class="p">(</span><span class="nv">SpanCtx</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                    <span class="k">end</span><span class="p">),</span>
</span></span></code></pre></div>
    </div>
    <div class="tab-body tab-pane fade"
        id="tabs-04-01" role="tabpanel" aria-labelled-by="tabs-04-01-tab" tabindex="4">
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-elixir" data-lang="elixir"><span class="line"><span class="cl"><span class="n">span_ctx</span> <span class="o">=</span> <span class="nc">OpenTelemetry.Tracer</span><span class="o">.</span><span class="n">start_span</span><span class="p">(</span><span class="ss">:child</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">ctx</span> <span class="o">=</span> <span class="nc">OpenTelemetry.Ctx</span><span class="o">.</span><span class="n">get_current</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">task</span> <span class="o">=</span> <span class="nc">Task</span><span class="o">.</span><span class="n">async</span><span class="p">(</span><span class="k">fn</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">                      <span class="nc">OpenTelemetry.Ctx</span><span class="o">.</span><span class="n">attach</span><span class="p">(</span><span class="n">ctx</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                      <span class="nc">OpenTelemetry.Tracer</span><span class="o">.</span><span class="n">set_current_span</span><span class="p">(</span><span class="n">span_ctx</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                      <span class="c1"># do work here</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">                      <span class="c1"># end span here</span>
</span></span><span class="line"><span class="cl">                      <span class="nc">OpenTelemetry.Tracer</span><span class="o">.</span><span class="n">end_span</span><span class="p">(</span><span class="n">span_ctx</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                  <span class="k">end</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">_</span> <span class="o">=</span> <span class="nc">Task</span><span class="o">.</span><span class="n">await</span><span class="p">(</span><span class="n">task</span><span class="p">)</span>
</span></span></code></pre></div>
    </div>
</div>


### Linking the New Span

A [Span](/docs/concepts/signals/traces/#spans) can be created with zero or more
[Span Links](/docs/concepts/signals/traces/#span-links) that causally link it to
another Span. A Span Link needs a Span context to be created.

   <ul class="nav nav-tabs" id="tabs-5" role="tablist">
  <li class="nav-item">
      <button class="nav-link active"
          id="tabs-05-00-tab" data-bs-toggle="tab" data-bs-target="#tabs-05-00" role="tab"
          data-td-tp-persist="erlang" aria-controls="tabs-05-00" aria-selected="true">
        Erlang
      </button>
    </li><li class="nav-item">
      <button class="nav-link"
          id="tabs-05-01-tab" data-bs-toggle="tab" data-bs-target="#tabs-05-01" role="tab"
          data-td-tp-persist="elixir" aria-controls="tabs-05-01" aria-selected="false">
        Elixir
      </button>
    </li>
</ul>

<div class="tab-content" id="tabs-5-content">
    <div class="tab-body tab-pane fade show active"
        id="tabs-05-00" role="tabpanel" aria-labelled-by="tabs-05-00-tab" tabindex="5">
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="nv">Parent</span> <span class="o">=</span> <span class="o">?</span><span class="n">current_span_ctx</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="nn">proc_lib</span><span class="p">:</span><span class="nb">spawn_link</span><span class="p">(</span><span class="k">fun</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">                        <span class="c">%% a new process has a new context so the span created
</span></span></span><span class="line"><span class="cl">                        <span class="c">%% by the following `with_span` will have no parent
</span></span></span><span class="line"><span class="cl">                        <span class="nv">Link</span> <span class="o">=</span> <span class="nn">opentelemetry</span><span class="p">:</span><span class="nb">link</span><span class="p">(</span><span class="nv">Parent</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                        <span class="o">?</span><span class="n">with_span</span><span class="p">(</span><span class="n">&#39;other-process&#39;</span><span class="p">,</span> <span class="p">#{</span><span class="n">links</span> <span class="o">=&gt;</span> <span class="p">[</span><span class="nv">Link</span><span class="p">]},</span>
</span></span><span class="line"><span class="cl">                                   <span class="k">fun</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="n">ok</span> <span class="k">end</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                    <span class="k">end</span><span class="p">),</span>
</span></span></code></pre></div>
    </div>
    <div class="tab-body tab-pane fade"
        id="tabs-05-01" role="tabpanel" aria-labelled-by="tabs-05-01-tab" tabindex="5">
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-elixir" data-lang="elixir"><span class="line"><span class="cl"><span class="n">parent</span> <span class="o">=</span> <span class="nc">OpenTelemetry.Tracer</span><span class="o">.</span><span class="n">current_span_ctx</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">task</span> <span class="o">=</span> <span class="nc">Task</span><span class="o">.</span><span class="n">async</span><span class="p">(</span><span class="k">fn</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">                    <span class="c1"># a new process has a new context so the span created</span>
</span></span><span class="line"><span class="cl">                    <span class="c1"># by the following `with_span` will have no parent</span>
</span></span><span class="line"><span class="cl">                    <span class="n">link</span> <span class="o">=</span> <span class="nc">OpenTelemetry</span><span class="o">.</span><span class="n">link</span><span class="p">(</span><span class="n">parent</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                    <span class="nc">Tracer</span><span class="o">.</span><span class="n">with_span</span> <span class="ss">:&#34;my-task&#34;</span><span class="p">,</span> <span class="p">%{</span><span class="ss">links</span><span class="p">:</span> <span class="p">[</span><span class="n">link</span><span class="p">]}</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">                      <span class="ss">:hello</span>
</span></span><span class="line"><span class="cl">                    <span class="k">end</span>
</span></span><span class="line"><span class="cl">                 <span class="k">end</span><span class="p">)</span>
</span></span></code></pre></div>
    </div>
</div>


### Adding Attributes to a Span

[Attributes](/docs/concepts/signals/traces/#attributes) let you attach key/value
pairs to a Span so it carries more information about the current operation that
it’s tracking.

The following example shows the two ways of setting attributes on a span by both
setting an attribute in the start options and then again with `set_attributes`
in the body of the span operation:

   <ul class="nav nav-tabs" id="tabs-6" role="tablist">
  <li class="nav-item">
      <button class="nav-link active"
          id="tabs-06-00-tab" data-bs-toggle="tab" data-bs-target="#tabs-06-00" role="tab"
          data-td-tp-persist="erlang" aria-controls="tabs-06-00" aria-selected="true">
        Erlang
      </button>
    </li><li class="nav-item">
      <button class="nav-link"
          id="tabs-06-01-tab" data-bs-toggle="tab" data-bs-target="#tabs-06-01" role="tab"
          data-td-tp-persist="elixir" aria-controls="tabs-06-01" aria-selected="false">
        Elixir
      </button>
    </li>
</ul>

<div class="tab-content" id="tabs-6-content">
    <div class="tab-body tab-pane fade show active"
        id="tabs-06-00" role="tabpanel" aria-labelled-by="tabs-06-00-tab" tabindex="6">
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="o">?</span><span class="n">with_span</span><span class="p">(</span><span class="n">my_span</span><span class="p">,</span> <span class="p">#{</span><span class="n">attributes</span> <span class="o">=&gt;</span> <span class="p">[{</span><span class="n">&#39;start-opts-attr&#39;</span><span class="p">,</span> <span class="o">&lt;&lt;</span><span class="s">&#34;start-opts-value&#34;</span><span class="o">&gt;&gt;</span><span class="p">}]},</span>
</span></span><span class="line"><span class="cl">           <span class="k">fun</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">               <span class="o">?</span><span class="n">set_attributes</span><span class="p">([{</span><span class="n">&#39;my-attribute&#39;</span><span class="p">,</span> <span class="o">&lt;&lt;</span><span class="s">&#34;my-value&#34;</span><span class="o">&gt;&gt;</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">                                <span class="p">{</span><span class="n">another_attribute</span><span class="p">,</span> <span class="o">&lt;&lt;</span><span class="s">&#34;value-of-attribute&#34;</span><span class="o">&gt;&gt;</span><span class="p">}])</span>
</span></span><span class="line"><span class="cl">           <span class="k">end</span><span class="p">)</span>
</span></span></code></pre></div>
    </div>
    <div class="tab-body tab-pane fade"
        id="tabs-06-01" role="tabpanel" aria-labelled-by="tabs-06-01-tab" tabindex="6">
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-elixir" data-lang="elixir"><span class="line"><span class="cl"><span class="nc">Tracer</span><span class="o">.</span><span class="n">with_span</span> <span class="ss">:span_1</span><span class="p">,</span> <span class="p">%{</span><span class="ss">attributes</span><span class="p">:</span> <span class="p">[{</span><span class="ss">:&#34;start-opts-attr&#34;</span><span class="p">,</span> <span class="p">&lt;&lt;</span><span class="s2">&#34;start-opts-value&#34;</span><span class="p">&gt;&gt;}]}</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="nc">Tracer</span><span class="o">.</span><span class="n">set_attributes</span><span class="p">([{</span><span class="ss">:&#34;my-attributes&#34;</span><span class="p">,</span> <span class="s2">&#34;my-value&#34;</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">                         <span class="p">{</span><span class="ss">:another_attribute</span><span class="p">,</span> <span class="s2">&#34;value-of-attributes&#34;</span><span class="p">}])</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div>
    </div>
</div>


### Semantic Attributes

Semantic Attributes are attributes that are defined by the [OpenTelemetry
Specification][] in order to provide a shared set of attribute keys across
multiple languages, frameworks, and runtimes for common concepts like HTTP
methods, status codes, user agents, and more. These attribute keys are generated
from the specification and provided in
[opentelemetry_semantic_conventions](https://hex.pm/packages/opentelemetry_semantic_conventions).

For example, an instrumentation for an HTTP client or server would need to
include semantic attributes like the scheme of the URL:

   <ul class="nav nav-tabs" id="tabs-7" role="tablist">
  <li class="nav-item">
      <button class="nav-link active"
          id="tabs-07-00-tab" data-bs-toggle="tab" data-bs-target="#tabs-07-00" role="tab"
          data-td-tp-persist="erlang" aria-controls="tabs-07-00" aria-selected="true">
        Erlang
      </button>
    </li><li class="nav-item">
      <button class="nav-link"
          id="tabs-07-01-tab" data-bs-toggle="tab" data-bs-target="#tabs-07-01" role="tab"
          data-td-tp-persist="elixir" aria-controls="tabs-07-01" aria-selected="false">
        Elixir
      </button>
    </li>
</ul>

<div class="tab-content" id="tabs-7-content">
    <div class="tab-body tab-pane fade show active"
        id="tabs-07-00" role="tabpanel" aria-labelled-by="tabs-07-00-tab" tabindex="7">
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">-</span><span class="ni">include_lib</span><span class="p">(</span><span class="s">&#34;opentelemetry_semantic_conventions/include/trace.hrl&#34;</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">?</span><span class="n">with_span</span><span class="p">(</span><span class="n">my_span</span><span class="p">,</span> <span class="p">#{</span><span class="n">attributes</span> <span class="o">=&gt;</span> <span class="p">[{</span><span class="o">?</span><span class="nv">HTTP_SCHEME</span><span class="p">,</span> <span class="o">&lt;&lt;</span><span class="s">&#34;https&#34;</span><span class="o">&gt;&gt;</span><span class="p">}]},</span>
</span></span><span class="line"><span class="cl">           <span class="k">fun</span><span class="p">()</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">             <span class="p">...</span>
</span></span><span class="line"><span class="cl">           <span class="k">end</span><span class="p">)</span>
</span></span></code></pre></div>
    </div>
    <div class="tab-body tab-pane fade"
        id="tabs-07-01" role="tabpanel" aria-labelled-by="tabs-07-01-tab" tabindex="7">
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-elixir" data-lang="elixir"><span class="line"><span class="cl"><span class="kn">alias</span> <span class="nc">OpenTelemetry.SemanticConventions.Trace</span><span class="p">,</span> <span class="ss">as</span><span class="p">:</span> <span class="nc">Trace</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nc">Tracer</span><span class="o">.</span><span class="n">with_span</span> <span class="ss">:span_1</span><span class="p">,</span> <span class="p">%{</span><span class="ss">attributes</span><span class="p">:</span> <span class="p">[{</span><span class="nc">Trace</span><span class="o">.</span><span class="n">http_scheme</span><span class="p">(),</span> <span class="p">&lt;&lt;</span><span class="s2">&#34;https&#34;</span><span class="p">&gt;&gt;}]}</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div>
    </div>
</div>


### Adding Events

A [Span Event](/docs/concepts/signals/traces/#span-events) is a human-readable
message on an [Span](/docs/concepts/signals/traces/#spans) that represents a
discrete event with no duration that can be tracked by a single timestamp. You
can think of it like a primitive log.

   <ul class="nav nav-tabs" id="tabs-8" role="tablist">
  <li class="nav-item">
      <button class="nav-link active"
          id="tabs-08-00-tab" data-bs-toggle="tab" data-bs-target="#tabs-08-00" role="tab"
          data-td-tp-persist="erlang" aria-controls="tabs-08-00" aria-selected="true">
        Erlang
      </button>
    </li><li class="nav-item">
      <button class="nav-link"
          id="tabs-08-01-tab" data-bs-toggle="tab" data-bs-target="#tabs-08-01" role="tab"
          data-td-tp-persist="elixir" aria-controls="tabs-08-01" aria-selected="false">
        Elixir
      </button>
    </li>
</ul>

<div class="tab-content" id="tabs-8-content">
    <div class="tab-body tab-pane fade show active"
        id="tabs-08-00" role="tabpanel" aria-labelled-by="tabs-08-00-tab" tabindex="8">
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="o">?</span><span class="n">add_event</span><span class="p">(</span><span class="o">&lt;&lt;</span><span class="s">&#34;Gonna try it&#34;</span><span class="o">&gt;&gt;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">%% Do the thing
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">?</span><span class="n">add_event</span><span class="p">(</span><span class="o">&lt;&lt;</span><span class="s">&#34;Did it!&#34;</span><span class="o">&gt;&gt;</span><span class="p">),</span>
</span></span></code></pre></div>
    </div>
    <div class="tab-body tab-pane fade"
        id="tabs-08-01" role="tabpanel" aria-labelled-by="tabs-08-01-tab" tabindex="8">
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-elixir" data-lang="elixir"><span class="line"><span class="cl"><span class="nc">Tracer</span><span class="o">.</span><span class="n">add_event</span><span class="p">(</span><span class="s2">&#34;Gonna try it&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">%%</span> <span class="nc">Do</span> <span class="n">the</span> <span class="n">thing</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nc">Tracer</span><span class="o">.</span><span class="n">add_event</span><span class="p">(</span><span class="s2">&#34;Did it!&#34;</span><span class="p">)</span>
</span></span></code></pre></div>
    </div>
</div>


Events can also have attributes of their own:

   <ul class="nav nav-tabs" id="tabs-9" role="tablist">
  <li class="nav-item">
      <button class="nav-link active"
          id="tabs-09-00-tab" data-bs-toggle="tab" data-bs-target="#tabs-09-00" role="tab"
          data-td-tp-persist="erlang" aria-controls="tabs-09-00" aria-selected="true">
        Erlang
      </button>
    </li><li class="nav-item">
      <button class="nav-link"
          id="tabs-09-01-tab" data-bs-toggle="tab" data-bs-target="#tabs-09-01" role="tab"
          data-td-tp-persist="elixir" aria-controls="tabs-09-01" aria-selected="false">
        Elixir
      </button>
    </li>
</ul>

<div class="tab-content" id="tabs-9-content">
    <div class="tab-body tab-pane fade show active"
        id="tabs-09-00" role="tabpanel" aria-labelled-by="tabs-09-00-tab" tabindex="9">
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="o">?</span><span class="n">add_event</span><span class="p">(</span><span class="o">&lt;&lt;</span><span class="s">&#34;Process exited with reason&#34;</span><span class="o">&gt;&gt;</span><span class="p">,</span> <span class="p">[{</span><span class="n">pid</span><span class="p">,</span> <span class="nv">Pid</span><span class="p">)},</span> <span class="p">{</span><span class="n">reason</span><span class="p">,</span> <span class="nv">Reason</span><span class="p">}]))</span>
</span></span></code></pre></div>
    </div>
    <div class="tab-body tab-pane fade"
        id="tabs-09-01" role="tabpanel" aria-labelled-by="tabs-09-01-tab" tabindex="9">
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-elixir" data-lang="elixir"><span class="line"><span class="cl"><span class="nc">Tracer</span><span class="o">.</span><span class="n">add_event</span><span class="p">(</span><span class="s2">&#34;Process exited with reason&#34;</span><span class="p">,</span> <span class="ss">pid</span><span class="p">:</span> <span class="n">pid</span><span class="p">,</span> <span class="ss">reason</span><span class="p">:</span> <span class="nc">Reason</span><span class="p">)</span>
</span></span></code></pre></div>
    </div>
</div>


### Set Span Status

A [Status](/docs/concepts/signals/traces/#span-status) can be set on a
[Span](/docs/concepts/signals/traces/#spans), typically used to specify that a
Span has not completed successfully - `StatusCode.ERROR`. In rare scenarios, you
could override the Error status with `StatusCode.OK`, but don’t set
`StatusCode.OK` on successfully-completed spans.

The status can be set at any time before the span is finished:

   <ul class="nav nav-tabs" id="tabs-10" role="tablist">
  <li class="nav-item">
      <button class="nav-link active"
          id="tabs-10-00-tab" data-bs-toggle="tab" data-bs-target="#tabs-10-00" role="tab"
          data-td-tp-persist="erlang" aria-controls="tabs-10-00" aria-selected="true">
        Erlang
      </button>
    </li><li class="nav-item">
      <button class="nav-link"
          id="tabs-10-01-tab" data-bs-toggle="tab" data-bs-target="#tabs-10-01" role="tab"
          data-td-tp-persist="elixir" aria-controls="tabs-10-01" aria-selected="false">
        Elixir
      </button>
    </li>
</ul>

<div class="tab-content" id="tabs-10-content">
    <div class="tab-body tab-pane fade show active"
        id="tabs-10-00" role="tabpanel" aria-labelled-by="tabs-10-00-tab" tabindex="10">
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">-</span><span class="ni">include_lib</span><span class="p">(</span><span class="s">&#34;opentelemetry_api/include/opentelemetry.hrl&#34;</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">?</span><span class="n">set_status</span><span class="p">(</span><span class="o">?</span><span class="nv">OTEL_STATUS_ERROR</span><span class="p">,</span> <span class="o">&lt;&lt;</span><span class="s">&#34;this is not ok&#34;</span><span class="o">&gt;&gt;</span><span class="p">)</span>
</span></span></code></pre></div>
    </div>
    <div class="tab-body tab-pane fade"
        id="tabs-10-01" role="tabpanel" aria-labelled-by="tabs-10-01-tab" tabindex="10">
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-elixir" data-lang="elixir"><span class="line"><span class="cl"><span class="nc">Tracer</span><span class="o">.</span><span class="n">set_status</span><span class="p">(</span><span class="ss">:error</span><span class="p">,</span> <span class="s2">&#34;this is not ok&#34;</span><span class="p">)</span>
</span></span></code></pre></div>
    </div>
</div>


## Metrics

To produce metrics the dependencies `opentelemetry_experimental_api` and
`opentelemetry_experimental` must be added to the project. Application
environment configuration for `opentelemetry_experimental` is used to configure
a `MeterProvider` which is initialized when the application starts. Meters are
created with the `MeterProvider` automatically on boot and the appropriate
`Meter` is used to create instruments depending on where in your code your
create the instrument. OpenTelemetry Erlang currently supports the following
instruments:

- Counter, a synchronous instrument that supports non-negative increments
- Asynchronous Counter, an asynchronous instrument which supports non-negative
  increments
- Histogram, a synchronous instrument that supports arbitrary values that are
  statistically meaningful, such as histograms, summaries, or percentile
- Asynchronous Gauge, an asynchronous instrument that supports non-additive
  values, such as room temperature
- UpDownCounter, a synchronous instrument that supports increments and
  decrements, such as the number of active requests
- Asynchronous UpDownCounter, an asynchronous instrument that supports
  increments and decrements

For more on synchronous and asynchronous instruments, and which kind is best
suited for your use case, see
[Supplementary Guidelines](/docs/specs/otel/metrics/supplementary-guidelines/).

### Initialize Metrics

> [!NB] If you’re instrumenting a library, **skip this step**.

To enable metrics in your application, you’ll need to have an initialized
`MeterProvider` with a `Reader`. This is done through configuration of the
`opentelemetry_experimental` application:

```erlang
{opentelemetry_experimental,
  [{readers, [#{module => otel_metric_reader,
                config => #{export_interval_ms => 1000,
                            exporter => {otel_exporter_metrics_otlp, #{}}}}]}]},
```

This configuration tells the application to create a `MetricProvider` with a
single `Reader`. The `Reader` exports every second to an OTLP receiver, like the
collector, at `localhost:4318` by default. To change the endpoint add to the map
`endpoints => ["<host>:<port>"]` and configure the protocol to use
`protocol => http_protobuf | grpc`.

Use `exporter => {otel_exporter_metrics_console, #{}}` for outputting the
metrics to the console.

### Acquiring a Meter

Instruments are created with a `Meter`. Acquiring a `Meter` manually is not
required but done automatically when the macros for instrument creation are
used.

### Synchronous and asynchronous instruments

### Using Counters

Counters can be used to measure a non-negative, increasing value.

Creating a counter can be done with the `?create_counter` macro:

```erlang
?create_counter(my_fun_counter, #{description => ~"Number of times this function
is called."})
```

To increment the counter use the `?counter_add` macro passing the name of the
instrument, the increment value and a map of attributes:

```erlang
?counter_add(my_fun_counter, 1, #{}),
```

### Using UpDown Counters

UpDown counters can increment and decrement, allowing you to observe a
cumulative value that goes up or down.

For example, here’s how you report the number of items of some collection:

```erlang
create_items_counter() ->
  ?create_counter('items.counter', #{description => ~"Number of items",
                                     unit => '{items}'})

add_item(Item) ->
  ...
  ?updown_counter_add('items.counter', 1),

remove_item(Item) ->
  ...
  ?updown_counter_add('items.counter', -1),
```

### Using Histograms

Histograms are used to measure a distribution of values over time.

```erlang
?create_histogram('task.duration', #{description => ~"Duration of a task",
                                     unit => 's'}),
```

The `?histogram_record` macro is then used to record a measurement:

```erlang
{Microseconds, Result} = timer:tc(TaskFun),
?histogram_record('task.duration', Microseconds),
```

### Using Observable Counters

Observable counters can be used to measure an additive, non-negative,
monotonically increasing value.

For example, here’s how you report time since the Erlang node started:

```erlang
?create_observable_counter('uptime', fun(_Args) ->
                                         Uptime = erlang:convert_time_unit(erlang:monotonic_time() - erlang:system_info(start_time), native, seconds),
                                         [{Uptime, #{}}]
                                     end,
                                     [],
                                     #{description => ~"The duration since the node started.",
                                       unit => 's'}),
```

### Using Observable UpDown Counters

Observable UpDown counters can increment and decrement, allowing you to measure
an additive, non-negative, non-monotonically increasing cumulative value.

For example, the number of active HTTP connections for a web server:

```erlang
?create_observable_updown_counter('http.server.active_requests', fun(_Args) ->
                                         ActiveRequests = ....
                                         [{ActiveRequests, #{}}]
                                     end,
                                     [],
                                     #{description => ~"Number of active HTTP server requests.",
                                       unit => {request}'}),
```

### Using Observable Gauges

Observable Gauges should be used to measure non-additive values.

For example, here’s how you report memory usage of ETS tables on a node:

```erlang
?create_observable_gauge('memory.ets', fun(_Args) ->
                                         EtsMemory = erlang:memory(ets),
                                         [{EtsMemory, #{}}]
                                     end,
                                     [],
                                     #{description => ~"Memory used by ETS tables.",
                                       unit => 'By'}),
```

### Adding Attributes

Attributes can be added to any measurement as a map in the last place in the
recording macro:

```erlang
?updown_counter_add('items.counter', 1, #{~"key-1" => ~"value-1"}),
```

### Registering Views

A view provides SDK users with the flexibility to customize the metrics output
by the SDK. You can customize which metric instruments are to be processed or
ignored. You can also customize aggregation and what attributes you want to
report on metrics.

Every instrument has a default view, which retains the original name,
description, and attributes, and has a default aggregation that is based on the
type of instrument. When a registered view matches an instrument, the default
view is replaced by the registered view. Additional registered views that match
the instrument are additive, and result in multiple exported metrics for the
instrument.

Here’s how you create a view that renames the `latency` instrument to
`request.latency`:

```erlang
{opentelemetry_experimental,
  [...
    {views, [#{name => request.latency',
               selector => #{instrument_name => 'latency'}}]}
  ]},
```

Or if instead you want a histogram for latency:

```erlang
{opentelemetry_experimental,
  [...
    {views, [#{selector => #{instrument_name => 'latency'},
               aggregation_module => otel_aggregation_histogram_explicit}]}
  ]},
```

The SDK filters metrics and attributes before exporting metrics. For example,
you can use views to reduce memory usage of high cardinality metrics or drop
attributes that might contain sensitive data.

Here’s how you create a view that drops the latency:

```erlang
{opentelemetry_experimental,
  [...
    {views, [#{selector => #{instrument_name => 'latency'},
               aggregation_module => otel_aggregation_drop}]}
  ]},
```

A wildcard can be used to match all instruments:

```erlang
{opentelemetry_experimental,
  [...
    {views, [#{selector => #{instrument_name => '*'},
               aggregation_module => otel_aggregation_drop}]}
  ]},
```

Since Views are additive any additional views mean specific metrics can be
exported while all others, that have no match besides the wildcard, are dropped.

## Logs

The logs API, found in `apps/opentelemetry_experimental_api` of the
[opentelemetry-erlang](https://github.com/open-telemetry/opentelemetry-erlang)
repository, is currently unstable, documentation TBA.

## Next Steps

You’ll also want to configure an appropriate exporter to
[export your telemetry data](/docs/languages/erlang/exporters) to one or more
telemetry backends.

[opentelemetry specification]: /docs/specs/otel/
