diff --git a/lib/zexbox/metrics/context.ex b/lib/zexbox/metrics/context.ex index b44c054..14c8582 100644 --- a/lib/zexbox/metrics/context.ex +++ b/lib/zexbox/metrics/context.ex @@ -40,8 +40,10 @@ defmodule Zexbox.Metrics.Context do """ @spec metrics_disabled?() :: boolean() def metrics_disabled? do - pids_to_check = [self()] ++ callers() ++ ancestors() - Enum.any?(pids_to_check, &ContextRegistry.disabled?/1) + ([self()] ++ callers() ++ ancestors()) + |> List.flatten() + |> Enum.filter(&(is_pid(&1) or is_atom(&1))) + |> Enum.any?(&ContextRegistry.disabled?/1) end defp callers do diff --git a/lib/zexbox/metrics/context_registry.ex b/lib/zexbox/metrics/context_registry.ex index 416da2b..2249d63 100644 --- a/lib/zexbox/metrics/context_registry.ex +++ b/lib/zexbox/metrics/context_registry.ex @@ -37,14 +37,16 @@ defmodule Zexbox.Metrics.ContextRegistry do @doc """ Returns true if the given pid is in the disabled set. """ - @spec disabled?(pid()) :: boolean() - def disabled?(pid) when is_pid(pid) do + @spec disabled?(pid() | atom() | nil) :: boolean() + def disabled?(pid) when is_pid(pid) or is_atom(pid) do case :ets.lookup(@table, pid) do [{^pid, _present}] -> true [] -> false end end + def disabled?(_pid), do: false + @impl GenServer def init(_opts) do _table = :ets.new(@table, [:set, :public, :named_table, read_concurrency: true]) diff --git a/test/zexbox/metrics/context_registry_test.exs b/test/zexbox/metrics/context_registry_test.exs index 3e70128..245cda7 100644 --- a/test/zexbox/metrics/context_registry_test.exs +++ b/test/zexbox/metrics/context_registry_test.exs @@ -45,6 +45,12 @@ defmodule Zexbox.Metrics.ContextRegistryTest do # Wait until the registry processes the :DOWN and removes ETS entry. eventually(fn -> ContextRegistry.disabled?(pid) == false end) end + + test "disabled?/1 returns false for non-pid values" do + assert ContextRegistry.disabled?(nil) == false + assert ContextRegistry.disabled?({:not, :a, :pid}) == false + assert ContextRegistry.disabled?([self()]) == false + end end defp eventually(predicate, attempts \\ 50) diff --git a/test/zexbox/metrics/context_test.exs b/test/zexbox/metrics/context_test.exs index b068a20..d4048ec 100644 --- a/test/zexbox/metrics/context_test.exs +++ b/test/zexbox/metrics/context_test.exs @@ -6,6 +6,15 @@ defmodule Zexbox.Metrics.ContextTest do describe "disable_for_process/0 and enable_for_process/0" do setup do start_supervised!(ContextRegistry) + + old_callers = Process.get(:"$callers") + old_ancestors = Process.get(:"$ancestors") + + on_exit(fn -> + Process.put(:"$callers", old_callers) + Process.put(:"$ancestors", old_ancestors) + end) + :ok end @@ -33,5 +42,34 @@ defmodule Zexbox.Metrics.ContextTest do :ok = Zexbox.Metrics.enable_for_process() end + + test "metrics_disabled?/0 tolerates nested $callers and non-pid terms" do + parent = self() + + pid = + spawn(fn -> + send(parent, :ready) + + receive do + :stop -> :ok + end + end) + + on_exit(fn -> send(pid, :stop) end) + assert_receive :ready + + :ok = ContextRegistry.register(pid) + + # Simulate unexpected shapes (nested lists, tuples, etc.) in the process dictionary. + Process.put(:"$callers", [[pid], {:not_a_pid, 123}]) + Process.put(:"$ancestors", [[:some_name], [pid]]) + + assert Context.metrics_disabled?() == true + end + + test "metrics_disabled?/0 tolerates atom ancestors" do + Process.put(:"$ancestors", [:some_registered_name]) + assert Context.metrics_disabled?() == false + end end end