Telemetry & tracing

Copy Markdown

ChannelClient emits :telemetry events for connections, messages and channel operations. Attach handlers to build metrics, dashboards or traces — no configuration is needed inside the library, and nothing is emitted to payload-privacy-sensitive detail: event metadata contains only topics, event names, refs and byte sizes.

Event catalog

EventMeasurementsMetadata
[:channel_client, :connection, :start]%{system_time}%{transport, url}
[:channel_client, :connection, :stop]%{duration}%{transport, url, result} + reason on error
[:channel_client, :connection, :disconnected]%{}%{reason}
[:channel_client, :message, :out]%{payload_bytes}%{topic, event, ref}
[:channel_client, :message, :in]%{duration, payload_bytes}%{topic, event, ref, join_ref}
[:channel_client, :channel_join, :start]%{system_time}%{topic}
[:channel_client, :channel_join, :stop]%{duration}%{topic, result} + reason on error
[:channel_client, :push, :start]%{system_time}%{event}
[:channel_client, :push, :stop]%{duration}%{event, result} + reason on error
[:channel_client, :push, :exception]%{duration}%{event, kind, reason, stacktrace}

Notes:

  • duration values are in native time units — convert with System.convert_time_unit/3.
  • The start / stop / exception triples follow the standard telemetry span conventions, so APM tools can render them as spans.
  • On stop events, result is :ok or :error, and reason carries the bare error reason (for example reason: :timeout or the server's error response).
  • :push spans wrap synchronous ChannelClient.Channel.push/4 calls. Server-side error replies and timeouts close the span with result: :error; an :exception event is emitted only when the call itself crashes (for example pushing to a dead channel process).
  • Heartbeats appear as regular [:channel_client, :message, :out] events with topic "phoenix". After a reconnect, automatic rejoins show up as phx_join frames in the same stream.
  • Frames dropped by plugs or encoding failures never emit :message, :out.

Subscribing

# In your application start:
:ok = :telemetry.attach_many(
  "my-app-channel-client",
  ChannelClient.Telemetry.events(),
  &MyApp.Telemetry.handle_event/4,
  nil
)
defmodule MyApp.Telemetry do
  require Logger

  def handle_event([:channel_client, :connection, :stop], %{duration: duration}, meta, _) do
    Logger.info("channel_client connect #{meta.result} (#{us(duration)}s)")
  end

  def handle_event([:channel_client, :push, :stop], %{duration: duration}, meta, _) do
    Logger.debug("push #{meta.event} -> #{meta.result} in #{us(duration)}s")
  end

  def handle_event(_name, _measurements, _meta, _config), do: :ok

  defp us(native), do: System.convert_time_unit(native, :native, :microsecond)
end

Building traces

Because every operation follows the span triple shape, tracing backends can correlate events directly:

handle_event([:channel_client, :channel_join, :start], m, meta, config) do
  # open a span keyed by {self(), meta.topic}
end

handle_event([:channel_client, :channel_join, :stop], %{duration: d}, meta, config) do
  # close the span, record d as its latency, mark red/green by meta.result
end

handle_event([:channel_client, :push, :exception], %{duration: d}, meta, config) do
  # rare: only when the call itself crashed; meta.kind / meta.reason say how
end

OpenTelemetry users can convert these pairs into OTel spans in a handler, or use a converter library that speaks the same convention.

Correlating messages across services

To propagate trace context through channel payloads, write an outbound plug that stamps context into the payload and mirror it on your server:

outbound_plugs: [
  fn msg, _opts ->
    {:cont, %{msg | payload: Map.put(msg.payload || %{}, "_trace_id", get_trace_id())}}
  end
]

Keep such conventions explicit between client and server; ChannelClient never mutates payloads itself.

Phoenix LiveDashboard

LiveDashboard's metrics page can chart these events out of the box, e.g. queue a counter for [:channel_client, :message, :in] and a summary for [:channel_client, :push, :stop] durations.