Adding Erlang Alarm Handler

erlang

#1

I’m using a Fuse, an Erlang circuit breaker library, to help manage external API calls. When a fuse is blown, Fuse notifies the SASL alarm handler. I’d like to add a custom alarm handler which does some custom logger on this event.

I"m having trouble writing and swapping in my custom alarm handler. I’m also not very familiar with Erlang. Can anyone see what I’m doing wrong here?

An alarm handler is a gen_event which needs to handle the following events: {:set_alarm, {alarm_id, alarm_desc}} and {:clear_alarm, alarm_id}, plus the event I care about.

defmodule Pipeline.FuseAlarmHandler do

  require Logger

  def init(:ok) do
    {:ok, []}
  end

  def handle_event({:set_alarm, {alarm_id, alarm_desc}}, state) do
    Logger.info("Set #{inspect(alarm_id)}")
    {:ok, state}
  end

  def handle_event({:clear_alarm, alarm_id}, state) do
    Logger.info("Cleared #{inspect(alarm_id)}")
    {:ok, state}
  end

  def handle_event({fuse, :blown}, state) do
    Logger.info("Alarm raised: #{inspect(error_msg)}")
    {:ok, state}
  end

end

iex(2)> :gen_event.swap_handler(:alarm_handler, {:alarm_handler, :swap}, {Pipeline.FuseAlarmHandler, :ok})
{:error,
 {:EXIT,
  {:function_clause,
   [{Pipeline.FuseAlarmHandler, :init, [ok: {:alarm_handler, []}],
     [file: 'lib/pipeline/fuse_alarm_handler.ex', line: 5]},
    {:gen_event, :server_add_handler, 4, [file: 'gen_event.erl', line: 469]},
    {:gen_event, :server_swap_handler, 6, [file: 'gen_event.erl', line: 513]},
    {:gen_event, :handle_msg, 6, [file: 'gen_event.erl', line: 323]},
    {:proc_lib, :init_p_do_apply, 3, [file: 'proc_lib.erl', line: 247]}]}}}

#2

When doing a swap the init/1 callback is called with the argument {NewArgs,Res} where Res is the return value of the terminate/2 callback from the old handler. You can see this in the gen_event docs.

Btw this is the only case I know of where the return value of a terminate callback is actually used for something. Usually it is just thrown away.


#3

Thanks!, Will give this a shot


#4

The only thing to be aware of is that if you intend your event handler to be both added and used as an update then it has to handled both cases of being called with init(Arg) and init({Arg,Res}).