redis-stat 0.2.7 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md CHANGED
@@ -1,6 +1,14 @@
1
1
  # redis-stat
2
2
 
3
- A command-line Redis monitoring tool written in Ruby.
3
+ _redis-stat_ is a simple Redis monitoring tool written in Ruby.
4
+
5
+ It is based on [INFO](http://redis.io/commands/info) command of Redis,
6
+ and thus generally won't affect the performance of the Redis instance
7
+ unlike the other monitoring tools based on [MONITOR](http://redis.io/commands/monitor) command.
8
+
9
+ _redis-stat_ allows you to monitor Redis instances
10
+ - either with vmstat-like output from the terminal
11
+ - or with the dashboard page served by its embedded web server.
4
12
 
5
13
  ## Installation
6
14
 
@@ -13,38 +21,59 @@ gem install redis-stat
13
21
  ```
14
22
  usage: redis-stat [HOST[:PORT] ...] [INTERVAL [COUNT]]
15
23
 
16
- --auth=PASSWORD Password
17
- --csv=OUTPUT_CSV_FILE_PATH Save the result in CSV format
24
+ -a, --auth=PASSWORD Password
18
25
  -v, --verbose Show more info
19
26
  --style=STYLE Output style: unicode|ascii
20
27
  --no-color Suppress ANSI color codes
28
+ --csv=OUTPUT_CSV_FILE_PATH Save the result in CSV format
29
+
30
+ --server[=PORT] Launch redis-stat web server (default port: 63790)
31
+ --daemon Daemonize redis-stat. Must be used with --server option.
32
+
21
33
  --version Show version
22
34
  --help Show this message
23
35
  ```
24
36
 
25
- ## Examples
37
+ ## Running redis-stat for command-line monitoring
26
38
 
27
39
  ```
28
40
  redis-stat
29
-
30
41
  redis-stat 1
31
-
32
42
  redis-stat 1 10
33
-
43
+ redis-stat --verbose
34
44
  redis-stat localhost:6380 1 10
35
-
36
45
  redis-stat localhost localhost:6380 localhost:6381 5
37
-
38
46
  redis-stat localhost localhost:6380 1 10 --csv=/tmp/output.csv --verbose
39
47
  ```
40
48
 
41
- ## Screenshot
49
+ ### Screenshot
42
50
 
43
- ![](https://github.com/junegunn/redis-stat/raw/master/screenshots/redis-stat-0.2.4.png)
51
+ ![Terminal output](https://github.com/junegunn/redis-stat/raw/master/screenshots/redis-stat-0.3.0.png)
44
52
 
45
- ## Contributors
53
+ ## redis-stat in web browser
54
+
55
+ When `--server` option is set, redis-stat will open up an embedded web server (default port: 63790)
56
+ in the background so that you can monitor Redis in your browser.
57
+
58
+ Since _redis-stat_ pushes updates every interval via [Server-sent events](http://www.w3.org/TR/eventsource/),
59
+ modern browsers are required to view the page.
46
60
 
61
+ ```
62
+ redis-stat --server
63
+ redis-stat --verbose --server=8080 5
64
+
65
+ # redis-stat server can be daemonized
66
+ redis-stat --server --daemon
67
+ ```
68
+
69
+ ### Screenshot
70
+
71
+ ![Dashboard](https://github.com/junegunn/redis-stat/raw/master/screenshots/redis-stat-web.png)
72
+
73
+ ## Author
47
74
  - [Junegunn Choi](https://github.com/junegunn)
75
+
76
+ ## Contributors
48
77
  - [Chris Meisl](https://github.com/cmeisl)
49
78
 
50
79
  ## Contributing
data/bin/redis-stat CHANGED
@@ -5,4 +5,12 @@ require 'redis-stat'
5
5
 
6
6
  options = RedisStat::Option.parse ARGV
7
7
  rs = RedisStat.new options
8
- rs.start $stdout
8
+ if options[:daemon]
9
+ require 'daemons'
10
+ Daemons.daemonize(:app_name => 'redis-stat-daemon')
11
+ end
12
+ begin
13
+ rs.start $stdout
14
+ rescue Exception
15
+ raise if options[:verbose]
16
+ end
data/lib/redis-stat.rb CHANGED
@@ -1,7 +1,9 @@
1
1
  # encoding: utf-8
2
2
 
3
- require "redis-stat/version"
4
- require "redis-stat/option"
3
+ require 'redis-stat/version'
4
+ require 'redis-stat/constants'
5
+ require 'redis-stat/option'
6
+ require 'redis-stat/server' unless RUBY_PLATFORM == 'java'
5
7
  require 'insensitive_hash'
6
8
  require 'redis'
7
9
  require 'tabularize'
@@ -11,15 +13,14 @@ require 'parallelize'
11
13
  require 'si'
12
14
 
13
15
  class RedisStat
14
- DEFAULT_TERM_WIDTH = 180
15
- DEFAULT_TERM_HEIGHT = 25
16
+ attr_reader :hosts, :measures
16
17
 
17
18
  def initialize options = {}
18
19
  options = RedisStat::Option::DEFAULT.merge options
19
20
  @hosts = options[:hosts]
20
21
  @redises = @hosts.map { |e|
21
22
  host, port = e.split(':')
22
- Redis.new(Hash[ {:host => host, :port => port}.select { |k, v| v } ])
23
+ Redis.new(Hash[ {:host => host, :port => port, :timeout => DEFAULT_REDIS_TIMEOUT}.select { |k, v| v } ])
23
24
  }
24
25
  @interval = options[:interval]
25
26
  @max_count = options[:count]
@@ -28,9 +29,16 @@ class RedisStat
28
29
  @csv = options[:csv]
29
30
  @auth = options[:auth]
30
31
  @measures = MEASURES[ options[:verbose] ? :verbose : :default ]
32
+ @all_measures= MEASURES.values.inject(:+).uniq - [:at]
31
33
  @count = 0
32
34
  @style = options[:style]
33
35
  @first_batch = true
36
+ @server_port = options[:server_port]
37
+ @daemonized = options[:daemon]
38
+ end
39
+
40
+ def info
41
+ collect
34
42
  end
35
43
 
36
44
  def start output_stream
@@ -50,30 +58,18 @@ class RedisStat
50
58
  end
51
59
  end
52
60
 
53
- @started_at = Time.now
54
- all_measures = MEASURES.values.inject(:+).uniq - [:at]
55
- prev_info = nil
56
- loop do
57
- info = {}.insensitive
58
- class << info
59
- def sumf label
60
- (self[label] || []).map(&:to_f).inject(:+)
61
- end
62
- end
63
-
64
- info[:at] = Time.now.to_f
65
- @redises.pmap(@redises.length) { |redis|
66
- redis.info.insensitive
67
- }.each do |rinfo|
68
- (all_measures + rinfo.keys.select { |k| k =~ /^db[0-9]+$/ }).each do |k|
69
- info[k] ||= []
70
- info[k] << rinfo[k]
71
- end
72
- end
73
61
 
74
- output info, prev_info, csv
62
+ @started_at = Time.now
63
+ prev_info = nil
64
+ server = start_server if @server_port
75
65
 
66
+ loop do
67
+ info = collect
68
+ info_output = process info, prev_info
69
+ output info, info_output, csv unless @daemonized
70
+ server.push info, Hash[info_output] if server
76
71
  prev_info = info
72
+
77
73
  @count += 1
78
74
  break if @max_count && @count >= @max_count
79
75
  sleep @interval
@@ -84,7 +80,7 @@ class RedisStat
84
80
  @os.puts ansi(:yellow, :bold) { "Interrupted." }
85
81
  rescue Exception => e
86
82
  @os.puts ansi(:red, :bold) { e.to_s }
87
- exit 1
83
+ raise
88
84
  ensure
89
85
  csv.close if csv
90
86
  end
@@ -94,14 +90,51 @@ class RedisStat
94
90
  end
95
91
 
96
92
  private
93
+ def start_server
94
+ RedisStat::Server.set :port, @server_port
95
+ RedisStat::Server.set :redis_stat, self
96
+ Thread.new { RedisStat::Server.run! }
97
+ RedisStat::Server.wait_until_running
98
+ trap('INT') { Thread.main.raise Interrupt }
99
+ RedisStat::Server
100
+ end
101
+
102
+ def collect
103
+ {}.insensitive.tap do |info|
104
+ class << info
105
+ def sumf label
106
+ (self[label] || []).map(&:to_f).inject(:+)
107
+ end
108
+ end
109
+
110
+ info[:at] = Time.now.to_f
111
+ @redises.pmap(@redises.length) { |redis|
112
+ redis.info.insensitive
113
+ }.each do |rinfo|
114
+ (@all_measures + rinfo.keys.select { |k| k =~ /^db[0-9]+$/ }).each do |k|
115
+ info[k] ||= []
116
+ info[k] << rinfo[k]
117
+ end
118
+ end
119
+ end
120
+ end
121
+
97
122
  def update_term_size!
98
- if RUBY_PLATFORM.match(/java/)
123
+ if RUBY_PLATFORM == 'java'
99
124
  require 'java'
100
125
  begin
101
- @term ||= Java::jline.Terminal.getTerminal
102
- @term_width = (@term.getTerminalWidth rescue DEFAULT_TERM_WIDTH)
103
- @term_height = (@term.getTerminalHeight rescue DEFAULT_TERM_HEIGHT) - 4
104
- return
126
+ case JRUBY_VERSION
127
+ when /^1\.7/
128
+ @term ||= Java::jline.console.ConsoleReader.new.getTerminal
129
+ @term_width = (@term.width rescue DEFAULT_TERM_WIDTH)
130
+ @term_height = (@term.height rescue DEFAULT_TERM_HEIGHT) - 4
131
+ return
132
+ when /^1\.6/
133
+ @term ||= Java::jline.ConsoleReader.new.getTerminal
134
+ @term_width = (@term.getTerminalWidth rescue DEFAULT_TERM_WIDTH)
135
+ @term_height = (@term.getTerminalHeight rescue DEFAULT_TERM_HEIGHT) - 4
136
+ return
137
+ end
105
138
  rescue Exception
106
139
  # Fallback to tput (which yields incorrect values as of now)
107
140
  end
@@ -130,9 +163,7 @@ private
130
163
  end)
131
164
  end
132
165
 
133
- def output info, prev_info, file
134
- info_output = process info, prev_info
135
-
166
+ def output info, info_output, file
136
167
  @table ||= init_table info_output
137
168
 
138
169
  movement = nil
@@ -238,9 +269,11 @@ private
238
269
 
239
270
  case key
240
271
  when :at
241
- Time.now.strftime('%H:%M:%S')
272
+ val = Time.now.strftime('%H:%M:%S')
273
+ [val, val]
242
274
  when :used_cpu_user, :used_cpu_sys
243
275
  val = get_diff.call(key)
276
+ val &&= (val * 100).round
244
277
  [humanize_number(val), val]
245
278
  when :keys
246
279
  val = Hash[ info.select { |k, v| k =~ /^db[0-9]+$/ } ].values.inject(0) { |sum, vs|
@@ -251,14 +284,12 @@ private
251
284
  :keyspace_misses_per_second, :total_commands_processed_per_second
252
285
  val = get_diff.call(key.to_s.gsub(/_per_second$/, '').to_sym)
253
286
  [humanize_number(val), val]
254
- when :total_commands_processed, :evicted_keys, :expired_keys, :keyspace_hits, :keyspace_misses
255
- val = info.sumf(key)
256
- [humanize_number(val.to_i), val]
257
287
  when :used_memory, :used_memory_rss, :aof_current_size, :aof_base_size
258
288
  val = info.sumf(key)
259
289
  [humanize_number(val.to_i, true), val]
260
290
  else
261
- humanize_number info.sumf(key)
291
+ val = info.sumf(key)
292
+ [humanize_number(val), val]
262
293
  end
263
294
  end
264
295
 
@@ -280,111 +311,4 @@ private
280
311
  ANSI::Code.ansi *args, &block
281
312
  end
282
313
  end
283
-
284
- MEASURES = {
285
- :static => [
286
- :redis_version,
287
- :process_id,
288
- :uptime_in_seconds,
289
- :uptime_in_days,
290
- :gcc_version,
291
- :role,
292
- :connected_slaves,
293
- :aof_enabled,
294
- :vm_enabled
295
- ],
296
- :default => [
297
- :at,
298
- :used_cpu_user,
299
- :used_cpu_sys,
300
- :connected_clients,
301
- :blocked_clients,
302
- :used_memory,
303
- :used_memory_rss,
304
- :keys,
305
- :total_commands_processed_per_second,
306
- :expired_keys_per_second,
307
- :evicted_keys_per_second,
308
- :keyspace_hits_per_second,
309
- :keyspace_misses_per_second,
310
- :aof_current_size,
311
- :pubsub_channels,
312
- ],
313
- :verbose => [
314
- :at,
315
- :used_cpu_user,
316
- :used_cpu_sys,
317
- :connected_clients,
318
- :blocked_clients,
319
- :used_memory,
320
- :used_memory_rss,
321
- :mem_fragmentation_ratio,
322
- :keys,
323
- :total_commands_processed_per_second,
324
- :total_commands_processed,
325
- :expired_keys_per_second,
326
- :expired_keys,
327
- :evicted_keys_per_second,
328
- :evicted_keys,
329
- :keyspace_hits_per_second,
330
- :keyspace_hits,
331
- :keyspace_misses_per_second,
332
- :keyspace_misses,
333
- :aof_current_size,
334
- :aof_base_size,
335
- :pubsub_channels,
336
- :pubsub_patterns,
337
- ]
338
- }
339
-
340
- COLORS = {
341
- :at => [:bold],
342
- :used_cpu_user => [:yellow, :bold],
343
- :used_cpu_sys => [:yellow],
344
- :connected_clients => [:cyan, :bold],
345
- :blocked_clients => [:cyan, :bold],
346
- :used_memory => [:green],
347
- :used_memory_rss => [:green],
348
- :mem_fragmentation_ratio => [:green],
349
- :keys => [:bold],
350
- :total_commands_processed => [:blue, :bold],
351
- :total_commands_processed_per_second => [:blue, :bold],
352
- :expired_keys => [:red],
353
- :expired_keys_per_second => [:red],
354
- :evicted_keys => [:red, :bold],
355
- :evicted_keys_per_second => [:red, :bold],
356
- :keyspace_hits => [:magenta, :bold],
357
- :keyspace_hits_per_second => [:magenta, :bold],
358
- :keyspace_misses => [:magenta],
359
- :keyspace_misses_per_second => [:magenta],
360
- :aof_current_size => [:cyan],
361
- :aof_base_size => [:cyan],
362
- :pubsub_channels => [:cyan, :bold],
363
- :pubsub_patterns => [:cyan, :bold],
364
- }
365
-
366
- LABELS = {
367
- :at => 'time',
368
- :used_cpu_user => 'us',
369
- :used_cpu_sys => 'sy',
370
- :connected_clients => 'cl',
371
- :blocked_clients => 'bcl',
372
- :used_memory => 'mem',
373
- :used_memory_rss => 'rss',
374
- :mem_fragmentation_ratio => 'frag',
375
- :total_commands_processed => 'cmd',
376
- :total_commands_processed_per_second => 'cmd/s',
377
- :expired_keys => 'exp',
378
- :expired_keys_per_second => 'exp/s',
379
- :evicted_keys => 'evt',
380
- :evicted_keys_per_second => 'evt/s',
381
- :keyspace_hits => 'hit',
382
- :keyspace_hits_per_second => 'hit/s',
383
- :keyspace_misses => 'mis',
384
- :keyspace_misses_per_second => 'mis/s',
385
- :aof_current_size => 'aofcs',
386
- :aof_base_size => 'aofbs',
387
- :pubsub_channels => 'psch',
388
- :pubsub_patterns => 'psp',
389
- }
390
314
  end
@@ -0,0 +1,118 @@
1
+ class RedisStat
2
+ DEFAULT_TERM_WIDTH = 180
3
+ DEFAULT_TERM_HEIGHT = 25
4
+ DEFAULT_SERVER_PORT = 63790
5
+ DEFAULT_REDIS_TIMEOUT = 30
6
+
7
+ MEASURES = {
8
+ :static => [
9
+ :redis_version,
10
+ :process_id,
11
+ :uptime_in_seconds,
12
+ :uptime_in_days,
13
+ :gcc_version,
14
+ :role,
15
+ :connected_slaves,
16
+ :aof_enabled,
17
+ :vm_enabled
18
+ ],
19
+ :default => [
20
+ :at,
21
+ :used_cpu_user,
22
+ :used_cpu_sys,
23
+ :connected_clients,
24
+ :blocked_clients,
25
+ :used_memory,
26
+ :used_memory_rss,
27
+ :keys,
28
+ :total_commands_processed_per_second,
29
+ :expired_keys_per_second,
30
+ :evicted_keys_per_second,
31
+ :keyspace_hits_per_second,
32
+ :keyspace_misses_per_second,
33
+ :aof_current_size,
34
+ :pubsub_channels,
35
+ ],
36
+ :verbose => [
37
+ :at,
38
+ :used_cpu_user,
39
+ :used_cpu_sys,
40
+ :connected_clients,
41
+ :blocked_clients,
42
+ :used_memory,
43
+ :used_memory_rss,
44
+ :mem_fragmentation_ratio,
45
+ :keys,
46
+ :total_commands_processed_per_second,
47
+ :total_commands_processed,
48
+ :expired_keys_per_second,
49
+ :expired_keys,
50
+ :evicted_keys_per_second,
51
+ :evicted_keys,
52
+ :keyspace_hits_per_second,
53
+ :keyspace_hits,
54
+ :keyspace_misses_per_second,
55
+ :keyspace_misses,
56
+ :aof_current_size,
57
+ :aof_base_size,
58
+ :changes_since_last_save,
59
+ :pubsub_channels,
60
+ :pubsub_patterns,
61
+ ]
62
+ }
63
+
64
+ COLORS = {
65
+ :at => [:bold],
66
+ :used_cpu_user => [:yellow, :bold],
67
+ :used_cpu_sys => [:yellow],
68
+ :connected_clients => [:cyan, :bold],
69
+ :blocked_clients => [:cyan, :bold],
70
+ :used_memory => [:green],
71
+ :used_memory_rss => [:green],
72
+ :mem_fragmentation_ratio => [:green],
73
+ :keys => [:bold],
74
+ :total_commands_processed => [:blue, :bold],
75
+ :total_commands_processed_per_second => [:blue, :bold],
76
+ :expired_keys => [:red],
77
+ :expired_keys_per_second => [:red],
78
+ :evicted_keys => [:red, :bold],
79
+ :evicted_keys_per_second => [:red, :bold],
80
+ :keyspace_hits => [:magenta, :bold],
81
+ :keyspace_hits_per_second => [:magenta, :bold],
82
+ :keyspace_misses => [:magenta],
83
+ :keyspace_misses_per_second => [:magenta],
84
+ :aof_current_size => [:cyan],
85
+ :aof_base_size => [:cyan],
86
+ :changes_since_last_save => [:green, :bold],
87
+ :pubsub_channels => [:cyan, :bold],
88
+ :pubsub_patterns => [:cyan, :bold],
89
+ }
90
+
91
+ LABELS = {
92
+ :at => 'time',
93
+ :used_cpu_user => 'us',
94
+ :used_cpu_sys => 'sy',
95
+ :connected_clients => 'cl',
96
+ :blocked_clients => 'bcl',
97
+ :used_memory => 'mem',
98
+ :used_memory_rss => 'rss',
99
+ :mem_fragmentation_ratio => 'frag',
100
+ :total_commands_processed => 'cmd',
101
+ :total_commands_processed_per_second => 'cmd/s',
102
+ :expired_keys => 'exp',
103
+ :expired_keys_per_second => 'exp/s',
104
+ :evicted_keys => 'evt',
105
+ :evicted_keys_per_second => 'evt/s',
106
+ :keys => 'keys',
107
+ :keyspace_hits => 'hit',
108
+ :keyspace_hits_per_second => 'hit/s',
109
+ :keyspace_misses => 'mis',
110
+ :keyspace_misses_per_second => 'mis/s',
111
+ :aof_current_size => 'aofcs',
112
+ :aof_base_size => 'aofbs',
113
+ :changes_since_last_save => 'chsv',
114
+ :pubsub_channels => 'psch',
115
+ :pubsub_patterns => 'psp',
116
+ }
117
+ end
118
+