Monitoring GenServers with PryIn

The latest version of PryIn (v1.5.0) comes with a little helper to monitor your GenServers.

One of the main use cases of GenServers is holding state, and quite often you’ll be interested in how that state behaves over time. If you’re using a GenServer as some sort of queue, for example, you might be interested in the number of enqueued items: Tracking a queue's length on PryIn

PryIn lets you set up multiple values to be tracked per GenServer. For each metric you are interested in, you simply define a callback function that receives the GenServer’s state and returns the metric’s current value.

In the queue example, your function could return length(state.queued_entries) as tracked value.

Examples

Tracking a queue’s length

  defmodule MyQueue do
    use GenServer
    use PryIn.GenServer, values: %{
      "MyQueue queue length" =>  {&MyQueue.queue_length/1, 5_000}
    }

    def queue_length(state) do
      {:ok, length(state.queue_entries), state}
    end
  end

This will send the current queue length to PryIn every 5 seconds under the label “MyQueue queue length”. In case you also want to update the value manually in between, for example each time a new item is added, you can do that by calling PryIn.track_metric:

  PryIn.track_metric("MyQueue queue length", length(state.queue_entries))

Tracking how often a specific handle_call clause is called per period

Maybe you’re interested in how often elements are added to your queue, too:

  defmodule MyQueue do
    use GenServer
    use PryIn.GenServer, values: %{
      "MyQueue enqueues / minute" =>  {&MyQueue.elements_added_count/1, 60_000}
    }

    def init(_) do
      {:ok, %{queue_entries: [], elements_added_count: 0}}
    end

    def handle_call({:add_element, element}, _from, state) do
      {:reply, :ok, %{state |
                      queue_entries: [entry | state.queue_entries],
                      elements_added_count: state.elements_added_count + 1}}
    end

    def elements_added_count(state) do
      {:ok, state.elements_added_count, %{state | elements_added_count: 0}}
    end
  end

Instrumenting a specific handle_info clause

In another GenServer you might be periodically popping those enqueued elements and importing them into a database. To monitor how long that importing takes, wrap the handle_info clause doing that in a Custom Trace:

defmodule MyImporter do
  use GenServer

  ...

  def handle_info(:import) do
    PryIn.CustomTrace.start(group: "MyImporter", key: "import")
    items = MyQueue.pop_all_items()
    MyApp.import_items(items)
    PryIn.CustomTrace.finish()
  end
end

If this is called very often and you think it’s good enough to only include some of the traces, you can define a sample rate when starting the trace:

PryIn.CustomTrace.start(group: "MyImporter", key: "import", sample_rate: 0.01)

With this, only about 1% of those traces will be forwarded to PryIn and count towards the measurements included in your subscription.

TIP: Create a dashboard to hold all metrics you collect for a GenServer. This way you can see everything grouped together and get a nice overview.