Skip to content

Instantly share code, notes, and snippets.

@petethepig
Last active August 30, 2021 19:53
Show Gist options
  • Star 12 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save petethepig/0f33c910fb2edad8969a5775e23bb99f to your computer and use it in GitHub Desktop.
Save petethepig/0f33c910fb2edad8969a5775e23bb99f to your computer and use it in GitHub Desktop.
redis-stats.rb

This script generates a report on the incoming redis commands broken down by redis command.

The report is ncdu-compatible, so it's easy to browse.

Usage:

ssh ec2-user@52.206.63.28 redis-cli monitor | ruby redis-stats.rb
ncdu -f redis-stats.ncdu

image

# usage
# ssh ec2-user@52.206.63.28 redis-cli monitor | ruby ruby-stats.rb
require "terminal-table"
require "json"
class RedisAnalyser
def initialize(summary_interval, max_rows)
@metrics = {}
@tree = {}
@last_update = Time.new
@count = 0
@summary_interval = summary_interval
@max_rows = max_rows
end
def start
ARGF.each do |line|
process(line)
maybe_print_summary
end
end
def process(line)
arr = line.strip.split(/[ :"\/-]+/)
return unless arr.size > 4
metric = arr[5..-1]
metric.unshift(arr[4].upcase)
metric = metric.join(".")
@count += 1
add_to_table(metric)
add_to_tree(metric)
end
def add_to_table(metric)
parts = metric.split(".")
full_name = ""
parts.each do |part|
full_name << ".#{part}"
@metrics[full_name] ||= 0
@metrics[full_name] += 1
end
end
def add_to_tree(metric)
parts = metric.split(".")
node = @tree
last_metric = parts.pop
parts.each do |part|
node[part] ||= {}
node = node[part]
return unless node.is_a?(Hash)
end
node[last_metric] ||= 0
node[last_metric] += 1
end
def maybe_print_summary
if Time.new - @last_update > @summary_interval
print_summary
@last_update = Time.new
end
end
def print_summary
rows = @metrics.to_a.sort_by(&:last).reverse[0..@max_rows]
puts "#{@count} metrics collected, press Ctrl+C to quit and save ncdu-compatible report"
puts Terminal::Table.new(:rows => rows, :headings => ["metric prefix", "updates"])
puts ""
end
def traverse(node, key_name)
if node.is_a?(Hash)
children = node.map do |k, v|
traverse(v, k)
end
val = children.map do |x|
if x.is_a?(Hash)
x["asize"]
else
x.first["asize"]
end
end.inject(:+)
children.unshift({
"name" => key_name,
"asize" => val,
"dsize" => val,
})
children
else # number
{
"name" => key_name,
"asize" => node,
"dsize" => node,
}
end
end
def ncdu_export(path)
data = traverse(@tree, "root")
result = [
1,
0,
{
"progname" => "ncdu",
"progver" => "1.11",
"timestamp" => Time.new.to_i
}
] + [data]
puts "Saving ncdu-compatible report to #{path}"
puts "Run `ncdu -f #{path}` to browse the results"
File.binwrite(path, result.to_json)
end
end
puts DATA.read
ncdu_path = (ARGV[0] || "redis-stats.ncdu").to_s
summary_interval = (ARGV[1] || 5).to_i
max_rows = (ARGV[2] || 10).to_i
sa = RedisAnalyser.new(summary_interval, max_rows)
trap("SIGINT") do
sa.ncdu_export(ncdu_path)
end
sa.start
__END__
Usage:
ssh ec2-user@52.206.63.28 redis-cli monitor | ruby ruby-stats.rb [report-path] [summary-interval] [max-rows]
* report-path path to ncdu compatible report
* summary-interval interval between summary prints
* max-rows maximum amount of rows in summary report
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment