Home / Guides / Telemetry

Telemetry

Emissions emits telemetry events at key points throughout the event lifecycle. Attach handlers to these events to build dashboards, alerts, and observability into your application.

Attaching Handlers

defmodule MyApp.EmissionsTelemetry do
  def setup do
    :telemetry.attach_many(
      "emissions-metrics",
      [
        [:emissions, :buffer, :started],
        [:emissions, :buffer, :committed],
        [:emissions, :event, :dropped],
        [:emissions, :adapter, :success],
        [:emissions, :adapter, :error],
        [:emissions, :handler, :success],
        [:emissions, :handler, :error],
        [:emissions, :handler, :ignored]
      ],
      &handle_event/4,
      nil
    )
  end

  defp handle_event(event, measurements, metadata, _config) do
    # Report to your metrics system
    MyApp.Metrics.increment(event, measurements, metadata)
  end
end

Buffer Events

[:emissions, :buffer, :started]

A new per-request buffer was created.

Field Value
Measurements %{count: 1}
Metadata %{}

[:emissions, :buffer, :committed]

A buffer was committed and events were handed to the pipeline.

Field Value
Measurements %{count: pos_integer()}
Metadata %{event_count: pos_integer()}

Event Events

[:emissions, :event, :dropped]

An event was dropped because no buffer was active.

Field Value
Measurements %{count: 1}
Metadata %{event_name: atom(), reason: :not_started}

Adapter Events

[:emissions, :adapter, :success]

An adapter successfully processed a batch of events.

Field Value
Measurements %{duration: integer()}
Metadata %{adapter: module(), event_count: pos_integer()}

Duration is in native time units (see System.convert_time_unit/3).

[:emissions, :adapter, :error]

An adapter failed to process a batch of events.

Field Value
Measurements %{duration: integer()}
Metadata %{adapter: module(), reason: term(), event_count: pos_integer()}

Handler Events

[:emissions, :handler, :success]

A handler successfully processed an event.

Field Value
Measurements %{duration: integer()}
Metadata %{handler: module(), event: atom()}

[:emissions, :handler, :error]

A handler returned {:error, reason} for an event.

Field Value
Measurements %{duration: integer()}
Metadata %{handler: module(), event: atom(), reason: term()}

[:emissions, :handler, :ignored]

A handler returned :ignored for an event.

Field Value
Measurements %{count: 1}
Metadata %{handler: module(), event: atom()}

Example: StatsD Integration

defmodule MyApp.EmissionsMetrics do
  def setup do
    events = [
      [:emissions, :buffer, :committed],
      [:emissions, :adapter, :success],
      [:emissions, :adapter, :error],
      [:emissions, :handler, :success],
      [:emissions, :handler, :error]
    ]

    :telemetry.attach_many("emissions-statsd", events, &handle/4, nil)
  end

  defp handle([:emissions, :buffer, :committed], %{count: count}, _meta, _) do
    Statix.histogram("emissions.buffer.committed", count)
  end

  defp handle([:emissions, :adapter, :success], %{duration: d}, %{adapter: mod}, _) do
    Statix.timing("emissions.adapter.duration", native_to_ms(d), tags: ["adapter:#{mod}"])
    Statix.increment("emissions.adapter.success", tags: ["adapter:#{mod}"])
  end

  defp handle([:emissions, :adapter, :error], %{duration: d}, %{adapter: mod}, _) do
    Statix.timing("emissions.adapter.duration", native_to_ms(d), tags: ["adapter:#{mod}"])
    Statix.increment("emissions.adapter.error", tags: ["adapter:#{mod}"])
  end

  defp handle([:emissions, :handler, :success], %{duration: d}, %{handler: h, event: e}, _) do
    Statix.timing("emissions.handler.duration", native_to_ms(d),
      tags: ["handler:#{h}", "event:#{e}"]
    )
  end

  defp handle([:emissions, :handler, :error], _m, %{handler: h, event: e}, _) do
    Statix.increment("emissions.handler.error", tags: ["handler:#{h}", "event:#{e}"])
  end

  defp native_to_ms(native) do
    System.convert_time_unit(native, :native, :millisecond)
  end
end