Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
defmodule LiveDebuggerRefactor.App.Debugger.CallbackTracing.Web.Assigns.Filters do
@moduledoc """
Functions for assigning filters to the socket.
"""

import Phoenix.Component

alias LiveDebuggerRefactor.App.Debugger.CallbackTracing.Web.Helpers.Filters, as: FiltersHelpers

@doc """
Assigns filters as `:current_filters`.
"""
def assign_current_filters(socket, filters) do
assign(socket, :current_filters, filters)
end

@doc """
Assigns default filters as `:current_filters`.
"""
def assign_current_filters(%{assigns: %{node_id: node_id}} = socket) do
assign(socket, :current_filters, FiltersHelpers.default_filters(node_id))
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,18 @@ defmodule LiveDebuggerRefactor.App.Debugger.CallbackTracing.Web.Components.Trace
"""
end

attr(:trace, Trace, required: true)

def short_trace_content(assigns) do
assigns = assign(assigns, :content, Enum.map_join(assigns.trace.args, " ", &inspect/1))

~H"""
<div class="grow shrink text-secondary-text font-code font-normal text-3xs truncate">
<p class="hide-on-open mt-0.5"><%= @content %></p>
</div>
"""
end

@doc """
Timestamp and execution time of the trace.
"""
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
defmodule LiveDebuggerRefactor.App.Debugger.CallbackTracing.Web.GlobalTracesLive do
@moduledoc """
Nested LiveView component for displaying list of traces for all nodes in the LiveView Process.
Sidebar is hidden by default. You can show it by sending "open-sidebar" JS event to this live view.


## Examples

#{__MODULE__}.live_render(
socket: socket,
id: "global-traces",
lv_process: lv_process,
params: %{},
class: "flex-1"
)

JS.push("open-sidebar", target: "#global-traces")
"""

use LiveDebuggerRefactor.App.Web, :live_view

alias LiveDebuggerRefactor.App.Debugger.CallbackTracing.Web.Assigns.Filters, as: FiltersAssigns
alias LiveDebuggerRefactor.App.Debugger.CallbackTracing.Web.HookComponents

alias LiveDebuggerRefactor.App.Debugger.CallbackTracing.Web.Components.Trace,
as: TraceComponents

alias LiveDebuggerRefactor.Structs.LvProcess

@live_stream_limit 128
@page_size 25

attr(:socket, Phoenix.LiveView.Socket, required: true)
attr(:id, :string, required: true)
attr(:lv_process, LvProcess, required: true)
attr(:params, :map, required: true, doc: "Query params from the root LiveView")
attr(:class, :string, default: "", doc: "CSS class for the container")

def live_render(assigns) do
session = %{
"id" => assigns.id,
"lv_process" => assigns.lv_process,
"params" => assigns.params,
"parent_pid" => self()
}

assigns = assign(assigns, session: session)

~H"""
<%= live_render(@socket, __MODULE__,
id: @id,
session: @session,
container: {:div, class: @class}
) %>
"""
end

@impl true
def mount(
_params,
%{"parent_pid" => parent_pid, "lv_process" => lv_process, "id" => id},
socket
) do
socket
|> assign(
id: id,
parent_pid: parent_pid,
lv_process: lv_process,
existing_traces_status: :loading,
tracing_started?: false,
traces_empty?: true,
displayed_trace: nil,
traces_continuation: nil,
sidebar_hidden?: true,
trace_search_query: "",
node_id: nil
)
|> stream(:existing_traces, [], reset: true)
|> put_private(:page_size, @page_size)
|> put_private(:live_stream_limit, @live_stream_limit)
|> FiltersAssigns.assign_current_filters()
|> HookComponents.LoadMoreButton.init()
|> HookComponents.RefreshButton.init()
|> HookComponents.ClearButton.init()
|> HookComponents.ToggleTracingButton.init()
|> HookComponents.TraceWrapper.init()
|> HookComponents.Stream.init()
|> HookComponents.FiltersSidebar.init()
|> HookComponents.SearchInput.init()
|> ok()
end

@impl true
def render(assigns) do
assigns = assign_load_more_button(assigns)

~H"""
<div class="grow p-8 overflow-y-auto scrollbar-main">
<div class="w-full min-w-[25rem] max-w-screen-2xl mx-auto">
<div class="flex flex-col gap-1.5 pb-6 px-0.5">
<.h1>Global Callback Traces</.h1>
<span class="text-secondary-text">
This view lists all callbacks inside debugged LiveView and its LiveComponents
</span>
</div>
<div class="w-full min-w-[20rem] flex flex-col pt-2 shadow-custom rounded-sm bg-surface-0-bg border border-default-border">
<div class="w-full flex justify-between items-center border-b border-default-border pb-2">
<div class="ml-2">
<HookComponents.SearchInput.render
disabled?={@tracing_started?}
trace_search_query={@trace_search_query}
/>
</div>
<div class="flex gap-2 items-center h-8 px-2">
<HookComponents.ToggleTracingButton.render tracing_started?={@tracing_started?} />
<%= if not @tracing_started? do %>
<HookComponents.RefreshButton.render label_class="hidden @[30rem]/traces:block" />
<HookComponents.ClearButton.render label_class="hidden @[30rem]/traces:block" />
<% end %>
</div>
</div>
<div class="flex flex-1 overflow-auto rounded-sm bg-surface-0-bg p-4">
<div class="w-full h-full flex flex-col gap-4">
<HookComponents.Stream.render
id={@id}
existing_traces_status={@existing_traces_status}
existing_traces={@streams.existing_traces}
>
<:trace :let={{id, trace_display}}>
<HookComponents.TraceWrapper.render id={id} trace_display={trace_display}>
<:label class="grid-cols-[auto_1fr_auto]">
<TraceComponents.module id={id} trace={trace_display.trace} class="col-span-3" />
<TraceComponents.callback_name trace={trace_display.trace} />
<TraceComponents.short_trace_content trace={trace_display.trace} />
<TraceComponents.trace_time_info id={id} trace_display={trace_display} />
</:label>

<:body>
<TraceComponents.trace_body id={id} trace={trace_display.trace} />
</:body>
</HookComponents.TraceWrapper.render>
</:trace>
</HookComponents.Stream.render>
<HookComponents.LoadMoreButton.render
:if={@load_more_button?}
traces_continuation={@traces_continuation}
/>
<TraceComponents.trace_fullscreen
:if={@displayed_trace}
id="trace-fullscreen"
trace={@displayed_trace}
/>
</div>
</div>
</div>
</div>
</div>
<HookComponents.FiltersSidebar.render
sidebar_hidden?={@sidebar_hidden?}
current_filters={@current_filters}
tracing_started?={@tracing_started?}
/>
"""
end

defp assign_load_more_button(assigns) do
load_more_button? =
not assigns.tracing_started? and not assigns.traces_empty? and
assigns.trace_search_query == ""

assign(assigns, :load_more_button?, load_more_button?)
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,7 @@ defmodule LiveDebuggerRefactor.App.Debugger.CallbackTracing.Web.Helpers.Filters
max_time_value = apply_unit_factor(max_time, max_time_unit)

if min_time_value > max_time_value do
[]
|> Keyword.put(:exec_time_min, "min must be less than max")
|> Keyword.put(:exec_time_max, "max must be greater than min")
Keyword.put([], :exec_time_min, "min must be less than max")
else
[]
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ defmodule LiveDebuggerRefactor.App.Debugger.CallbackTracing.Web.HookComponents.F

@impl true
def render(assigns) do
assigns = assign(assigns, fullscreen_id: @fullscreen_id, form_id: @form_id)

~H"""
<.fullscreen id={@fullscreen_id} title="Filters" class="max-w-112 min-w-[20rem]">
<.live_component
Expand Down Expand Up @@ -107,4 +109,6 @@ defmodule LiveDebuggerRefactor.App.Debugger.CallbackTracing.Web.HookComponents.F
|> assign(:current_filters, FiltersHelpers.default_filters(socket.assigns.node_id))
|> halt()
end

defp handle_event(_, _, socket), do: {:cont, socket}
end
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ defmodule LiveDebuggerRefactor.App.Debugger.CallbackTracing.Web.HookComponents.F
def init(socket) do
socket
|> check_assigns!(@required_assigns)
|> check_hook!(:existing_traces)
|> attach_hook(:filters, :handle_info, &handle_info/2)
|> attach_hook(:filters, :handle_event, &handle_event/3)
|> register_hook(:filters)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ defmodule LiveDebuggerRefactor.App.Debugger.CallbackTracing.Web.HookComponents.L
socket
|> check_assigns!(@required_assigns)
|> check_stream!(:existing_traces)
|> check_private!(:page_size)
|> attach_hook(:load_more_button, :handle_event, &handle_event/3)
|> register_hook(:load_more_button)
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
defmodule LiveDebuggerRefactor.App.Debugger.CallbackTracing.Web.HookComponents.Stream do
@moduledoc """
Hook component for displaying the traces stream.
It allows for composing style of traces visible in the stream.
"""

use LiveDebuggerRefactor.App.Web, :hook_component

@required_assigns [:id, :existing_traces_status]

@impl true
def init(socket) do
socket
|> check_assigns!(@required_assigns)
|> check_stream!(:existing_traces)
|> register_hook(:traces_stream)
end

attr(:id, :string, required: true)
attr(:existing_traces_status, :atom, required: true)
attr(:existing_traces, Phoenix.LiveView.LiveStream, required: true)

slot(:trace, required: true, doc: "Used for styling trace element. Remember to add `id`")

@impl true
def render(assigns) do
~H"""
<div id={"#{@id}-stream"} phx-update="stream" class="flex flex-col gap-2">
<div id={"#{@id}-stream-empty"} class="only:block hidden text-secondary-text text-center">
<div :if={@existing_traces_status == :ok}>
No traces have been recorded yet.
</div>
<div :if={@existing_traces_status == :loading} class="w-full flex items-center justify-center">
<.spinner size="sm" />
</div>
<.alert
:if={@existing_traces_status == :error}
with_icon
heading="Error fetching historical callback traces"
>
New events will still be displayed as they come. Check logs for more information
</.alert>
</div>
<%= for {dom_id, wrapped_trace} <- @existing_traces do %>
<%= if wrapped_trace.id == "separator" do %>
<.separator id={dom_id} />
<% else %>
<%= render_slot(@trace, {dom_id, wrapped_trace}) %>
<% end %>
<% end %>
</div>
"""
end

attr(:id, :string, required: true)

defp separator(assigns) do
~H"""
<div id={@id}>
<div class="h-6 my-1 font-normal text-xs text-secondary-text flex align items-center">
<div class="border-b border-default-border grow"></div>
<span class="mx-2">Past Traces</span>
<div class="border-b border-default-border grow"></div>
</div>
</div>
"""
end
end
Loading