feat(introspection): add Excache.Command.Introspection.Info and render
This commit is contained in:
1 parent
fd919fe728
commit
8757d4b0c8
1 file changed
+163
@@ -0,0 +1,163 @@
|
||||
defmodule Excache.Command.Introspection.Info do
|
||||
@moduledoc false
|
||||
|
||||
alias Excache.Aof
|
||||
alias Excache.Clients
|
||||
alias Excache.Config
|
||||
alias Excache.Eviction
|
||||
alias Excache.Eviction.Runner
|
||||
alias Excache.Memory
|
||||
alias Excache.Persistence
|
||||
alias Excache.Store
|
||||
|
||||
@redisCompat "7.2.0"
|
||||
|
||||
@sections %{
|
||||
"server" => :server,
|
||||
"clients" => :clients,
|
||||
"memory" => :memory,
|
||||
"persistence" => :persistence,
|
||||
"stats" => :stats,
|
||||
"replication" => :replication,
|
||||
"keyspace" => :keyspace,
|
||||
"cpu" => :cpu
|
||||
}
|
||||
|
||||
@default [:server, :clients, :memory, :persistence, :stats, :replication, :keyspace]
|
||||
@everything @default ++ [:cpu]
|
||||
|
||||
@titles %{
|
||||
server: "Server",
|
||||
clients: "Clients",
|
||||
memory: "Memory",
|
||||
persistence: "Persistence",
|
||||
stats: "Stats",
|
||||
replication: "Replication",
|
||||
keyspace: "Keyspace",
|
||||
cpu: "Cpu"
|
||||
}
|
||||
|
||||
@clientSections [:server, :clients, :stats]
|
||||
@keyspaceSections [:memory, :keyspace]
|
||||
|
||||
def render(args), do: args |> requested() |> emit()
|
||||
|
||||
defp requested([]), do: @default
|
||||
|
||||
defp requested(args) do
|
||||
args
|
||||
|> Enum.flat_map(fn arg ->
|
||||
case String.downcase(arg) do
|
||||
"all" -> @everything
|
||||
"everything" -> @everything
|
||||
"default" -> @default
|
||||
name -> @sections |> Map.get(name) |> List.wrap()
|
||||
end
|
||||
end)
|
||||
|> Enum.uniq()
|
||||
end
|
||||
|
||||
defp emit([]), do: ""
|
||||
|
||||
defp emit(sections) do
|
||||
context = context(sections)
|
||||
|
||||
sections
|
||||
|> Enum.map(fn section -> block(Map.fetch!(@titles, section), lines(section, context)) end)
|
||||
|> IO.iodata_to_binary()
|
||||
end
|
||||
|
||||
defp block(title, fields) do
|
||||
[
|
||||
"# ",
|
||||
title,
|
||||
"\r\n",
|
||||
Enum.map(fields, fn {key, value} -> [key, ":", text(value), "\r\n"] end),
|
||||
"\r\n"
|
||||
]
|
||||
end
|
||||
|
||||
defp text(value) when is_binary(value), do: value
|
||||
defp text(value) when is_integer(value), do: Integer.to_string(value)
|
||||
defp text(value) when is_atom(value), do: Atom.to_string(value)
|
||||
defp text(value) when is_list(value), do: List.to_string(value)
|
||||
|
||||
defp context(sections) do
|
||||
%{}
|
||||
|> withClients(Enum.any?(sections, &(&1 in @clientSections)))
|
||||
|> withKeyspace(Enum.any?(sections, &(&1 in @keyspaceSections)))
|
||||
end
|
||||
|
||||
defp withClients(context, false), do: context
|
||||
defp withClients(context, true), do: Map.put(context, :clients, Clients.snapshot())
|
||||
|
||||
defp withKeyspace(context, false), do: context
|
||||
|
||||
defp withKeyspace(context, true) do
|
||||
entries = Store.dump()
|
||||
now = System.system_time(:millisecond)
|
||||
remaining = for {_key, _kind, _value, at} <- entries, at != nil and at > now, do: at - now
|
||||
|
||||
Map.merge(context, %{
|
||||
memory: Memory.stats(entries),
|
||||
volatile: length(remaining),
|
||||
avgTtl: averageTtl(remaining)
|
||||
})
|
||||
end
|
||||
|
||||
defp averageTtl([]), do: 0
|
||||
defp averageTtl(remaining), do: div(Enum.sum(remaining), length(remaining))
|
||||
|
||||
defp lines(:server, context) do
|
||||
snapshot = context.clients
|
||||
uptime = div(System.system_time(:millisecond) - snapshot.startedAt, 1000)
|
||||
|
||||
[
|
||||
{"excache_version", version()},
|
||||
{"redis_version", @redisCompat},
|
||||
{"redis_mode", "standalone"},
|
||||
{"os", :erlang.system_info(:system_architecture)},
|
||||
{"arch_bits", :erlang.system_info(:wordsize) * 8},
|
||||
{"process_id", :os.getpid()},
|
||||
{"run_id", snapshot.runId},
|
||||
{"tcp_port", tcpPort()},
|
||||
{"uptime_in_seconds", uptime},
|
||||
{"uptime_in_days", div(uptime, 86_400)},
|
||||
{"otp_release", :erlang.system_info(:otp_release)},
|
||||
{"erts_version", :erlang.system_info(:version)},
|
||||
{"elixir_version", System.version()}
|
||||
]
|
||||
end
|
||||
|
||||
defp lines(:clients, context) do
|
||||
clients = context.clients.clients
|
||||
|
||||
[
|
||||
{"connected_clients", length(clients)},
|
||||
{"blocked_clients", Enum.count(clients, fn client -> Clients.blocked?(client.pid) end)},
|
||||
{"pubsub_clients",
|
||||
Enum.count(clients, fn client -> Clients.subscriptionCount(client.pid) > 0 end)}
|
||||
]
|
||||
end
|
||||
|
||||
defp lines(:memory, context) do
|
||||
stats = context.memory
|
||||
limit = Config.maxmemory()
|
||||
|
||||
[
|
||||
{"used_memory", stats.usedMemory},
|
||||
{"used_memory_human", humanBytes(stats.usedMemory)},
|
||||
{"used_memory_dataset", stats.datasetBytes},
|
||||
{"used_memory_overhead", stats.overheadBytes},
|
||||
{"used_memory_beam", :erlang.memory(:total)},
|
||||
{"used_memory_beam_processes", :erlang.memory(:processes)},
|
||||
{"used_memory_beam_binary", :erlang.memory(:binary)},
|
||||
{"used_memory_beam_ets", :erlang.memory(:ets)},
|
||||
{"maxmemory", limit},
|
||||
{"maxmemory_human", humanBytes(limit)},
|
||||
{"maxmemory_policy", Eviction.policyName(Config.policy())},
|
||||
{"maxmemory_samples", Config.maxmemorySamples()},
|
||||
{"mem_allocator", "erts"}
|
||||
]
|
||||
end
|
||||
|
||||
Reference in new issue
Block a user