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