Give background processes useful labels

almirsarajcic

almirsarajcic

1 hour ago

0 comments

PIDs are not much help when several copies of the same worker are running. Process.set_label/1 adds a term that identifies what each process is doing.

defmodule MyApp.Importer do
  use GenServer

  def start_link(opts) do
    GenServer.start_link(__MODULE__, opts)
  end

  @impl GenServer
  def init(opts) do
    path = Keyword.fetch!(opts, :path)
    Process.set_label({:importer, path})

    {:ok, %{path: path}}
  end
end

Labels can be any term and do not have to be unique. You can use the same label for a group of workers or include a value, such as the path of the file being imported, to tell them apart.

Observer includes the label in its process list, and crash reports include it too. Seeing {:importer, "orders_2026_08.csv"} in a report is usually more useful than seeing a PID alone.

This is separate from process registration. A registered name is used to look up a process and must be unique within its registry. A label is only metadata, so it is safe to use values that are created at runtime.

# Include enough context to identify the work.
Process.set_label(:cache_warmer)
Process.set_label({:session, user.id})
Process.set_label({:webhook_retry, event_id, attempt})

GenServers are not special here. A supervised task can set its own label in the same way:

Task.Supervisor.start_child(MyApp.TaskSupervisor, fn ->
  Process.set_label({:thumbnail, upload.id})

  generate_thumbnail(upload)
end)

Process.set_label/1 is available in Elixir 1.17 and later. Process.get_label/1, which reads another process’s label, was added in Elixir 1.20. With earlier Elixir versions on OTP 27 or later, use :proc_lib.get_label/1.

Process.set_label/1 docs

Comments (0)

Sign in with GitHub to join the discussion