Packages

Behaviour and use macro for Raven monitor integrations

Current section

Files

Jump to
raven_observer_sdk lib code_name_raven monitor checkin.ex
Raw

lib/code_name_raven/monitor/checkin.ex

defmodule CodeNameRaven.Monitor.Checkin do
@moduledoc """
Optional mixin for a Monitor that reacts to external check-in pings —
Raven's `/checkin/:checkin_id` HTTP endpoint — in addition to (not
instead of) its own periodic `collect/2`.
This is a narrower, different shape than `CodeNameRaven.Probe`: Probe
fully replaces `collect/2` with a reactive stub, for a monitor whose only
purpose is watching another monitor's events. A check-in monitor still
runs a real, meaningful `collect/2` on every scheduled interval (typically
a staleness check against its own last-check-in timestamp) — it just also
needs an event-driven hook to update that timestamp when a ping arrives.
Layer this alongside a normal `use CodeNameRaven.Monitor`, not in place of
it.
Like `CodeNameRaven.Probe`, this module is implementation-free: it defines
only the callback contract. Raven itself subscribes a check-in-shaped
monitor to check-in events on its behalf and calls `on_checkin/3`
directly — the module never subscribes to anything itself and has no
dependency on any Raven-internal module.
## Using this behaviour
defmodule Integrations.MyDeadMansSwitch do
use CodeNameRaven.Monitor
use CodeNameRaven.Monitor.Checkin
@impl true
def on_checkin(%{checkin_id: incoming_id}, params, state) do
if incoming_id == params[:checkin_id] do
{:ok, %{state | last_checkin_at: DateTime.utc_now()}, :check_now}
else
{:ok, state}
end
end
@impl true
def collect(params, state) do
# staleness check against state.last_checkin_at
end
@impl true
def healthy?(result), do: # :up / :degraded / :down based on elapsed time
end
## Event shape
`on_checkin/3`'s first argument is loosely typed (`term()`) rather than a
concrete struct, matching `CodeNameRaven.Probe`'s `on_metric/3`/
`on_status/3` precedent. It has (at least) a `checkin_id` field (string —
the id from the check-in URL) and a `checkin_at` field (`DateTime.t()`).
"""
@doc """
Handles an arriving check-in event.
Called directly by Raven when a check-in event arrives, for any monitor
Raven has recognized as check-in-shaped. Return `{:ok, new_state}` to
update state, or `{:ok, new_state, :check_now}` to also trigger an
immediate check cycle — the common case, since a fresh check-in usually
means "re-evaluate health right now" rather than waiting out the rest of
the current interval.
The default ignores all check-ins and returns the state unchanged.
"""
@callback on_checkin(checkin :: term(), params :: map(), state :: term()) ::
{:ok, term()} | {:ok, term(), :check_now}
@doc """
Returns true if the given module implements this mixin.
Checks for `on_checkin/3`, the one callback it adds.
"""
@spec checkin_module?(module()) :: boolean()
def checkin_module?(module) do
Code.ensure_loaded?(module) and function_exported?(module, :on_checkin, 3)
end
@doc false
defmacro __using__(_opts \\ []) do
quote do
@behaviour CodeNameRaven.Monitor.Checkin
@impl CodeNameRaven.Monitor.Checkin
def on_checkin(_checkin, _params, state), do: {:ok, state}
defoverridable on_checkin: 3
end
end
end