# OpenTelemetry Setup (/sdks/elixir/how-to/observability/opentelemetry-setup)



## Overview [#overview]

**OpenTelemetry integration** wires the client's messaging operations into your tracing and metrics pipeline without hand-instrumenting every call site. In a distributed system where a message might be published by one service, queued, and consumed by three others, per-call logging tells you almost nothing — you need spans that correlate across process boundaries and latency/error metrics broken out by operation. The Elixir SDK takes the idiomatic BEAM approach: instead of an OTel-specific API, it emits standard [`:telemetry`](https://hexdocs.pm/telemetry) events, the same mechanism every well-behaved Elixir library uses for instrumentation.

`:telemetry.attach_many/4` registers a handler across multiple event names, each following the pattern `[:kubemq, :client, action, phase]` (e.g., `[:kubemq, :client, :send_event, :stop]`), with `phase` being `:start`, `:stop`, or `:exception`. Handlers receive `measurements` (duration and similar numeric data) and `metadata` (operation, channel, client ID). To get this into OpenTelemetry specifically, bridge these events with the `opentelemetry_telemetry` library rather than consuming them directly. &#x2A;*Gotchas:** a slow or raising telemetry handler runs inline on the calling process and can measurably impact latency or crash the caller — keep handlers fast and defensive; `attach_many` silently no-ops if the event name list doesn't exactly match what the SDK emits (typos fail quietly, not loudly); and this raw `:telemetry` approach only gets you logging by default — actual OTel export requires the separate bridge library, not just the code shown here.

## Prerequisites [#prerequisites]

* KubeMQ server running on `localhost:50000`
* Elixir SDK installed (`{:kubemq, "~> 1.0"}` in mix.exs)

## Code [#code]

```elixir title="main.exs"
:telemetry.attach_many(
  "kubemq-logger",
  [
    [:kubemq, :client, :ping, :stop],
    [:kubemq, :client, :send_event, :start],
    [:kubemq, :client, :send_event, :stop],
    [:kubemq, :client, :send_event, :exception],
    [:kubemq, :client, :send_command, :stop],
    [:kubemq, :client, :send_query, :stop],
    [:kubemq, :client, :send_queue_message, :stop],
    [:kubemq, :client, :poll_queue, :stop]
  ],
  fn event, measurements, metadata, _config ->
    [_, _, action, phase] = event

    IO.puts("[Telemetry] #{action}:#{phase}")
    IO.puts("  measurements: #{inspect(measurements)}")
    IO.puts("  metadata: #{inspect(Map.take(metadata, [:operation, :channel, :client_id]))}")
  end,
  nil
)

IO.puts("Telemetry handlers attached")

{:ok, client} = KubeMQ.Client.start_link(address: "localhost:50000", client_id: "elixir-telemetry")

IO.puts("\n--- Ping ---")
KubeMQ.Client.ping(client)

IO.puts("\n--- Send Event ---")
KubeMQ.Client.send_event(client,
  KubeMQ.Event.new(channel: "elixir-observability.opentelemetry-setup", body: "traced event"))

Process.sleep(500)
IO.puts("\nTelemetry events logged above.")

KubeMQ.Client.close(client)
```

## How It Works [#how-it-works]

* `:telemetry.attach_many/4` registers a handler for multiple telemetry events
* Event names follow the pattern `[:kubemq, :client, :action, :phase]`
* Phases include `:start`, `:stop`, and `:exception`
* Measurements contain duration and metadata contains operation context
* This integrates with OpenTelemetry via the `opentelemetry_telemetry` bridge

## Related [#related]

* [Elixir SDK Reference](/sdks/elixir/reference)
* [Elixir SDK Getting Started](/sdks/elixir)
