CodeNameRaven.Handler behaviour (raven_observer_sdk v0.6.0)

Copy Markdown

The behaviour contract for a Raven alert handler.

A handler watches the DataBus for monitor status events and decides when to fire. When it fires, the platform calls every channel in the handler's config. Handlers run as supervised GenServer processes — one process per configured handler instance — giving them persistent state across events.

This is the last piece of the alerting stack:

Monitor  DataBus  Handler  Channel

Monitors publish health signals. Handlers react to those signals. Channels deliver the notification.

Implementing a handler

defmodule MyHandlers.OnDown do
  use CodeNameRaven.Handler

  @impl true
  def handler_init(_params), do: {:ok, %{}}

  @impl true
  def on_status(%{status: :down} = msg, _params, state) do
    notification = %Notification{
      monitor_id:      msg.monitor_id,
      status:          :down,
      previous_status: nil,
      occurred_at:     msg.checked_at
    }
    {:fire, notification, state}
  end

  def on_status(_msg, _params, state), do: {:ok, state}
end

on_status/3 is the only required callback — everything else has a working default.

Return values from on_status/3

  • {:ok, new_state} — no alert; update state and wait for the next event.
  • {:fire, notification, new_state} — deliver notification to every channel in this handler's config, then update state.

The status argument

on_status/3's first argument is loosely typed (term()) rather than a concrete struct — the real message module (and its version/upcast machinery) lives in Raven itself, not this SDK, matching CodeNameRaven.Probe.on_metric/3's precedent for the identical problem. It has (at least) these fields: monitor_id (string), status (:up | :degraded | :down | :unknown), checked_at (DateTime.t()), node (the reporting node, or nil/:nonode@nohost for a local-only signal). Raven upcasts the message to its current version before this callback ever sees it, so implementations never need to think about version compatibility themselves — pattern-match on the fields you need with a plain map pattern (it matches the real struct too, since structs are maps) rather than a named struct.

State

handler_state is opaque to the platform. Use it to track whatever the handler needs between events: the last known status per monitor, cooldown timers, deduplication keys. The state is ephemeral — a handler restart resets it via handler_init/1.

Summary

Callbacks

Returns the category this handler belongs to.

Returns the human-readable name for this handler type.

Initialises the handler's state.

Reacts to a monitor status event.

Called periodically to perform timed tasks, such as checking for stale monitors.

Returns the NimbleOptions-style schema for validating/rendering this handler's params — the type-aware form the admin UI renders (checkbox for booleans, dropdown for a fixed set of choices) instead of a generic text box. Defaults to [] (no schema, fully backward compatible with every handler predating this callback — the UI falls back to params_template/0 in that case).

Returns a template map of params this handler expects.

Functions

Returns true if the given module implements the Handler behaviour.

Callbacks

category()

@callback category() :: atom()

Returns the category this handler belongs to.

display_name()

@callback display_name() :: String.t()

Returns the human-readable name for this handler type.

handler_init(params)

@callback handler_init(params :: map()) :: {:ok, term()} | {:error, String.t()}

Initialises the handler's state.

Called once when the handler process starts. Returns {:ok, initial_state} on success or {:error, reason} if the handler cannot start.

The default implementation returns {:ok, %{}}.

on_status(status, params, state)

@callback on_status(
  status :: term(),
  params :: map(),
  state :: term()
) :: {:ok, term()} | {:fire, CodeNameRaven.Channel.Notification.t(), term()}

Reacts to a monitor status event.

Called for every status event published to the DataBus — see "The status argument" above for its shape. Return {:ok, new_state} to update state without firing, or {:fire, notification, new_state} to trigger channel delivery.

The default implementation ignores all events and returns {:ok, state}.

on_tick(params, state)

(optional)
@callback on_tick(
  params :: map(),
  state :: term()
) ::
  {:ok, term()}
  | {:fire,
     CodeNameRaven.Channel.Notification.t()
     | [CodeNameRaven.Channel.Notification.t()], term()}

Called periodically to perform timed tasks, such as checking for stale monitors.

params_schema()

@callback params_schema() :: keyword()

Returns the NimbleOptions-style schema for validating/rendering this handler's params — the type-aware form the admin UI renders (checkbox for booleans, dropdown for a fixed set of choices) instead of a generic text box. Defaults to [] (no schema, fully backward compatible with every handler predating this callback — the UI falls back to params_template/0 in that case).

params_template()

@callback params_template() :: map()

Returns a template map of params this handler expects.

Used by the admin UI as a fallback to derive a configuration form when params_schema/0 is empty.

Functions

handler_module?(module)

@spec handler_module?(module()) :: boolean()

Returns true if the given module implements the Handler behaviour.