Supervisor doesn't log the child exit

The doc of Supervisor says any exit reason other than :normal, :shutdown or {:shutdown, term} will be logged.

But I didn’t see any error log with these code:

defmodule MyWorker do
  def start_link(_) do
    pid = spawn_link(fn ->
      IO.puts "Worker say hello"
      :timer.sleep(1000)
      exit(:goodbye)
    end)

    {:ok, pid}
  end
end

defmodule MySup do
  use Supervisor

  def start_link(_) do
    Supervisor.start_link(__MODULE__, :ok)
  end

  def init(_) do
    [
      %{start: {MyWorker, :start_link, [:ok]}, id: :my_worker}
    ]
    |> Supervisor.init(strategy: :one_for_one)
  end
end

run with MySup.start_link(:ok), it just restart the worker 3 times without any error log. Did I missing something?

After change the children worker into a GenServer, it logs when child terminated:

defmodule MyGenServerWorker do
  use GenServer

  def start_link(_) do
    GenServer.start_link(__MODULE__, :ok)
  end

  def init(_) do
    {:ok, nil, {:continue, :goodbye}}
  end

  def handle_continue(:goodbye, state) do
    IO.puts "GenServer say hello"
    :timer.sleep(1000)
    {:stop, :goodbye, state}
  end
end

log:

19:54:11.273 [error] GenServer #PID<0.1112.0> terminating
** (stop) :goodbye
Last message: {:continue, :goodbye}
State: nil

So the logging only works with the GenServer, not the normal process. I think the document should clarify this.

The docs could probably be clearer about this but yeah Supervisor’s can only supervise OTP compliant processes. Your initial process doesn’t respond to the init message or return the appropriate response, so the supervisor process is actually just locked up.

Thanks. What is the init message? I thought the init is a callback which be called by the GenServer process.

The worker didn’t receive any message in my example.

defmodule MyWorker do
  def start_link(_) do
    pid = spawn_link(fn ->
      IO.puts "Worker say hello"
      :timer.sleep(1000)

      receive do
        m -> IO.inspect m
      end
      exit(:goodbye)
    end)

    {:ok, pid}
  end
end

@Ljzn apologies, I was misremembering how this works. You are correct, the supervisor does not send you a message. However, the supervisor is waiting to receive a message from your process to know that it’s done. Eg: otp/gen_server.erl at master · erlang/otp · GitHub otp/proc_lib.erl at master · erlang/otp · GitHub

If you send the supervisor pid a message of the right shape from your pid then that’d at least get you past the init phase. However your process wouldn’t respond correctly to get_state messages and so forth. At that point you’re just reimplimenting the genserver module and should probably just use a genserver.

1 Like

I think the supervisor can supervise non-OTP processes.

The proc_lib is for blocking init, which is not a requirement for supervisors, but a feature if you need it. I think the supervisor does not use system messages. But in a nutshell you are right: it is probably better to roll with GenServer.

When it comes to logging, it is also done by the behaviours, so we need to update the docs. Will do.

Thank you @Ljzn and @benwilson512 for the discussion.

3 Likes