sidekiq-spy 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7a20cacb912beed5baf1c80012a87e5115dae2bf
4
+ data.tar.gz: 1db3848aa6af319f5037ee72dc6720683735d53d
5
+ SHA512:
6
+ metadata.gz: ed49f93281287bbc32d5a5b7629a85d5489e50e35bbae88e16369e764644d87f697593c610a5e149e65d9a4aff8041dc902b2901782d4b2e061e7930e8bc0cef
7
+ data.tar.gz: d16b91fb061442fb4ec76ed66e415cb10e3ab5776bab6b7c77e5893c88d666072c09559b35870b222151a68005a22f5332be5e57b9aad0a7ce06a014d54a38ed
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ /*.gem
2
+ /*.rbc
3
+ /.bundle/
4
+ /.config
5
+ /.yardoc/
6
+ /Gemfile.lock
7
+ /InstalledFiles
8
+ /_yardoc/
9
+ /coverage/
10
+ /doc/
11
+ /lib/bundler/man/
12
+ /pkg/
13
+ /rdoc/
14
+ /spec/reports/
15
+ /test/tmp/
16
+ /test/version_tmp/
17
+ /tmp/
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ sidekiq-spy
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-2.0.0
data/.travis.yml ADDED
@@ -0,0 +1,16 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - jruby-19mode
5
+ - rbx-19mode
6
+ - 2.0.0
7
+ notifications:
8
+ email:
9
+ recipients:
10
+ - tp@tiredpixel.com
11
+ - sidekiq.spy@librelist.com
12
+ matrix:
13
+ allow_failures:
14
+ - rvm: 1.9.3
15
+ - rvm: jruby-19mode
16
+ - rvm: rbx-19mode
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in sidekiq-spy.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 tiredpixel
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,119 @@
1
+ # Sidekiq Spy
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/sidekiq-spy.png)](http://badge.fury.io/rb/sidekiq-spy)
4
+ [![Build Status](https://travis-ci.org/tiredpixel/sidekiq-spy.png?branch=master,develop)](https://travis-ci.org/tiredpixel/sidekiq-spy)
5
+ [![Code Climate](https://codeclimate.com/github/tiredpixel/sidekiq-spy.png)](https://codeclimate.com/github/tiredpixel/sidekiq-spy)
6
+ [![Coverage Status](https://coveralls.io/repos/tiredpixel/sidekiq-spy/badge.png?branch=master,develop)](https://coveralls.io/r/tiredpixel/sidekiq-spy)
7
+
8
+ [Sidekiq](https://github.com/mperham/sidekiq) monitoring in the console.
9
+ A bit like Sidekiq::Web. But without the web.
10
+
11
+ So, Sidekiq is a beautiful thing. :) But if you know the sadness of wanting to
12
+ see how your workers are doing at-a-glance when SSHed into a remote box with
13
+ no web server, this one's for you. <3
14
+
15
+ This project is so hot out of the oven you might need mitts. But it's already
16
+ functional, with the main statistics from the Sidekiq::Web homepage. In time,
17
+ it would be nice to add the Workers, Queues, Retries, and Scheduled tabs, too.
18
+
19
+ More sleep lost by [tiredpixel](http://www.tiredpixel.com).
20
+
21
+
22
+ ## Installation
23
+
24
+ Install using:
25
+
26
+ $ gem install sidekiq-spy
27
+
28
+
29
+ ## Usage
30
+
31
+ View the available options:
32
+
33
+ $ sidekiq-spy --help
34
+
35
+ If you're connecting to `redis://127.0.0.1:6379/0` with no namespace you can
36
+ simply:
37
+
38
+ $ sidekiq-spy
39
+
40
+ To use a connection string URL:
41
+
42
+ $ sidekiq-spy -u redis://da.example.com:237/42
43
+
44
+ Or, if you prefer your options like your Hi-Fi:
45
+
46
+ $ sidekiq-spy -h da.example.com -p 237 -d 42
47
+
48
+ Maybe you're using [Resque](https://github.com/resque/resque)? Sssh!
49
+ We won't tell anyone! ;) (Resque is awesome, too!)
50
+
51
+ $ sidekiq-spy -n resque
52
+
53
+ That's about it.
54
+
55
+
56
+ ## ASCII Art (a.k.a. Screenshot)
57
+
58
+ Sidekiq Spy 0.0.1 | 01:02:58 +0100
59
+ redis: 127.0.0.1:6379/0|namespace:
60
+ redis version: 2.6.11|uptime (d): 8|connections: 3
61
+ memory: 4.84M|memory peak: 13.52M|
62
+
63
+ busy: 0|retries: 0|processed: 1304
64
+ enqueued: 0|scheduled: 0|failed: 1236
65
+
66
+
67
+ ## Stay Tuned
68
+
69
+ We have a [Librelist](http://librelist.com) mailing list!
70
+ To subscribe, send an email to <sidekiq.spy@librelist.com>.
71
+ To unsubscribe, send an email to <sidekiq.spy-unsubscribe@librelist.com>.
72
+ There be [archives](http://librelist.com/browser/sidekiq.spy/).
73
+ That was easy.
74
+
75
+
76
+ ## Growing Like Flowers
77
+
78
+ Dear Me, Here is a vague wishlist:
79
+
80
+ - more tests for the display parts
81
+ - Workers table on main page
82
+ - Queues page
83
+ - Retries page
84
+ - Scheduled page
85
+ - a little control to go with your monitoring, maybe...
86
+
87
+
88
+ ## Contributions
89
+
90
+ Contributions are embraced with much love and affection!
91
+ Please fork the repository and wizard your magic, preferably with plenty of
92
+ fairy-dust sprinkled over the tests. ;)
93
+ Then send me a pull request. Simples!
94
+ If you'd like to discuss what you're doing or planning to do, or if you get
95
+ stuck on something, then just wave. :)
96
+
97
+ Do whatever makes you happy. We'll probably still like you. :)
98
+
99
+ Tests are written using [minitest](https://github.com/seattlerb/minitest),
100
+ which is included by default in Ruby 1.9 onwards. To run all tests:
101
+
102
+ rake test
103
+
104
+ Or, if you're of that turn of mind, use [TURN](https://github.com/TwP/turn)
105
+ (`gem install turn`):
106
+
107
+ turn test/sidekiq-spy/
108
+
109
+
110
+ ## Blessing
111
+
112
+ May you find peace, and help others to do likewise.
113
+
114
+
115
+ ## Licence
116
+
117
+ © [tiredpixel](http://www.tiredpixel.com) 2013.
118
+ It is free software, released under the MIT License, and may be redistributed
119
+ under the terms specified in `LICENSE`.
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs.push 'lib'
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ t.verbose = true
9
+ end
10
+
11
+ task :default => 'test'
data/bin/sidekiq-spy ADDED
@@ -0,0 +1,99 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $stdout.sync = true
4
+
5
+ require 'optparse'
6
+
7
+ require File.expand_path('../../lib/sidekiq-spy', __FILE__)
8
+
9
+
10
+ # = Parse opts
11
+
12
+ options = {}
13
+
14
+ OptionParser.new do |opts|
15
+ opts.version = "v#{SidekiqSpy::VERSION}"
16
+
17
+ opts.banner = opts.ver
18
+
19
+ opts.separator ""
20
+ opts.separator "Usage: sidekiq-spy [OPTIONS]"
21
+
22
+ opts.on("-u", "--url URL",
23
+ "Redis connection string URL"
24
+ ) { |o| options[:url] = o }
25
+
26
+ opts.on("-h", "--host HOSTNAME",
27
+ "Redis hostname (default: 127.0.0.1)"
28
+ ) { |o| options[:host] = o }
29
+
30
+ opts.on("-p", "--port PORT", Integer,
31
+ "Redis port (default: 6379)"
32
+ ) { |o| options[:port] = o }
33
+
34
+ opts.on("-d", "--database DATABASE", Integer,
35
+ "Redis database (default: 0)"
36
+ ) { |o| options[:database] = o }
37
+
38
+ opts.on("-n", "--namespace NAMESPACE",
39
+ "Redis namespace (default: '')"
40
+ ) { |o| options[:namespace] = o }
41
+
42
+ opts.on("-i", "--interval INTERVAL", Integer,
43
+ "Refresh every INTERVAL s (default: 5)"
44
+ ) { |o| options[:interval] = o }
45
+
46
+ opts.separator ""
47
+ opts.separator "Examples:"
48
+
49
+ opts.separator [
50
+ "sidekiq-spy",
51
+ "sidekiq-spy -u redis://da.example.com:237/42",
52
+ "sidekiq-spy -h da.example.com -p 237 -d 42",
53
+ "sidekiq-spy -n resque -i 1",
54
+ ].map { |e| " #{e}" }
55
+
56
+ opts.separator ""
57
+
58
+ opts.on_tail("--help",
59
+ "Output (this) help and exit"
60
+ ) do
61
+ puts opts
62
+ exit
63
+ end
64
+
65
+ opts.on_tail("--version",
66
+ "Output version and exit"
67
+ ) do
68
+ puts opts.ver
69
+ exit
70
+ end
71
+ end.parse!
72
+
73
+
74
+ # = Create app
75
+
76
+ @app = SidekiqSpy::App.new
77
+
78
+
79
+ # = Configure
80
+
81
+ @app.configure do |c|
82
+ params = [
83
+ :url,
84
+ :host,
85
+ :port,
86
+ :database,
87
+ :namespace,
88
+ :interval,
89
+ ]
90
+
91
+ params.each { |p| c.send("#{p}=", options[p]) unless options[p].nil? }
92
+ end
93
+
94
+
95
+ # = Run app
96
+
97
+ trap('INT') { @app.stop }
98
+
99
+ @app.start
@@ -0,0 +1,11 @@
1
+ require File.expand_path('../sidekiq-spy/version', __FILE__)
2
+ require File.expand_path('../sidekiq-spy/config', __FILE__)
3
+
4
+ require File.expand_path('../sidekiq-spy/translatable', __FILE__)
5
+ require File.expand_path('../sidekiq-spy/app', __FILE__)
6
+
7
+ require File.expand_path('../sidekiq-spy/spy/stats', __FILE__)
8
+
9
+ require File.expand_path('../sidekiq-spy/display/screen', __FILE__)
10
+ require File.expand_path('../sidekiq-spy/display/panel', __FILE__)
11
+ require File.expand_path('../sidekiq-spy/display/subpanel', __FILE__)
@@ -0,0 +1,74 @@
1
+ require 'sidekiq'
2
+
3
+
4
+ module SidekiqSpy
5
+ class App
6
+
7
+ attr_reader :running
8
+
9
+ def initialize
10
+ @running = false
11
+ end
12
+
13
+ def config
14
+ @config ||= Config.new
15
+ end
16
+
17
+ def configure
18
+ yield config
19
+
20
+ configure_sidekiq
21
+ end
22
+
23
+ def start
24
+ begin
25
+ @running = true
26
+
27
+ setup
28
+
29
+ while @running do
30
+ refresh
31
+
32
+ @sleep_timer = config.interval
33
+
34
+ while @running && @sleep_timer > 0
35
+ sleep 1
36
+
37
+ @sleep_timer -= 1
38
+ end
39
+ end
40
+ ensure
41
+ cleanup
42
+ end
43
+ end
44
+
45
+ def stop
46
+ @running = false
47
+ end
48
+
49
+ private
50
+
51
+ def configure_sidekiq
52
+ Sidekiq.configure_client do |sidekiq_config|
53
+ sidekiq_config.logger = nil
54
+ sidekiq_config.redis = {
55
+ :url => config.url,
56
+ :namespace => config.namespace,
57
+ }
58
+ end
59
+ end
60
+
61
+ def setup
62
+ @screen = Display::Screen.new
63
+ end
64
+
65
+ def refresh
66
+ @screen.refresh
67
+ end
68
+
69
+ def cleanup
70
+ @screen.close if @screen
71
+ end
72
+
73
+ end
74
+ end
@@ -0,0 +1,34 @@
1
+ require 'uri'
2
+
3
+
4
+ module SidekiqSpy
5
+ class Config
6
+
7
+ attr_accessor :host
8
+ attr_accessor :port
9
+ attr_accessor :database
10
+ attr_accessor :namespace
11
+ attr_accessor :interval
12
+
13
+ def initialize
14
+ @host = '127.0.0.1'
15
+ @port = 6379
16
+ @database = 0
17
+ @namespace = nil
18
+ @interval = 5
19
+ end
20
+
21
+ def url=(url)
22
+ url = URI.parse(url)
23
+
24
+ @host = url.host unless url.host.empty?
25
+ @port = url.port unless url.path.empty?
26
+ @database = url.path.tr('/', '').to_i unless url.path.empty?
27
+ end
28
+
29
+ def url
30
+ "redis://#{@host}:#{@port}/#{@database}"
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,81 @@
1
+ require 'curses'
2
+
3
+
4
+ module SidekiqSpy
5
+ module Display
6
+ class Panel
7
+
8
+ def initialize(height, width, top, left, structure, opts = {})
9
+ @height = height
10
+ @width = width
11
+ @top = top
12
+ @left = left
13
+
14
+ @dividers = {
15
+ :left => opts[:divider_l].to_s,
16
+ :right => opts[:divider_r].to_s,
17
+ }
18
+
19
+ @divider_length = @dividers.values.map(&:length).inject(:+)
20
+
21
+ @window = Curses::Window.new(@height, @width, @top, @left)
22
+
23
+ @subpanels = build_subpanels(structure)
24
+ end
25
+
26
+ def close
27
+ @window.close
28
+ end
29
+
30
+ def refresh
31
+ @subpanels.each(&:refresh) # build changes
32
+
33
+ @window.refresh # push changes to window
34
+ end
35
+
36
+ private
37
+
38
+ def build_subpanels(structure)
39
+ subpanels = []
40
+
41
+ structure.each_with_index do |row, i|
42
+ next if row.nil? # skip blank rows
43
+
44
+ row_cols = row.map { |e| e[0] }.inject(:+)
45
+
46
+ col_width = (@width / row_cols) + @divider_length
47
+
48
+ col_i = 0
49
+
50
+ row.each_with_index do |(tag_cols, data_l, data_r), row_i|
51
+ first_col = row_i == 0
52
+ last_col = row_i + 1 == row.length
53
+
54
+ tag_col_width = if last_col
55
+ @width - ((row_cols - tag_cols) * col_width)
56
+ else
57
+ tag_cols * col_width
58
+ end
59
+
60
+ subpanels << Display::Subpanel.new(
61
+ @window,
62
+ 1,
63
+ tag_col_width,
64
+ i,
65
+ col_i,
66
+ :data_l => data_l,
67
+ :data_r => data_r,
68
+ :divider_l => (@dividers[:left] unless first_col),
69
+ :divider_r => (@dividers[:right] unless last_col)
70
+ )
71
+
72
+ col_i += tag_col_width
73
+ end
74
+ end
75
+
76
+ subpanels
77
+ end
78
+
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,82 @@
1
+ require 'curses'
2
+
3
+
4
+ module SidekiqSpy
5
+ module Display
6
+ class Screen
7
+
8
+ include Translatable
9
+
10
+ def initialize
11
+ Curses.init_screen
12
+ Curses.nl
13
+ Curses.noecho
14
+ Curses.curs_set(0)
15
+
16
+ @height = Curses.lines
17
+ @width = Curses.cols
18
+
19
+ @panels = {
20
+ :header => panel_header,
21
+ }
22
+ end
23
+
24
+ def close
25
+ @panels.each { |pname, panel| panel.close }
26
+
27
+ Curses.close_screen
28
+ end
29
+
30
+ def refresh
31
+ @panels.each { |pname, panel| panel.refresh }
32
+ end
33
+
34
+ private
35
+
36
+ def panel_header
37
+ stats = Spy::Stats.new
38
+
39
+ structure = [
40
+ [
41
+ [1, t[:program], nil],
42
+ [1, nil, -> { Time.now.strftime("%T %z") }],
43
+ ],
44
+ [
45
+ [2, t[:redis][:connection], -> { stats.connection }],
46
+ [1, t[:redis][:namespace], -> { stats.namespace }],
47
+ ],
48
+ [
49
+ [1, t[:redis][:version], -> { stats.redis_version }],
50
+ [1, t[:redis][:uptime], -> { stats.uptime }],
51
+ [1, t[:redis][:connections], -> { stats.connections }],
52
+ ],
53
+ [
54
+ [1, t[:redis][:memory], -> { stats.memory }],
55
+ [1, t[:redis][:memory_peak], -> { stats.memory_peak }],
56
+ [1, nil, nil],
57
+ ],
58
+ nil,
59
+ [
60
+ [1, t[:sidekiq][:busy], -> { stats.busy }],
61
+ [1, t[:sidekiq][:retries], -> { stats.retries }],
62
+ [1, t[:sidekiq][:processed], -> { stats.processed }],
63
+ ],
64
+ [
65
+ [1, t[:sidekiq][:enqueued], -> { stats.enqueued }],
66
+ [1, t[:sidekiq][:scheduled], -> { stats.scheduled }],
67
+ [1, t[:sidekiq][:failed], -> { stats.failed }],
68
+ ],
69
+ nil,
70
+ ]
71
+
72
+ opts = {
73
+ :divider_l => t[:divider][:left],
74
+ :divider_r => t[:divider][:right],
75
+ }
76
+
77
+ Display::Panel.new(structure.length, @width, 0, 0, structure, opts)
78
+ end
79
+
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,47 @@
1
+ module SidekiqSpy
2
+ module Display
3
+ class Subpanel
4
+
5
+ def initialize(window, height, width, top, left, opts = {})
6
+ @window = window
7
+ @height = height
8
+ @width = width
9
+ @top = top
10
+ @left = left
11
+
12
+ @data = {
13
+ :left => opts[:data_l],
14
+ :right => opts[:data_r],
15
+ }
16
+
17
+ @dividers = {
18
+ :left => opts[:divider_l].to_s,
19
+ :right => opts[:divider_r].to_s,
20
+ }
21
+
22
+ @content_width = @width - @dividers.values.map(&:length).inject(:+)
23
+ end
24
+
25
+ def refresh
26
+ new_current = content(@data[:left], @data[:right], @content_width)
27
+
28
+ unless new_current == @data[:current] # don't redraw if hasn't changed
29
+ @window.setpos(@top, @left)
30
+ @window.addstr(@dividers[:left] + new_current + @dividers[:right])
31
+ end
32
+
33
+ @data[:current] = new_current
34
+ end
35
+
36
+ private
37
+
38
+ def content(data_l, data_r, width)
39
+ l = data_l.is_a?(Proc) ? data_l.call : data_l.to_s
40
+ r = data_r.is_a?(Proc) ? data_r.call : data_r.to_s
41
+
42
+ l + "%#{width - l.length}s" % r
43
+ end
44
+
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,73 @@
1
+ require 'sidekiq'
2
+
3
+
4
+ module SidekiqSpy
5
+ module Spy
6
+ class Stats
7
+
8
+ def initialize
9
+ @stats = Sidekiq::Stats.new
10
+ @workers = Sidekiq::Workers.new
11
+ end
12
+
13
+ def connection
14
+ redis { |c| "#{c.client.location}/#{c.client.db}" }
15
+ end
16
+
17
+ def namespace
18
+ redis { |c| c.namespace if c.respond_to?(:namespace) }
19
+ end
20
+
21
+ def redis_version
22
+ redis { |c| c.info['redis_version'] }
23
+ end
24
+
25
+ def uptime
26
+ redis { |c| c.info['uptime_in_days'] }
27
+ end
28
+
29
+ def connections
30
+ redis { |c| c.info['connected_clients'] }
31
+ end
32
+
33
+ def memory
34
+ redis { |c| c.info['used_memory_human'] }
35
+ end
36
+
37
+ def memory_peak
38
+ redis { |c| c.info['used_memory_peak_human'] }
39
+ end
40
+
41
+ def busy
42
+ @workers.size
43
+ end
44
+
45
+ def enqueued
46
+ @stats.enqueued
47
+ end
48
+
49
+ def retries
50
+ @stats.retry_size
51
+ end
52
+
53
+ def scheduled
54
+ @stats.scheduled_size
55
+ end
56
+
57
+ def processed
58
+ @stats.processed
59
+ end
60
+
61
+ def failed
62
+ @stats.failed
63
+ end
64
+
65
+ # private
66
+
67
+ def redis
68
+ Sidekiq.redis { |c| yield c }
69
+ end
70
+
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,32 @@
1
+ module SidekiqSpy
2
+ module Translatable
3
+
4
+ def t
5
+ {
6
+ divider: {
7
+ left: "",
8
+ right: "|",
9
+ },
10
+ program: "Sidekiq Spy #{VERSION}",
11
+ redis: {
12
+ connection: "redis:",
13
+ namespace: "namespace:",
14
+ version: "redis version:",
15
+ uptime: "uptime (d):",
16
+ connections: "connections:",
17
+ memory: "memory:",
18
+ memory_peak: "memory peak:",
19
+ },
20
+ sidekiq: {
21
+ busy: "busy:",
22
+ retries: "retries:",
23
+ processed: "processed:",
24
+ enqueued: "enqueued:",
25
+ scheduled: "scheduled:",
26
+ failed: "failed:",
27
+ },
28
+ }
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,5 @@
1
+ module SidekiqSpy
2
+
3
+ VERSION = '0.1.0'
4
+
5
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'sidekiq-spy/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "sidekiq-spy"
8
+ spec.version = SidekiqSpy::VERSION
9
+ spec.authors = ["tiredpixel"]
10
+ spec.email = ["tp@tiredpixel.com"]
11
+ spec.description = %q{Sidekiq monitoring in the console. A bit like Sidekiq::Web. But without the web.}
12
+ spec.summary = %q{Sidekiq monitoring in the console.}
13
+ spec.homepage = "https://github.com/tiredpixel/sidekiq-spy"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "sidekiq", "~> 2.15"
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.3"
24
+ spec.add_development_dependency "rake"
25
+
26
+ spec.add_development_dependency 'coveralls'
27
+ end
@@ -0,0 +1,61 @@
1
+ {
2
+ "redis_version":"666.6.11",
3
+ "redis_git_sha1":"00000000",
4
+ "redis_git_dirty":"0",
5
+ "redis_mode":"standalone",
6
+ "os":"Darwin 12.5.0 x86_64",
7
+ "arch_bits":"64",
8
+ "multiplexing_api":"kqueue",
9
+ "gcc_version":"4.2.1",
10
+ "process_id":"339",
11
+ "run_id":"9b69467d5190215ef1ce1f8a0c78818c3cf54576",
12
+ "tcp_port":"6379",
13
+ "uptime_in_seconds":"648366",
14
+ "uptime_in_days":"7",
15
+ "hz":"10",
16
+ "lru_clock":"1852328",
17
+ "connected_clients":"8",
18
+ "client_longest_output_list":"0",
19
+ "client_biggest_input_buf":"0",
20
+ "blocked_clients":"1",
21
+ "used_memory":"5406656",
22
+ "used_memory_human":"5.16M",
23
+ "used_memory_rss":"643072",
24
+ "used_memory_peak":"5388592",
25
+ "used_memory_peak_human":"5.14M",
26
+ "used_memory_lua":"31744",
27
+ "mem_fragmentation_ratio":"0.12",
28
+ "mem_allocator":"libc",
29
+ "loading":"0",
30
+ "rdb_changes_since_last_save":"0",
31
+ "rdb_bgsave_in_progress":"0",
32
+ "rdb_last_save_time":"1381023714",
33
+ "rdb_last_bgsave_status":"ok",
34
+ "rdb_last_bgsave_time_sec":"-1",
35
+ "rdb_current_bgsave_time_sec":"-1",
36
+ "aof_enabled":"0",
37
+ "aof_rewrite_in_progress":"0",
38
+ "aof_rewrite_scheduled":"0",
39
+ "aof_last_rewrite_time_sec":"-1",
40
+ "aof_current_rewrite_time_sec":"-1",
41
+ "aof_last_bgrewrite_status":"ok",
42
+ "total_connections_received":"18",
43
+ "total_commands_processed":"110742",
44
+ "instantaneous_ops_per_sec":"5",
45
+ "rejected_connections":"0",
46
+ "expired_keys":"0",
47
+ "evicted_keys":"0",
48
+ "keyspace_hits":"21487",
49
+ "keyspace_misses":"59742",
50
+ "pubsub_channels":"0",
51
+ "pubsub_patterns":"0",
52
+ "latest_fork_usec":"0",
53
+ "role":"master",
54
+ "connected_slaves":"0",
55
+ "used_cpu_sys":"57.24",
56
+ "used_cpu_user":"47.85",
57
+ "used_cpu_sys_children":"0.00",
58
+ "used_cpu_user_children":"0.00",
59
+ "db0":"keys=49,expires=3",
60
+ "db1":"keys=3,expires=0"
61
+ }
data/test/helper.rb ADDED
@@ -0,0 +1,6 @@
1
+ require 'coveralls'
2
+ Coveralls.wear! do
3
+ add_filter "/test/sidekiq-spy/"
4
+ end
5
+
6
+ require 'minitest/autorun'
@@ -0,0 +1,60 @@
1
+ require File.expand_path('../../helper', __FILE__)
2
+
3
+ require File.expand_path('../../../lib/sidekiq-spy', __FILE__)
4
+
5
+
6
+ describe SidekiqSpy::App do
7
+
8
+ before do
9
+ @app = SidekiqSpy::App.new
10
+ end
11
+
12
+ it "sets status not-running" do
13
+ @app.running.must_equal false
14
+ end
15
+
16
+ describe "#start" do
17
+ before do
18
+ @app.configure do |c|
19
+ c.interval = 10
20
+ end
21
+ end
22
+
23
+ it "sets status running within 1s" do
24
+ thread_app = Thread.new { @app.start }
25
+
26
+ sleep 1 # patience, patience; give app time to start
27
+
28
+ @app.running.must_equal true
29
+
30
+ Thread.kill(thread_app)
31
+ end
32
+
33
+ it "stops running within 2s" do
34
+ thread_app = Thread.new { @app.start }
35
+
36
+ sleep 1 # patience, patience; give app time to start
37
+
38
+ @app.stop; t0 = Time.now
39
+
40
+ thread_app.join(3)
41
+
42
+ Thread.kill(thread_app)
43
+
44
+ assert_operator (Time.now - t0), :<=, 2
45
+ end
46
+ end
47
+
48
+ describe "#stop" do
49
+ before do
50
+ @app.instance_variable_set(:@running, true)
51
+ end
52
+
53
+ it "sets status not-running" do
54
+ @app.stop
55
+
56
+ @app.running.must_equal false
57
+ end
58
+ end
59
+
60
+ end
@@ -0,0 +1,140 @@
1
+ require File.expand_path('../../helper', __FILE__)
2
+
3
+ require File.expand_path('../../../lib/sidekiq-spy/config', __FILE__)
4
+ require File.expand_path('../../../lib/sidekiq-spy/app', __FILE__)
5
+
6
+
7
+ describe SidekiqSpy::Config do
8
+
9
+ before do
10
+ @app = SidekiqSpy::App.new
11
+ end
12
+
13
+ describe "default" do
14
+ before do
15
+ @config = SidekiqSpy::Config.new
16
+ end
17
+
18
+ it "sets host to 127.0.0.1" do
19
+ @config.host.must_equal '127.0.0.1'
20
+ end
21
+
22
+ it "sets port to 6379" do
23
+ @config.port.must_equal 6379
24
+ end
25
+
26
+ it "sets database to 0" do
27
+ @config.database.must_equal 0
28
+ end
29
+
30
+ it "sets namespace to nil" do
31
+ @config.namespace.must_be_nil
32
+ end
33
+
34
+ it "sets interval to 5" do
35
+ @config.interval.must_equal 5
36
+ end
37
+ end
38
+
39
+ describe "configure block main" do
40
+ before do
41
+ @app.configure do |c|
42
+ c.namespace = 'resque'
43
+ c.interval = 1
44
+ end
45
+
46
+ @config = @app.config
47
+ end
48
+
49
+ it "configures namespace" do
50
+ @config.namespace.must_equal 'resque'
51
+ end
52
+
53
+ it "configures interval" do
54
+ @config.interval.must_equal 1
55
+ end
56
+ end
57
+
58
+ describe "configure block url" do
59
+ before do
60
+ @app.configure do |c|
61
+ c.url = 'redis://da.example.com:237/42'
62
+ end
63
+
64
+ @config = @app.config
65
+ end
66
+
67
+ it "configures host" do
68
+ @config.host.must_equal 'da.example.com'
69
+ end
70
+
71
+ it "configures port" do
72
+ @config.port.must_equal 237
73
+ end
74
+
75
+ it "configures database" do
76
+ @config.database.must_equal 42
77
+ end
78
+ end
79
+
80
+ describe "configure block url-brief" do
81
+ before do
82
+ @app.configure do |c|
83
+ c.url = 'redis://da.example.com'
84
+ end
85
+
86
+ @config = @app.config
87
+ end
88
+
89
+ it "configures host" do
90
+ @config.host.must_equal 'da.example.com'
91
+ end
92
+
93
+ it "configures port" do
94
+ @config.port.must_equal 6379
95
+ end
96
+
97
+ it "configures database" do
98
+ @config.database.must_equal 0
99
+ end
100
+ end
101
+
102
+ describe "configure block non-url" do
103
+ before do
104
+ @app.configure do |c|
105
+ c.host = 'da.example.com'
106
+ c.port = 237
107
+ c.database = 42
108
+ end
109
+
110
+ @config = @app.config
111
+ end
112
+
113
+ it "configures host" do
114
+ @config.host.must_equal 'da.example.com'
115
+ end
116
+
117
+ it "configures port" do
118
+ @config.port.must_equal 237
119
+ end
120
+
121
+ it "configures database" do
122
+ @config.database.must_equal 42
123
+ end
124
+ end
125
+
126
+ describe "#url" do
127
+ before do
128
+ @config = SidekiqSpy::Config.new
129
+
130
+ @config.host = 'da.example.com'
131
+ @config.port = 237
132
+ @config.database = 42
133
+ end
134
+
135
+ it "returns connection string URL" do
136
+ @config.url.must_equal 'redis://da.example.com:237/42'
137
+ end
138
+ end
139
+
140
+ end
@@ -0,0 +1,125 @@
1
+ require 'redis/namespace'
2
+ require 'json'
3
+
4
+ require File.expand_path('../../../helper', __FILE__)
5
+
6
+ require File.expand_path('../../../../lib/sidekiq-spy/spy/stats', __FILE__)
7
+
8
+
9
+ describe SidekiqSpy::Spy::Stats do
10
+
11
+ before do
12
+ @redis = Redis::Namespace.new('resque', :redis => Redis.new)
13
+
14
+ Sidekiq.configure_client do |config|
15
+ config.redis = ConnectionPool.new(:size => 1, &proc { @redis })
16
+ end
17
+
18
+ @fixtures = {
19
+ :redis_info => JSON.parse(File.read(
20
+ File.expand_path('../../../fixtures/redis_info.json', __FILE__)
21
+ )),
22
+ }
23
+
24
+ @sidekiq_stats = Minitest::Mock.new
25
+ @sidekiq_stats.expect(:enqueued, 16776977673)
26
+ @sidekiq_stats.expect(:retry_size, 924984826746)
27
+ @sidekiq_stats.expect(:scheduled_size, 317321542620)
28
+ @sidekiq_stats.expect(:processed, 923531545885)
29
+ @sidekiq_stats.expect(:failed, 779187529140)
30
+
31
+ @sidekiq_workers = Minitest::Mock.new
32
+ @sidekiq_workers.expect(:size, 162165179294)
33
+
34
+ Sidekiq::Stats.stub(:new, @sidekiq_stats) do
35
+ Sidekiq::Workers.stub(:new, @sidekiq_workers) do
36
+ @stats = SidekiqSpy::Spy::Stats.new
37
+ end
38
+ end
39
+ end
40
+
41
+ describe "#connection" do
42
+ it "returns stat connection" do
43
+ @redis.client.stub(:location, 'da.example.com:237') do
44
+ @redis.client.stub(:db, 42) do
45
+ @stats.connection.must_equal 'da.example.com:237/42'
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ describe "#namespace" do
52
+ it "returns stat namespace" do
53
+ @stats.namespace.must_equal 'resque'
54
+ end
55
+ end
56
+
57
+ describe "#uptime" do
58
+ it "returns stat uptime" do
59
+ @redis.stub(:info, @fixtures[:redis_info]) do
60
+ @stats.uptime.must_equal '7'
61
+ end
62
+ end
63
+ end
64
+
65
+ describe "#connections" do
66
+ it "returns stat connections" do
67
+ @redis.stub(:info, @fixtures[:redis_info]) do
68
+ @stats.connections.must_equal '8'
69
+ end
70
+ end
71
+ end
72
+
73
+ describe "#memory" do
74
+ it "returns stat memory" do
75
+ @redis.stub(:info, @fixtures[:redis_info]) do
76
+ @stats.memory.must_equal '5.16M'
77
+ end
78
+ end
79
+ end
80
+
81
+ describe "#memory_peak" do
82
+ it "returns stat memory_peak" do
83
+ @redis.stub(:info, @fixtures[:redis_info]) do
84
+ @stats.memory_peak.must_equal '5.14M'
85
+ end
86
+ end
87
+ end
88
+
89
+ describe "#busy" do
90
+ it "returns stat busy" do
91
+ @stats.busy.must_equal 162165179294
92
+ end
93
+ end
94
+
95
+ describe "#enqueued" do
96
+ it "returns stat enqueued" do
97
+ @stats.enqueued.must_equal 16776977673
98
+ end
99
+ end
100
+
101
+ describe "#retries" do
102
+ it "returns stat retries" do
103
+ @stats.retries.must_equal 924984826746
104
+ end
105
+ end
106
+
107
+ describe "#scheduled" do
108
+ it "returns stat scheduled" do
109
+ @stats.scheduled.must_equal 317321542620
110
+ end
111
+ end
112
+
113
+ describe "#processed" do
114
+ it "returns stat processed" do
115
+ @stats.processed.must_equal 923531545885
116
+ end
117
+ end
118
+
119
+ describe "#failed" do
120
+ it "returns stat failed" do
121
+ @stats.failed.must_equal 779187529140
122
+ end
123
+ end
124
+
125
+ end
@@ -0,0 +1,12 @@
1
+ require File.expand_path('../../helper', __FILE__)
2
+
3
+ require File.expand_path('../../../lib/sidekiq-spy/version', __FILE__)
4
+
5
+
6
+ describe "SidekiqSpy::VERSION" do
7
+
8
+ it "uses major.minor.patch" do
9
+ SidekiqSpy::VERSION.must_match /\A\d+\.\d+\.\d+\z/
10
+ end
11
+
12
+ end
metadata ADDED
@@ -0,0 +1,133 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sidekiq-spy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - tiredpixel
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-10-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sidekiq
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '2.15'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '2.15'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: coveralls
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Sidekiq monitoring in the console. A bit like Sidekiq::Web. But without
70
+ the web.
71
+ email:
72
+ - tp@tiredpixel.com
73
+ executables:
74
+ - sidekiq-spy
75
+ extensions: []
76
+ extra_rdoc_files: []
77
+ files:
78
+ - .gitignore
79
+ - .ruby-gemset
80
+ - .ruby-version
81
+ - .travis.yml
82
+ - Gemfile
83
+ - LICENSE.txt
84
+ - README.md
85
+ - Rakefile
86
+ - bin/sidekiq-spy
87
+ - lib/sidekiq-spy.rb
88
+ - lib/sidekiq-spy/app.rb
89
+ - lib/sidekiq-spy/config.rb
90
+ - lib/sidekiq-spy/display/panel.rb
91
+ - lib/sidekiq-spy/display/screen.rb
92
+ - lib/sidekiq-spy/display/subpanel.rb
93
+ - lib/sidekiq-spy/spy/stats.rb
94
+ - lib/sidekiq-spy/translatable.rb
95
+ - lib/sidekiq-spy/version.rb
96
+ - sidekiq-spy.gemspec
97
+ - test/fixtures/redis_info.json
98
+ - test/helper.rb
99
+ - test/sidekiq-spy/app_test.rb
100
+ - test/sidekiq-spy/config_test.rb
101
+ - test/sidekiq-spy/spy/stats_test.rb
102
+ - test/sidekiq-spy/version_test.rb
103
+ homepage: https://github.com/tiredpixel/sidekiq-spy
104
+ licenses:
105
+ - MIT
106
+ metadata: {}
107
+ post_install_message:
108
+ rdoc_options: []
109
+ require_paths:
110
+ - lib
111
+ required_ruby_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - '>='
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ required_rubygems_version: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - '>='
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ requirements: []
122
+ rubyforge_project:
123
+ rubygems_version: 2.1.9
124
+ signing_key:
125
+ specification_version: 4
126
+ summary: Sidekiq monitoring in the console.
127
+ test_files:
128
+ - test/fixtures/redis_info.json
129
+ - test/helper.rb
130
+ - test/sidekiq-spy/app_test.rb
131
+ - test/sidekiq-spy/config_test.rb
132
+ - test/sidekiq-spy/spy/stats_test.rb
133
+ - test/sidekiq-spy/version_test.rb