redis-stat 0.3.7 → 0.3.8

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
@@ -73,6 +73,12 @@ killall -9 redis-stat-daemon
73
73
 
74
74
  ![Dashboard](https://github.com/junegunn/redis-stat/raw/master/screenshots/redis-stat-web.png)
75
75
 
76
+ ## Windows support
77
+
78
+ If you're running Windows, you can only install redis-stat on
79
+ [JRuby](http://jruby.org/). Notice that fancy terminal colors will not be
80
+ printed as they are not supported in the default Windows command prompt.
81
+
76
82
  ## Author
77
83
  - [Junegunn Choi](https://github.com/junegunn)
78
84
 
@@ -7,16 +7,23 @@ require 'redis-stat/server'
7
7
  require 'insensitive_hash'
8
8
  require 'redis'
9
9
  require 'tabularize'
10
- require 'ansi'
10
+ require 'ansi256'
11
11
  require 'csv'
12
12
  require 'parallelize'
13
13
  require 'si'
14
+ require 'rbconfig'
15
+ require 'lps'
14
16
 
15
17
  class RedisStat
16
18
  attr_reader :hosts, :measures, :tab_measures, :verbose, :interval
17
19
 
18
20
  def initialize options = {}
19
- options = RedisStat::Option::DEFAULT.merge options
21
+ options = RedisStat::Option::DEFAULT.merge options
22
+ windows = RbConfig::CONFIG['target_os'] =~ /mswin|mingw/
23
+
24
+ Ansi256.enabled = STDOUT.tty? && !(windows || options[:mono])
25
+ options[:style] = :ascii if windows
26
+
20
27
  @hosts = options[:hosts]
21
28
  @redises = @hosts.map { |e|
22
29
  host, port = e.split(':')
@@ -24,7 +31,6 @@ class RedisStat
24
31
  }
25
32
  @interval = options[:interval]
26
33
  @max_count = options[:count]
27
- @mono = options[:mono]
28
34
  @colors = options[:colors] || COLORS
29
35
  @csv = options[:csv]
30
36
  @auth = options[:auth]
@@ -34,6 +40,7 @@ class RedisStat
34
40
  @all_measures= MEASURES.values.inject(:+).uniq - [:at]
35
41
  @count = 0
36
42
  @style = options[:style]
43
+ @varwidth = STDOUT.tty? && !windows
37
44
  @first_batch = true
38
45
  @server_port = options[:server_port]
39
46
  @server_thr = nil
@@ -49,24 +56,15 @@ class RedisStat
49
56
  trap('INT') { Thread.main.raise Interrupt }
50
57
 
51
58
  begin
52
- csv = File.open(@csv, 'w') if @csv
59
+ csv = File.open(File.expand_path(@csv), 'w') if @csv
53
60
  update_term_size!
54
-
55
- # Warm-up / authenticate only when needed
56
- @redises.each do |r|
57
- begin
58
- r.info
59
- rescue Redis::CommandError
60
- r.auth @auth if @auth
61
- end
62
- end
63
-
61
+ authenticate!
64
62
 
65
63
  @started_at = Time.now
66
64
  prev_info = nil
67
65
  server = start_server if @server_port
68
66
 
69
- loop do
67
+ LPS.interval(@interval).loop do
70
68
  errs = 0
71
69
  info =
72
70
  begin
@@ -74,12 +72,15 @@ class RedisStat
74
72
  rescue Interrupt
75
73
  raise
76
74
  rescue Exception => e
75
+ if need_auth?(e)
76
+ authenticate!
77
+ retry
78
+ end
79
+
77
80
  errs += 1
78
81
  if server || errs < NUM_RETRIES
79
82
  @os.puts if errs == 1
80
- @os.puts ansi(:red, :bold) {
81
- "#{e} (#{ server ? "#{errs}" : [errs, NUM_RETRIES].join('/') })"
82
- }
83
+ @os.puts "#{e} (#{ server ? "#{errs}" : [errs, NUM_RETRIES].join('/') })".red.bold
83
84
  sleep @interval
84
85
  retry
85
86
  else
@@ -93,25 +94,22 @@ class RedisStat
93
94
 
94
95
  @count += 1
95
96
  break if @max_count && @count >= @max_count
96
- sleep @interval
97
97
  end
98
98
  @os.puts
99
99
  rescue Interrupt
100
100
  @os.puts
101
- @os.puts ansi(:yellow, :bold) { "Interrupted." }
101
+ @os.puts "Interrupted.".yellow.bold
102
102
  if @server_thr
103
103
  @server_thr.raise Interrupt
104
104
  @server_thr.join
105
105
  end
106
106
  rescue Exception => e
107
- @os.puts ansi(:red, :bold) { e.to_s }
107
+ @os.puts e.to_s.red.bold
108
108
  raise
109
109
  ensure
110
110
  csv.close if csv
111
111
  end
112
- @os.puts ansi(:blue, :bold) {
113
- "Elapsed: #{"%.2f" % (Time.now - @started_at)} sec."
114
- }
112
+ @os.puts "Elapsed: #{"%.2f" % (Time.now - @started_at)} sec.".blue.bold
115
113
  end
116
114
 
117
115
  private
@@ -153,6 +151,7 @@ private
153
151
  begin
154
152
  case JRUBY_VERSION
155
153
  when /^1\.7/
154
+ # TODO Currently Windows terminal is not supported: always returns 80x24
156
155
  @term ||= Java::jline.console.ConsoleReader.new.getTerminal
157
156
  @term_width = (@term.width rescue DEFAULT_TERM_WIDTH)
158
157
  @term_height = (@term.height rescue DEFAULT_TERM_HEIGHT) - 4
@@ -173,25 +172,29 @@ private
173
172
  end
174
173
 
175
174
  def move! lines
176
- return if lines == 0
175
+ return if lines == 0 || !@varwidth
177
176
 
178
177
  @os.print(
179
- if defined?(Win32::Console)
180
- if lines < 0
181
- "\e[#{- lines}F"
182
- else
183
- "\e[#{lines}E"
184
- end
178
+ if lines < 0
179
+ "\e[#{- lines}A\e[0G"
185
180
  else
186
- if lines < 0
187
- "\e[#{- lines}A\e[0G"
188
- else
189
- "\e[#{lines}B\e[0G"
190
- end
191
- end)
181
+ "\e[#{lines}B\e[0G"
182
+ end
183
+ )
192
184
  end
193
185
 
194
- def output info, info_output, file
186
+ def output_file info_output, file
187
+ file.puts CSV.generate_line(info_output.map { |pair|
188
+ LABELS[pair.first] || pair.first
189
+ }) if @count == 0
190
+
191
+ file.puts CSV.generate_line(info_output.map { |pair|
192
+ [*pair.last].last
193
+ })
194
+ file.flush
195
+ end
196
+
197
+ def output_term info_output
195
198
  @table ||= init_table info_output
196
199
 
197
200
  movement = nil
@@ -199,11 +202,6 @@ private
199
202
  output_static_info info
200
203
 
201
204
  movement = 0
202
- if file
203
- file.puts CSV.generate_line(info_output.map { |pair|
204
- LABELS[pair.first] || pair.first
205
- })
206
- end
207
205
  elsif @count % @term_height == 0
208
206
  @first_batch = false
209
207
  movement = -1
@@ -213,33 +211,34 @@ private
213
211
 
214
212
  # Build output table
215
213
  @table << info_output.map { |pair|
216
- ansi(*@colors[pair.first]) { [*pair.last].first }
214
+ # [ key, [ humanized, raw ] ]
215
+ msg = [*pair.last].first
216
+ colorize msg, *@colors[pair.first]
217
217
  }
218
- lines = @table.to_s.lines.map(&:chomp)
218
+ lines = @table.to_s.lines.map(&:chomp)
219
219
  lines.delete_at @first_batch ? 1 : 0
220
220
  width = lines.first.length
221
221
  height = lines.length
222
222
 
223
- # Calculate the number of lines to go upward
224
- if movement.nil?
225
- if @prev_width && @prev_width == width
226
- lines = lines[-2..-1]
227
- movement = -1
228
- else
229
- movement = -(height - 1)
223
+ if @varwidth
224
+ # Calculate the number of lines to go upward
225
+ if movement.nil?
226
+ if @prev_width && @prev_width == width
227
+ lines = lines[-2..-1]
228
+ movement = -1
229
+ else
230
+ movement = -(height - 1)
231
+ end
230
232
  end
233
+ else
234
+ lines = movement ? lines[0..-2] : lines[-2..-2]
231
235
  end
232
236
  @prev_width = width
233
237
 
234
238
  move! movement
235
239
  begin
236
240
  @os.print $/ + lines.join($/)
237
-
238
- if file
239
- file.puts CSV.generate_line(info_output.map { |pair|
240
- [*pair.last].last
241
- })
242
- end
241
+ @os.flush
243
242
  rescue Interrupt
244
243
  move! -movement
245
244
  raise
@@ -252,28 +251,33 @@ private
252
251
  :border_style => @style,
253
252
  :screen_width => @term_width
254
253
  )
255
- tab << [nil] + @hosts.map { |h| ansi(:bold, :green) { h } }
254
+ tab << [nil] + @hosts.map { |h| h.bold.green }
256
255
  tab.separator!
257
256
  @tab_measures.each do |key|
258
- tab << [ansi(:bold) { key }] + info[key] unless info[key].compact.empty?
257
+ tab << [key.to_s.bold] + info[key] unless info[key].compact.empty?
259
258
  end
260
259
  @os.puts tab
261
260
  end
262
261
 
262
+ def output info, info_output, file
263
+ output_term info_output
264
+ output_file info_output, file if file
265
+ end
266
+
263
267
  def init_table info_output
264
- table = Tabularize.new :unicode => false,
265
- :align => :right,
266
- :border_style => @style,
267
- :border_color => @mono ? nil : ANSI::Code.red,
268
- :vborder => ' ',
269
- :pad_left => 0,
270
- :pad_right => 0,
271
- :screen_width => @term_width
268
+ table = Tabularize.new(
269
+ :unicode => false,
270
+ :align => :right,
271
+ :border_style => @style,
272
+ :border_color => (Ansi256.enabled? ? Ansi256.red : nil),
273
+ :vborder => ' ',
274
+ :pad_left => 0,
275
+ :pad_right => 0,
276
+ :screen_width => @term_width)
272
277
  table.separator!
273
278
  table << info_output.map { |pair|
274
- ansi(*((@colors[pair.first] || []) + [:underline])) {
275
- LABELS[pair.first] || pair.first
276
- }
279
+ key = pair.first
280
+ colorize LABELS.fetch(key, key), :underline, *@colors[key]
277
281
  }
278
282
  table.separator!
279
283
  table
@@ -281,6 +285,7 @@ private
281
285
 
282
286
  def process info, prev_info
283
287
  @measures.map { |key|
288
+ # [ key, [humanized, raw] ]
284
289
  [ key, process_how(info, prev_info, key) ]
285
290
  }.select { |pair| pair.last }
286
291
  end
@@ -296,14 +301,6 @@ private
296
301
  end
297
302
  end
298
303
 
299
- get_ratio = lambda do |x, y|
300
- if x > 0 || y > 0
301
- x / (x + y) * 100
302
- else
303
- nil
304
- end
305
- end
306
-
307
304
  case key
308
305
  when :at
309
306
  val = Time.now.strftime('%H:%M:%S')
@@ -327,12 +324,12 @@ private
327
324
  when :keyspace_hit_ratio
328
325
  hits = info.sumf(:keyspace_hits)
329
326
  misses = info.sumf(:keyspace_misses)
330
- val = get_ratio.call(hits, misses)
327
+ val = ratio(hits, misses)
331
328
  [humanize_number(val), val]
332
329
  when :keyspace_hit_ratio_per_second
333
330
  hits = get_diff.call(:keyspace_hits) || 0
334
331
  misses = get_diff.call(:keyspace_misses) || 0
335
- val = get_ratio.call(hits, misses)
332
+ val = ratio(hits, misses)
336
333
  [humanize_number(val), val]
337
334
  else
338
335
  val = info.sumf(key)
@@ -340,6 +337,14 @@ private
340
337
  end
341
338
  end
342
339
 
340
+ def ratio x, y
341
+ if x > 0 || y > 0
342
+ x / (x + y) * 100
343
+ else
344
+ nil
345
+ end
346
+ end
347
+
343
348
  def humanize_number num, byte = false
344
349
  return '-' if num.nil?
345
350
 
@@ -351,11 +356,20 @@ private
351
356
  end
352
357
  end
353
358
 
354
- def ansi *args, &block
355
- if @mono || args.empty?
356
- block.call
357
- else
358
- ANSI::Code.ansi *args, &block
359
+ def colorize str, *colors
360
+ colors.each do |color|
361
+ str = str.send color
359
362
  end
363
+ str
364
+ end
365
+
366
+ def need_auth? e
367
+ @auth && e.is_a?(Redis::CommandError) && e.to_s =~ /operation not permitted/
368
+ end
369
+
370
+ def authenticate!
371
+ @redises.each do |r|
372
+ r.ping rescue (r.auth @auth)
373
+ end if @auth
360
374
  end
361
375
  end
@@ -1,3 +1,3 @@
1
1
  class RedisStat
2
- VERSION = "0.3.7"
2
+ VERSION = "0.3.8"
3
3
  end
@@ -17,7 +17,7 @@ Gem::Specification.new do |gem|
17
17
  gem.require_paths = ["lib"]
18
18
  gem.version = RedisStat::VERSION
19
19
 
20
- gem.add_runtime_dependency "ansi", '~> 1.4.3'
20
+ gem.add_runtime_dependency "ansi256", '~> 0.2.5'
21
21
  gem.add_runtime_dependency "redis", '~> 3.0.2'
22
22
  gem.add_runtime_dependency "tabularize", '~> 0.2.9'
23
23
  gem.add_runtime_dependency "insensitive_hash", '~> 0.3.0'
@@ -25,6 +25,7 @@ Gem::Specification.new do |gem|
25
25
  gem.add_runtime_dependency "si", '~> 0.1.4'
26
26
  gem.add_runtime_dependency "sinatra", '~> 1.3.3'
27
27
  gem.add_runtime_dependency "json", '~> 1.7.5'
28
+ gem.add_runtime_dependency "lps", '~> 0.2.0'
28
29
 
29
30
  if RUBY_PLATFORM == 'java'
30
31
  gem.add_runtime_dependency "puma", '~> 2.3.2'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redis-stat
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.7
4
+ version: 0.3.8
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,16 +9,16 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-08-22 00:00:00.000000000 Z
12
+ date: 2013-08-27 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- name: ansi
15
+ name: ansi256
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
20
20
  - !ruby/object:Gem::Version
21
- version: 1.4.3
21
+ version: 0.2.5
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
@@ -26,7 +26,7 @@ dependencies:
26
26
  requirements:
27
27
  - - ~>
28
28
  - !ruby/object:Gem::Version
29
- version: 1.4.3
29
+ version: 0.2.5
30
30
  - !ruby/object:Gem::Dependency
31
31
  name: redis
32
32
  requirement: !ruby/object:Gem::Requirement
@@ -139,6 +139,22 @@ dependencies:
139
139
  - - ~>
140
140
  - !ruby/object:Gem::Version
141
141
  version: 1.7.5
142
+ - !ruby/object:Gem::Dependency
143
+ name: lps
144
+ requirement: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ~>
148
+ - !ruby/object:Gem::Version
149
+ version: 0.2.0
150
+ type: :runtime
151
+ prerelease: false
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ~>
156
+ - !ruby/object:Gem::Version
157
+ version: 0.2.0
142
158
  - !ruby/object:Gem::Dependency
143
159
  name: thin
144
160
  requirement: !ruby/object:Gem::Requirement