Last active
January 11, 2021 22:09
-
-
Save dkuku/b4c4320c8d245022f72e8fc2bc77cea4 to your computer and use it in GitHub Desktop.
live dashboard os processes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule Phoenix.LiveDashboard.OsProcesses do | |
@moduledoc false | |
use Phoenix.LiveDashboard.PageBuilder | |
# module requires: | |
# {:table_parser, "~> 0.1.1"}, | |
# {:number, "~> 1.0.3"}, | |
@ps_cmd "ps" | |
@stat_map %{ | |
"D" => "uninterruptible sleep (IO)", | |
"I" => "Idle kernel thread", | |
"R" => "running or runnable", | |
"S" => "interruptible sleep (waiting)", | |
"T" => "stopped, by a job control signal", | |
"t" => "stopped by debugger", | |
"W" => "paging", | |
"X" => "dead", | |
"Z" => "zombie process", | |
"<" => "high-priority", | |
"N" => "low-priority", | |
"L" => "has pages locked into memory", | |
"s" => "is a session leader", | |
"l" => "is multi-threaded", | |
"+" => "foreground process group." | |
} | |
@impl true | |
def menu_link(_, _) do | |
{:ok, "OS Processes"} | |
end | |
@impl true | |
def render_page(assigns) do | |
table( | |
columns: columns(), | |
id: :os_processes, | |
row_attrs: &row_attrs/1, | |
row_fetcher: &fetch_processes/2, | |
rows_name: "processes", | |
title: "OS processes", | |
sort_by: :"%cpu" | |
) | |
end | |
def processes_callback(ps_params, limit, search, sort_by, sort_dir) do | |
@ps_cmd | |
|> System.cmd(ps_params) | |
|> elem(0) | |
|> TableParser.parse_table() | |
|> Enum.sort_by(&sort_value(&1, sort_by), sort_dir) | |
|> filter_rows(search) | |
|> Enum.take(limit) | |
end | |
def fetch_processes(params, node) do | |
%{limit: limit, search: search, sort_by: sort_by, sort_dir: sort_dir} = params | |
ps_params = | |
columns() | |
|> Enum.map(&Map.get(&1, :ps, Map.get(&1, :field))) | |
|> Enum.join(",") | |
data = | |
:rpc.call(node, __MODULE__, :processes_callback, [["-eo", ps_params], limit, search, sort_by, sort_dir]) | |
{data, length(data)} | |
end | |
defp filter_rows(rows, nil), do: rows | |
defp filter_rows(rows, search) do | |
Enum.filter(rows, fn row -> include_row?(row, search) end) | |
end | |
defp include_row?(nil, search), do: false | |
defp include_row?("", search), do: false | |
defp include_row?(row, search) do | |
row |> Map.values() |> Enum.join() =~ search | |
end | |
defp sort_value(map, key) do | |
map[key] |> Float.parse() |> elem(0) | |
end | |
def decode_stat(stat) do | |
{:safe, | |
String.graphemes(stat) | |
|> Enum.map(fn char -> Map.get(@stat_map, char) end) | |
|> Enum.join("<br>")} | |
end | |
def columns() do | |
[ | |
%{ | |
field: :pid, | |
sortable: :asc | |
}, | |
%{ | |
field: :user | |
}, | |
%{ | |
field: :"%cpu", | |
ps: :pcpu, | |
sortable: :desc, | |
format: &Kernel.<>(&1, "%") | |
}, | |
%{ | |
field: :"%mem", | |
ps: :pmem, | |
sortable: :desc, | |
format: &Kernel.<>(&1, "%") | |
}, | |
%{ | |
field: :vsz, | |
sortable: :asc, | |
format: &humanize_kilobyte_number/1 | |
}, | |
%{ | |
field: :rss, | |
sortable: :asc, | |
format: &humanize_kilobyte_number/1 | |
}, | |
%{ | |
field: :stat, | |
format: &decode_stat/1 | |
}, | |
%{ | |
field: :command, | |
ps: :comm | |
} | |
] | |
end | |
defp row_attrs(table) do | |
[ | |
{"phx-page-loading", true} | |
] | |
end | |
defp humanize_number(num_str) do | |
num_str | |
|> String.to_integer() | |
|> Number.SI.number_to_si( unit: "B") | |
end | |
defp humanize_kilobyte_number(num_str) do | |
num_str | |
|> String.to_integer() | |
|> Kernel.*(1024) | |
|> Number.SI.number_to_si( unit: "B") | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment