extatus v0.2.3 Extatus.Process behaviour

This module defines a behaviour to instrument a GenServers.

For an uninstrumented GenServer process, i.e:

defmodule Uninstrumented do
  use GenServer

  def start_link, do: GenServer.start_link(__MODULE__, nil)

  def stop(pid), do: GenServer.stop(pid)
      
  def value(pid), do: GenServer.call(pid, :value)
        
  def inc(pid), do: GenServer.call(pid, :inc)
          
  def dec(pid), do: GenServer.call(pid, :dec)

  def init(_), do: {:ok, 0}

  def handle_call(:value, _from, n), do: {:reply, {:ok, n}, n}
  def handle_call(:inc, _from, n), do: {:reply, :ok, n + 1} 
  def handle_call(:dec, _from, n), do: {:reply, :ok, n - 1}
  def handle_call(_, n), do: {:noreply, n}
end

It is necessary to provide the metric definition and the function definitions for get_name/1 and report/1. get_name/1 is used to generate a name for the process used as a label in Prometheus and the report/1 function is to report the custom metrics to Prometheus. Both functions receive the current GenServer state. So, the instrumentating the previous GenServer, i.e:

defmodule Instrumented do
  use GenServer
  use Extatus.Process # Extatus.Process behaviour

  def start_link, do: GenServer.start_link(__MODULE__, nil)

  def stop(pid), do: GenServer.stop(pid)
  
  def value(pid), do: GenServer.call(pid, :value)
  
  def inc(pid), do: GenServer.call(pid, :inc)
  
  def dec(pid), do: GenServer.call(pid, :dec)

  # Metric
  defmetrics do
    gauge :instrument_gauge do
      label :label
      registry :default
      help "Instrument gauge"
    end
  end

  # Name of the process. This must be unique.
  def get_name(_n), do: {:ok, "instrumented_process"}

  # Report
  def report(n) do
    Gauge.set(:instrument_gauge, [label: "Label"], n)
  end

  def init(_) do
    {:ok, _} = Extatus.set(__MODULE__, self()) # Add extatus handler.
    {:ok, 0}
  end

  def handle_call(:value, _from, n), do: {:reply, {:ok, n}, n}
  def handle_call(:inc, _from, n), do: {:reply, :ok, n + 1} 
  def handle_call(:dec, _from, n), do: {:reply, :ok, n - 1}
  def handle_call(_, n), do: {:noreply, n}
end

This GenServer will report the current value stored in the server as the metric :instrument_gauge to Prometheus.

Additionally, Yggdrasil subscriptions to the channel:

%Yggdrasil.Channel{name: :extatus}

can be used to get the updates on the current state of the process i.e:

iex> chan = %Yggdrasil.Channel{name: :extatus}
iex> Yggdrasil.subscribe(chan)
iex> flush()
{:Y_CONNECTED, (...)}
iex> {:ok, _} = Instrumented.start_link()
{:ok, #PID<0.603.0>}
iex> flush()
{:Y_EVENT, _, %Extatus.Message{name: "instrumented_process", state: :up}}

Summary

Callbacks

Gets the name of the proccess from its state

Gets the metrics from the process state

Callbacks

get_name(state)
get_name(state :: term) :: {:ok, term} | {:error, term}

Gets the name of the proccess from its state.

report(state)
report(state :: term) :: term

Gets the metrics from the process state.