lib/beeline/appsignal.ex

defmodule Beeline.Appsignal do
  @moduledoc """
  an Appsignal exporter for Beeline telemetry

  This exporter works by attaching a telemetry handler. This means that the
  code to set the gauge runs in the process of the HealthChecker.

  Attach this exporter by adding this task to a supervision tree, for example
  the application supervision tree defined in the `lib/my_app/application.ex`
  file:

  ```elixir
  def start(_type, _args) do
    children = [
      {Beeline.Appsignal, []},
      MyApp.MyBeelineTopology
    ]
    opts = [strategy: :one_for_one, name: MyApp.Supervisor]
    Supervisor.start_link(children, opts)
  end
  ```

  This exporter sets an Appsignal gauge measuring the difference between
  the latest available event number and the current position given by the
  Beeline's `:get_stream_position` function option. This gauge is tagged
  with the name of the producer under the key `:module` and the hostname
  on which the producer is running under the key `:hostname`.

  ## Options

  The `start_link/1` function takes a keyword list of options. These can also
  be specified by passing the keyword as the second element of a tuple given
  to a `Supervisor.start_link/2` list of children.

  * `:gauge_name` (string, default: `"event_listener_lag"`) - the gauge name
    to which the delta should be published
  """

  @appsignal Application.get_env(:beeline_appsignal, :appsignal, Appsignal)

  use Task

  @doc false
  def start_link(opts) do
    Task.start_link(__MODULE__, :attach, [opts])
  end

  @doc false
  def attach(opts) do
    :telemetry.attach(
      "beeline-appsignal-exporter",
      [:beeline, :health_check, :stop],
      &__MODULE__.handle_event/4,
      opts
    )
  end

  @doc false
  def handle_event(_event, _measurement, metadata, state) do
    @appsignal.set_gauge(
      state[:gauge_name] || "event_listener_lag",
      metadata[:head_position] - metadata[:current_position],
      %{
        module: inspect(metadata[:producer]),
        hostname: metadata[:hostname]
      }
    )

    state
  end
end