defmodule Edeliver.StartupProgress do
@moduledoc """
Monitors application start progress.
This module can be added as additional error report handler
using `:error_logger.add_report_handler/1` and
handles progress reports from started applications and
prints that information on the hidden node that called
`Edeliver.monitor_startup_progress/2` as rpc call to
wait until the release started after the asynchronous
`bin/$APP start` command was executed.
"""
@typedoc "State / Options passed to this report handler"
@type t :: term
@spec init(t) :: {:ok, t}
@doc false
def init(state), do: {:ok, state}
@doc """
Handles progress reports from started applications and
prints that info on the hidden node that called
`Edeliver.monitor_startup_progress/2` as rpc call to
wait until the release started after the asynchronous
`bin/$APP start` command was executed
"""
# report from different node
@spec handle_event({type::atom, pid, term}, t) :: {:ok, t}
def handle_event({_type, gl, _message}, state) when node(gl) != node() do
{:ok, state}
end
# progress report
def handle_event({:info_report, _pid, {_, :progress, keywords}}, state) do
case Keyword.get(keywords, :started_at) do
nil -> :ignore
_node ->
case Keyword.get(keywords, :application) do
nil -> :ignore
application -> format_in_rpc_script 'Started application \'~w\'.~n', [application]
end
end
{:ok, state}
end
# no pregress report
def handle_event(_event, state), do: {:ok, state}
@doc false
@spec handle_info(term, t) :: {:ok, t}
def handle_info(_, state), do: {:ok, state}
@doc false
@spec handle_call(term, t) :: {:error, :bad_query}
def handle_call(_query, _state), do: {:error, :bad_query}
@doc false
@spec terminate(atom, t) :: :ok
def terminate(_reason, _state), do: :ok
defp format_in_rpc_script(format, arguments) do
:erlang.nodes(:hidden) |> Enum.filter(fn node ->
Regex.match?(~r/_maint_\d+/, Atom.to_string(node))
end) |> Enum.each(fn node ->
:rpc.cast(node, :io, :format, [:user, format, arguments])
end)
end
end