Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Http Server Introspection

http_server_introspection.livemd

Http Server Introspection

Mix.install([
  {:kino, "~> 0.13.0"},
  {:kino_vega_lite, "~> 0.1.11"}
])

Section

import Kino.Shorts

defmodule Introspection do
  require Kino.RPC
  def get_active_connections(%{processes: processes}) do
    connection_supervisor = processes 
      |> Enum.find(fn 
           proc -> Keyword.get(proc, :registered_name) == HttpServer.ConnectionSupervisor 
         end)

    active_connections =  processes 
      |> Enum.filter(fn proc -> 
           Keyword.get(proc, :pid) in Keyword.get(connection_supervisor, :links) 
         end)
      |> Enum.filter(fn proc -> 
           Keyword.get(proc, :registered_name) != HttpServer.Supervisor 
         end)

    active_connections
  end

  def get_analytics(node, secret) do
    Node.set_cookie(node, secret)
    Kino.RPC.eval_string(
    node,
    ~S"""
    processes = Process.list() 
    |> Enum.flat_map(fn pid -> 
        case Process.info(pid) do
          nil -> []
          info -> [Keyword.put(info, :pid, pid)]
        end
      end)

    memory = :erlang.memory(:total)

    %{processes: processes, memory: memory}
    """,
    file: __ENV__.file
  )
  end
end

node = :"http@host.docker.internal"
Node.set_cookie(node, String.to_atom(System.fetch_env!("LB_SECRET")))

  
node = read_text("node", default: "http@host.docker.internal")
cookie = read_password("cookie")

node
connections_chart = VegaLite.new(width: 700)
|> VegaLite.mark(:line)
|> VegaLite.encode_field(:x, "time", type: :temporal)
|> VegaLite.encode_field(:y, "connection_count", type: :quantitative, title: "# connection")
|> Kino.VegaLite.render()

memory_chart = VegaLite.new(width: 700)
|> VegaLite.mark(:line)
|> VegaLite.encode_field(:x, "time", type: :temporal)
|> VegaLite.encode_field(:y, "memory", type: :quantitative, title: "memory in KB")
|> Kino.VegaLite.render()

total_memory_chart = VegaLite.new(width: 700)
|> VegaLite.mark(:line)
|> VegaLite.encode_field(:x, "time", type: :temporal)
|> VegaLite.encode_field(:y, "memory", type: :quantitative, title: "memory in MB")
|> Kino.VegaLite.render()

Kino.listen(20, fn _i -> 
  node_atom = String.to_atom(node)
  cookie_atom = String.to_atom(cookie)
  
  analytics = Introspection.get_analytics(node_atom, cookie_atom) 
  active_connections_procs = Introspection.get_active_connections(analytics)
  
  point = %{
    time: DateTime.now!("Etc/UTC"),
    connection_count: active_connections_procs |> Enum.count()
  }
  Kino.VegaLite.push(connections_chart, point, window: 10_000)

  used_memory = active_connections_procs
    |> Enum.map(fn proc -> proc[:heap_size] + proc[:stack_size] end)
    |> Enum.sum()

  memory_point = %{
    time: DateTime.now!("Etc/UTC"),
    memory: used_memory / 1000
  }
  Kino.VegaLite.push(memory_chart, memory_point, window: 10_000)

  total_memory = analytics[:memory]
  total_memory_point = %{
    time: DateTime.now!("Etc/UTC"),
    memory: total_memory / 1000_000
  }

  Kino.VegaLite.push(total_memory_chart, total_memory_point, window: 10_000)
end)