181 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Elixir
		
	
	
	
	
	
			
		
		
	
	
			181 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Elixir
		
	
	
	
	
	
defmodule Phoenix.LiveDashboard.Helpers do
 | 
						|
  @moduledoc false
 | 
						|
 | 
						|
  alias Phoenix.LiveDashboard.{PageBuilder, SystemInfo}
 | 
						|
  use Phoenix.Component
 | 
						|
 | 
						|
  alias SystemInfo.{ProcessDetails, PortDetails}
 | 
						|
  @format_limit 100
 | 
						|
 | 
						|
  @doc """
 | 
						|
  Formats any value.
 | 
						|
  """
 | 
						|
  def format_value(%ProcessDetails{} = info, live_dashboard_path) do
 | 
						|
    %{pid: pid, name_or_initial_call: name_or_initial_call} = info
 | 
						|
 | 
						|
    text =
 | 
						|
      if name_or_initial_call do
 | 
						|
        "#{inspect(pid)} (#{name_or_initial_call})"
 | 
						|
      else
 | 
						|
        inspect(pid)
 | 
						|
      end
 | 
						|
 | 
						|
    assigns = %{
 | 
						|
      to: live_dashboard_path.(node(pid), info: PageBuilder.encode_pid(pid)),
 | 
						|
      text: text
 | 
						|
    }
 | 
						|
 | 
						|
    ~H|<.link patch={@to}><%= @text %></.link>|
 | 
						|
  end
 | 
						|
 | 
						|
  def format_value(%PortDetails{} = info, live_dashboard_path) do
 | 
						|
    %{port: port, description: description} = info
 | 
						|
 | 
						|
    assigns = %{
 | 
						|
      to: live_dashboard_path.(node(port), info: PageBuilder.encode_port(port)),
 | 
						|
      text: "#{inspect(port)} (#{description})"
 | 
						|
    }
 | 
						|
 | 
						|
    ~H|<.link patch={@to}><%= @text %></.link>|
 | 
						|
  end
 | 
						|
 | 
						|
  # Not used in `phoenix_live_dashboard` code, but available for custom pages
 | 
						|
  def format_value(port, live_dashboard_path) when is_port(port) do
 | 
						|
    assigns = %{
 | 
						|
      to: live_dashboard_path.(node(port), info: PageBuilder.encode_port(port)),
 | 
						|
      text: inspect(port)
 | 
						|
    }
 | 
						|
 | 
						|
    ~H|<.link patch={@to}><%= @text %></.link>|
 | 
						|
  end
 | 
						|
 | 
						|
  # Not used in `phoenix_live_dashboard` code, but available for custom pages
 | 
						|
  def format_value(pid, live_dashboard_path) when is_pid(pid) do
 | 
						|
    assigns = %{
 | 
						|
      to: live_dashboard_path.(node(pid), info: PageBuilder.encode_pid(pid)),
 | 
						|
      text: inspect(pid)
 | 
						|
    }
 | 
						|
 | 
						|
    ~H|<.link patch={@to}><%= @text %></.link>|
 | 
						|
  end
 | 
						|
 | 
						|
  def format_value([_ | _] = list, live_dashboard_path) do
 | 
						|
    {entries, left_over} = Enum.split(list, @format_limit)
 | 
						|
 | 
						|
    entries
 | 
						|
    |> Enum.map(&format_value(&1, live_dashboard_path))
 | 
						|
    |> Kernel.++(if left_over == [], do: [], else: ["..."])
 | 
						|
    |> Enum.intersperse({:safe, "<br />"})
 | 
						|
  end
 | 
						|
 | 
						|
  def format_value(other, _socket), do: inspect(other, pretty: true, limit: @format_limit)
 | 
						|
 | 
						|
  @doc """
 | 
						|
  Formats initial calls.
 | 
						|
 | 
						|
  Same as `format_call` but takes into account Supervisor's special format.
 | 
						|
  """
 | 
						|
  def format_initial_call({:supervisor, mod, arity}), do: format_call({mod, :init, arity})
 | 
						|
  def format_initial_call(call), do: format_call(call)
 | 
						|
 | 
						|
  @doc """
 | 
						|
  Formats MFAs.
 | 
						|
  """
 | 
						|
  def format_call(:undefined), do: "undefined"
 | 
						|
  def format_call({m, f, a}), do: Exception.format_mfa(m, f, a)
 | 
						|
 | 
						|
  @doc """
 | 
						|
  Formats the stacktrace.
 | 
						|
  """
 | 
						|
  def format_stacktrace(stacktrace) do
 | 
						|
    stacktrace
 | 
						|
    |> Exception.format_stacktrace()
 | 
						|
    |> String.split("\n")
 | 
						|
    |> Enum.map(&String.replace_prefix(&1, "    ", ""))
 | 
						|
    |> Enum.join("\n")
 | 
						|
  end
 | 
						|
 | 
						|
  @format_path_regex ~r/^(?<beginning>((.+?\/){3})).*(?<ending>(\/.*){3})$/
 | 
						|
 | 
						|
  @doc """
 | 
						|
  Formats large paths by removing intermediate parts.
 | 
						|
  """
 | 
						|
  def format_path(path) do
 | 
						|
    path_string =
 | 
						|
      path
 | 
						|
      |> to_string()
 | 
						|
      |> String.replace_prefix("\"", "")
 | 
						|
      |> String.replace_suffix("\"", "")
 | 
						|
 | 
						|
    case Regex.named_captures(@format_path_regex, path_string) do
 | 
						|
      %{"beginning" => beginning, "ending" => ending} -> "#{beginning}...#{ending}"
 | 
						|
      _ -> path_string
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  @doc """
 | 
						|
  Formats uptime.
 | 
						|
  """
 | 
						|
  def format_uptime(uptime) do
 | 
						|
    {d, {h, m, _s}} = :calendar.seconds_to_daystime(div(uptime, 1000))
 | 
						|
 | 
						|
    cond do
 | 
						|
      d > 0 -> "#{d}d#{h}h#{m}m"
 | 
						|
      h > 0 -> "#{h}h#{m}m"
 | 
						|
      true -> "#{m}m"
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  @doc """
 | 
						|
  Formats percent.
 | 
						|
  """
 | 
						|
  def format_percent(percent) when is_float(percent), do: "#{Float.round(percent, 1)}%"
 | 
						|
  def format_percent(nil), do: "0%"
 | 
						|
  def format_percent(percent), do: "#{percent}%"
 | 
						|
 | 
						|
  @doc """
 | 
						|
  Formats words as bytes.
 | 
						|
  """
 | 
						|
  def format_words(words) when is_integer(words) do
 | 
						|
    format_bytes(words * :erlang.system_info(:wordsize))
 | 
						|
  end
 | 
						|
 | 
						|
  @doc """
 | 
						|
  Formats bytes.
 | 
						|
  """
 | 
						|
  def format_bytes(bytes) when is_integer(bytes) do
 | 
						|
    cond do
 | 
						|
      bytes >= memory_unit(:TB) -> format_bytes(bytes, :TB)
 | 
						|
      bytes >= memory_unit(:GB) -> format_bytes(bytes, :GB)
 | 
						|
      bytes >= memory_unit(:MB) -> format_bytes(bytes, :MB)
 | 
						|
      bytes >= memory_unit(:KB) -> format_bytes(bytes, :KB)
 | 
						|
      true -> format_bytes(bytes, :B)
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  defp format_bytes(bytes, :B) when is_integer(bytes), do: "#{bytes} B"
 | 
						|
 | 
						|
  defp format_bytes(bytes, unit) when is_integer(bytes) do
 | 
						|
    value = bytes / memory_unit(unit)
 | 
						|
    "#{:erlang.float_to_binary(value, decimals: 1)} #{unit}"
 | 
						|
  end
 | 
						|
 | 
						|
  defp memory_unit(:TB), do: 1024 * 1024 * 1024 * 1024
 | 
						|
  defp memory_unit(:GB), do: 1024 * 1024 * 1024
 | 
						|
  defp memory_unit(:MB), do: 1024 * 1024
 | 
						|
  defp memory_unit(:KB), do: 1024
 | 
						|
 | 
						|
  @doc """
 | 
						|
  Computes the percentage between `value` and `total`.
 | 
						|
  """
 | 
						|
  def percentage(value, total, rounds \\ 1)
 | 
						|
  def percentage(_value, 0, _rounds), do: 0
 | 
						|
  def percentage(nil, _total, _rounds), do: 0
 | 
						|
  def percentage(value, total, rounds), do: Float.round(value / total * 100, rounds)
 | 
						|
 | 
						|
  @doc """
 | 
						|
  All connected nodes (including the current node).
 | 
						|
  """
 | 
						|
  def nodes(), do: [node()] ++ Node.list(:connected)
 | 
						|
end
 |