defmodule StepFlow.Repo.SupervisionTree do
@moduledoc """
Dump the application supervision tree into the application logs.
Useful for debugging purpose.
"""
use GenServer
require Logger
@interval 10_000
def child_spec(_) do
%{
id: StepFlow.Repo.SupervisionTree,
start: {StepFlow.Repo.SupervisionTree, :start_link, []},
type: :worker
}
end
def start_link do
GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
@impl true
def init(_) do
Logger.warn("[#{__MODULE__}] Start printing supervision tree!")
tree =
"\n" <>
print_supervision_tree(StepFlow.Repo) <>
print_supervision_tree(StepFlow.ProcessManager) <>
print_supervision_tree(StepFlow.Amqp.Supervisor)
Logger.info("[#{__MODULE__}] Supervision tree:\n#{tree}")
Process.send_after(self(), :print, @interval)
{:ok, %{}}
end
@impl true
def handle_info(:print, _) do
tree =
"\n" <>
print_supervision_tree(StepFlow.Repo) <>
print_supervision_tree(StepFlow.ProcessManager) <>
print_supervision_tree(StepFlow.Amqp.Supervisor)
Logger.info("[#{__MODULE__}] Supervision tree:\n#{tree}")
Process.send_after(self(), :print, @interval)
{:noreply, %{}}
end
defp print_supervision_tree(supervisor) do
case Process.whereis(supervisor) do
nil ->
"\t==== Supervision tree from #{inspect(supervisor)}: not running ====\n\n"
process_manager_pid ->
"\t==== Supervision tree from: #{inspect(supervisor)} (#{inspect(process_manager_pid)}) ====\n" <>
if Process.alive?(process_manager_pid) do
subtree =
Supervisor.which_children(supervisor)
|> print_children()
|> Enum.join("\n")
subtree <> "\n\n"
else
"\t#{inspect(supervisor)}: is dead (#{inspect(process_manager_pid)})\n\n"
end
end
end
defp print_children(_children, _tab \\ 1, _result \\ [])
defp print_children([], _tab, result), do: result
defp print_children([child | children], tab, result) do
{name, pid, type, _} = child
pid_str =
case pid do
:restarting ->
":restarting"
:undefined ->
":undefined"
pid ->
status =
if Process.alive?(pid) do
"alive"
else
"dead"
end
"#{inspect(pid)} (#{status})"
end
result =
result ++
[
String.duplicate("\t", tab) <>
" |- #{String.pad_trailing("#{name}", 50, " ")}\t #{pid_str}\t #{inspect(type)}"
]
result =
result ++
if type == :supervisor and pid != :restarting and pid != :undefined and
Process.alive?(pid) do
Supervisor.which_children(name)
|> print_children(tab + 1)
else
[]
end
print_children(children, tab, result)
end
end