diff --git a/lib/shoehorn/application.ex b/lib/shoehorn/application.ex index e5d6c57..7cb976d 100644 --- a/lib/shoehorn/application.ex +++ b/lib/shoehorn/application.ex @@ -5,13 +5,13 @@ defmodule Shoehorn.Application do @impl Application def start(_type, _args) do + env = Application.get_all_env(:shoehorn) if using_shoehorn?() do - opts = Application.get_all_env(:shoehorn) - :error_logger.add_report_handler(Shoehorn.ReportHandler, opts) + Shoehorn.ReportHandler.init_handler() end opts = [strategy: :one_for_one, name: Shoehorn.Supervisor] - Supervisor.start_link([], opts) + Supervisor.start_link([{Shoehorn.ReportHandler, env}], opts) end defp using_shoehorn?() do diff --git a/lib/shoehorn/filter.ex b/lib/shoehorn/filter.ex new file mode 100644 index 0000000..6114fd1 --- /dev/null +++ b/lib/shoehorn/filter.ex @@ -0,0 +1,40 @@ +defmodule Shoehorn.Filter do + @moduledoc false + + @doc """ + A filter for handling Application.start/1,2 and Application.stop/1 events. Always return :ignore so that other filters + can continue to handle the events as they please. + """ + def filter(_event = %{msg: msg}, _extra) do + maybe_log_message(msg) + :ignore + end + + def filter(_event, _extra) do + :ignore + end + + def maybe_log_message( + {:report, + %{ + label: {:application_controller, :progress}, + report: [application: app, started_at: _node] + }} + ) do + GenServer.cast(Shoehorn.ReportHandler, {:started, app}) + end + + def maybe_log_message( + {:report, + %{ + label: {:application_controller, :exit}, + report: [application: app, exited: reason, type: _type] + }} + ) do + GenServer.cast(Shoehorn.ReportHandler, {:exit, app, reason}) + end + + def maybe_log_message(_msg) do + :ok + end +end diff --git a/lib/shoehorn/report_handler.ex b/lib/shoehorn/report_handler.ex index e0f4acf..07353ab 100644 --- a/lib/shoehorn/report_handler.ex +++ b/lib/shoehorn/report_handler.ex @@ -1,58 +1,36 @@ defmodule Shoehorn.ReportHandler do @moduledoc false - @behaviour :gen_event - alias Shoehorn.Handler + use GenServer @shutdown_timer 30_000 - @impl :gen_event - def init(opts) do - shutdown_timer = opts[:shutdown_timer] || @shutdown_timer - - {:ok, - %{ - handler: Handler.init(opts), - shutdown_timer: shutdown_timer - }} + def init_handler() do + current_filters = :logger.get_primary_config() |> find_filters() + shoehorn_filters = [ + shoehorn_filter: {&Shoehorn.Filter.filter/2, []} + ] + # put the shoehorn filter to the front of the list to make sure it handles the message first. + :logger.set_primary_config(:filters, shoehorn_filters ++ current_filters) end - @impl :gen_event - def handle_call(_, s) do - {:ok, :ok, s} + def start_link(opts) do + GenServer.start_link(__MODULE__, opts, name: __MODULE__) end - @impl :gen_event - def handle_event({:info_report, _pid, {_, :std_info, info}}, s) when is_list(info) do - case Keyword.get(info, :exited) do - nil -> - {:ok, s} - - reason -> - app = Keyword.get(info, :application) - {:ok, exited(app, reason, s)} - end - end - - def handle_event({:info_report, _pid, {_, :progress, info}}, s) when is_list(info) do - case Keyword.get(info, :started_at) do - nil -> - {:ok, s} - - _node -> - app = Keyword.get(info, :application) - {:ok, started(app, s)} - end + def init(opts) do + shutdown_timer = opts[:shutdown_timer] || @shutdown_timer + state = %{handler: Handler.init(opts), shutdown_timer: shutdown_timer} + {:ok, state} end - def handle_event(_event, s) do - {:ok, s} + def handle_cast({:exit, app, reason}, s) do + {:noreply, exited(app, reason, s)} end - @impl :gen_event - def terminate(_args, _s) do - :ok + def handle_cast({:started, app}, s) do + {:noreply, started(app, s)} end defp exited(app, reason, s) do @@ -75,4 +53,7 @@ defmodule Shoehorn.ReportHandler do defp react({:halt, _}, _), do: :erlang.halt() defp react({:continue, handler}, state), do: %{state | handler: handler} + + defp find_filters(%{filters: filters}), do: filters + defp find_filters(_), do: [] end diff --git a/mix.exs b/mix.exs index 8dc050b..98045a9 100644 --- a/mix.exs +++ b/mix.exs @@ -8,7 +8,7 @@ defmodule Shoehorn.MixProject do [ app: :shoehorn, version: @version, - elixir: "~> 1.10", + elixir: "~> 1.15", start_permanent: Mix.env() == :prod, elixirc_paths: elixirc_paths(Mix.env()), description: description(),