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 +6 -0
- data/lib/redis-stat.rb +100 -86
- data/lib/redis-stat/version.rb +1 -1
- data/redis-stat.gemspec +2 -1
- metadata +21 -5
data/README.md
CHANGED
@@ -73,6 +73,12 @@ killall -9 redis-stat-daemon
|
|
73
73
|
|
74
74
|

|
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
|
|
data/lib/redis-stat.rb
CHANGED
@@ -7,16 +7,23 @@ require 'redis-stat/server'
|
|
7
7
|
require 'insensitive_hash'
|
8
8
|
require 'redis'
|
9
9
|
require 'tabularize'
|
10
|
-
require '
|
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
|
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
|
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
|
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
|
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
|
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
|
180
|
-
|
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
|
-
|
187
|
-
|
188
|
-
|
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
|
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
|
-
|
214
|
+
# [ key, [ humanized, raw ] ]
|
215
|
+
msg = [*pair.last].first
|
216
|
+
colorize msg, *@colors[pair.first]
|
217
217
|
}
|
218
|
-
lines
|
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
|
-
|
224
|
-
|
225
|
-
if
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
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|
|
254
|
+
tab << [nil] + @hosts.map { |h| h.bold.green }
|
256
255
|
tab.separator!
|
257
256
|
@tab_measures.each do |key|
|
258
|
-
tab << [
|
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
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
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
|
-
|
275
|
-
|
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 =
|
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 =
|
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
|
355
|
-
|
356
|
-
|
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
|
data/lib/redis-stat/version.rb
CHANGED
data/redis-stat.gemspec
CHANGED
@@ -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 "
|
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.
|
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-
|
12
|
+
date: 2013-08-27 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
|
-
name:
|
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:
|
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:
|
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
|