diff --git a/CHANGELOG.md b/CHANGELOG.md index 87c0fff..43d037a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # CHANGELOG (v0.1.X) +## 0.1.13 () + +### Backwards incompatible changes for 0.1.12 + * None + +### Bug fixes + * None + +### Enhancements + * [[`PR-30`](https://github.com/thiagoesteves/observer_web/pull/30)] Adding configurable timeout for fetching specific states. + ## 0.1.12 🚀 (2025-10-12) ### Backwards incompatible changes for 0.1.11 diff --git a/lib/observer_web/apps/process.ex b/lib/observer_web/apps/process.ex index 4bf20aa..2cad254 100644 --- a/lib/observer_web/apps/process.ex +++ b/lib/observer_web/apps/process.ex @@ -9,6 +9,8 @@ defmodule ObserverWeb.Apps.Process do alias ObserverWeb.Apps.Helper alias ObserverWeb.Rpc + @default_get_state_timeout 100 + @process_full [ :registered_name, :priority, @@ -34,16 +36,16 @@ defmodule ObserverWeb.Apps.Process do @doc """ Creates a complete overview of process stats based on the given `pid`. """ - @spec info(pid :: pid()) :: :undefined | map - def info(pid) do - process_info(pid, @process_full, &structure_full/2) + @spec info(pid :: pid(), timeout :: non_neg_integer()) :: :undefined | map + def info(pid, timeout \\ @default_get_state_timeout) do + process_info(pid, @process_full, &structure_full/3, timeout) end - @spec state(pid :: pid()) :: {:ok, any()} | {:error, String.t()} - def state(pid) do + @spec state(pid :: pid(), timeout :: non_neg_integer()) :: {:ok, any()} | {:error, String.t()} + def state(pid, timeout \\ @default_get_state_timeout) do if pid != self() do try do - state = :sys.get_state(pid, 100) + state = :sys.get_state(pid, timeout) {:ok, state} catch _, reason -> @@ -59,10 +61,10 @@ defmodule ObserverWeb.Apps.Process do ### ========================================================================== ### Private functions ### ========================================================================== - defp process_info(pid, information, structurer) do + defp process_info(pid, information, structurer, timeout) do case Rpc.pinfo(pid, information) do :undefined -> :undefined - data -> structurer.(data, pid) + data -> structurer.(data, pid, timeout) end end @@ -93,12 +95,12 @@ defmodule ObserverWeb.Apps.Process do # Structurers - defp structure_full(data, pid) do + defp structure_full(data, pid, timeout) do gc = Keyword.get(data, :garbage_collection, []) dictionary = Keyword.get(data, :dictionary) {state, phx_lv_socket} = - case state(pid) do + case state(pid, timeout) do {:ok, %{socket: %Phoenix.LiveView.Socket{}} = state} -> new_state = %{state | socket: "Phoenix.LiveView.Socket", components: "hidden"} {to_string(:io_lib.format("~tp", [new_state])), state.socket} diff --git a/lib/observer_web/telemetry/producer/phx_lv_socket.ex b/lib/observer_web/telemetry/producer/phx_lv_socket.ex index 2a94c29..3a34447 100644 --- a/lib/observer_web/telemetry/producer/phx_lv_socket.ex +++ b/lib/observer_web/telemetry/producer/phx_lv_socket.ex @@ -7,6 +7,8 @@ defmodule ObserverWeb.Telemetry.Producer.PhxLvSocket do alias ObserverWeb.Telemetry.Consumer + @default_lv_get_state_timeout 300 + @type t :: %__MODULE__{ module: atom(), event: list() @@ -95,7 +97,7 @@ defmodule ObserverWeb.Telemetry.Producer.PhxLvSocket do defp sockets_connected(sockets) do sockets |> Enum.reduce(0, fn socket, acc -> - case ObserverWeb.Apps.Process.state(socket) do + case ObserverWeb.Apps.Process.state(socket, @default_lv_get_state_timeout) do {:ok, %{socket: %Phoenix.LiveView.Socket{transport_pid: transport_pid}}} when is_pid(transport_pid) -> acc + 1 diff --git a/lib/web/components/metrics/phx_lv_socket.ex b/lib/web/components/metrics/phx_lv_socket.ex index b27a58c..9e74d5b 100644 --- a/lib/web/components/metrics/phx_lv_socket.ex +++ b/lib/web/components/metrics/phx_lv_socket.ex @@ -47,7 +47,7 @@ defmodule Observer.Web.Components.Metrics.PhxLvSocket do """ end - defp liveview_regex(), do: ~r/^phoenix\.liveview\.socket\..+\.total$/ + defp liveview_regex, do: ~r/^phoenix\.liveview\.socket\..+\.total$/ # NOTE: Streams are retrieved in the reverse order defp normalize(metrics) do diff --git a/lib/web/pages/apps/page.ex b/lib/web/pages/apps/page.ex index c8d4cec..480fe12 100644 --- a/lib/web/pages/apps/page.ex +++ b/lib/web/pages/apps/page.ex @@ -96,6 +96,13 @@ defmodule Observer.Web.Apps.Page do min="-1" label="Initial Depth" /> + <:inner_button> @@ -196,8 +203,15 @@ defmodule Observer.Web.Apps.Page do {:noreply, socket |> assign(:show_observer_options, show_observer_options)} end - def handle_parent_event("form-update", %{"initial_tree_depth" => depth}, socket) do - {:noreply, assign(socket, form: to_form(%{"initial_tree_depth" => depth}))} + def handle_parent_event( + "form-update", + %{"initial_tree_depth" => depth, "get_state_timeout" => get_state_timeout}, + socket + ) do + {:noreply, + assign(socket, + form: to_form(%{"initial_tree_depth" => depth, "get_state_timeout" => get_state_timeout}) + )} end def handle_parent_event( @@ -347,10 +361,18 @@ defmodule Observer.Web.Apps.Page do @impl Page def handle_info( {"request-process", %{"id" => request_id, "series_name" => series_name}}, - %{assigns: %{current_selected_id: %{id_string: id_string, debouncing: debouncing}}} = + %{ + assigns: %{ + current_selected_id: %{id_string: id_string, debouncing: debouncing}, + form: form + } + } = socket ) when id_string != request_id or debouncing < 0 do + get_state_timeout = form.params["get_state_timeout"] |> String.to_integer() + + # IO.inspect get_state_timeout pid? = String.contains?(request_id, "#PID<") port? = String.contains?(request_id, "#Port<") @@ -364,7 +386,7 @@ defmodule Observer.Web.Apps.Page do |> :erlang.list_to_pid() %{ - info: Apps.Process.info(pid), + info: Apps.Process.info(pid, get_state_timeout), id_string: request_id, type: "pid", debouncing: @tooltip_debouncing @@ -462,7 +484,7 @@ defmodule Observer.Web.Apps.Page do assign(socket, :observer_data, Map.put(observer_data, data_key, updated_data)) end - defp default_form_options, do: %{"initial_tree_depth" => "3"} + defp default_form_options, do: %{"initial_tree_depth" => "3", "get_state_timeout" => "100"} defp node_info_new do %{ diff --git a/mix.exs b/mix.exs index 71681e3..087a602 100644 --- a/mix.exs +++ b/mix.exs @@ -2,7 +2,7 @@ defmodule ObserverWeb.MixProject do use Mix.Project @source_url "https://github.com/thiagoesteves/observer_web" - @version "0.1.12" + @version "0.1.13" def project do [ diff --git a/test/observer_web/web/live/apps/page_test.exs b/test/observer_web/web/live/apps/page_test.exs index 19d4f76..c0f6830 100644 --- a/test/observer_web/web/live/apps/page_test.exs +++ b/test/observer_web/web/live/apps/page_test.exs @@ -43,6 +43,27 @@ defmodule Observer.Web.Apps.PageLiveTest do assert html =~ "4242" end + test "Adjust Get State Timeout", %{conn: conn} do + RpcStubber.defaults() + TelemetryStubber.defaults() + + {:ok, index_live, _html} = live(conn, "/observer/applications") + + html = + index_live + |> element("#apps-multi-select-toggle-options") + |> render_click() + + refute html =~ "8888" + + html = + index_live + |> element("#apps-update-form") + |> render_change(%{get_state_timeout: "8888"}) + + assert html =~ "8888" + end + test "Add/Remove Local Service + Kernel App", %{conn: conn} do node = Node.self() |> to_string service = Helpers.normalize_id(node) @@ -218,7 +239,7 @@ defmodule Observer.Web.Apps.PageLiveTest do assert_receive {:apps_page_pid, apps_page_pid}, 1_000 with_mock ObserverWeb.Apps.Process, - info: fn _ -> + info: fn _pid, _timeout -> data = %{ pid: self(), registered_name: "name",