sidekiq-spy 0.1.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.
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