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
# 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 | |