sidekiq_monitor 0.0.8 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -87,11 +87,32 @@ class HardWorker
87
87
 
88
88
  def perform(user_ids)
89
89
  puts 'Doing hard work'
90
- { processed_users_count: user_ids.length }
90
+ {
91
+ message: "#{user_ids.length} users processed",
92
+ processed_users_count: user_ids.length
93
+ }
91
94
  end
92
95
  end
93
96
  ```
94
97
 
98
+ If you set `:message` as a key of this hash, that value will be displayed in the Result column of jobs tables in the UI. To view the full result hash of a job in the UI, click on the job status button to open up the modal job view.
99
+
100
+ ### Graph
101
+
102
+ A single histogram will be shown by default in the Graph view, but you can also split the queues into multiple histograms. (This is especially useful if you have a large number of queues and the single histogram has too many bars to be readable.) The keys of this hash are JS regex patterns for matching queues, and the values of the hash will be the titles of each histogram:
103
+
104
+ ```ruby
105
+ # config/initializers/sidekiq_monitor.rb
106
+ Sidekiq::Monitor.options[:graphs] = {
107
+ 'ALL' => 'All',
108
+ 'OTHER' => 'Default Priority',
109
+ '_high$' => 'High Priority',
110
+ '_low$' => 'Low Priority'
111
+ }
112
+ ```
113
+
114
+ `ALL` and `OTHER` are special keys: `ALL` will show all queues and `OTHER` will show all queues that aren't matched by the regex keys.
115
+
95
116
  ### Authentication
96
117
 
97
118
  You'll likely want to restrict access to this interface in a production setting. To do this, you can use routing constraints:
@@ -33,82 +33,107 @@ class SidekiqMonitor.Graph
33
33
  , @options.poll_interval
34
34
 
35
35
  render: =>
36
- d3.json(SidekiqMonitor.settings.api_url('jobs/graph'), (data) =>
36
+ d3.json SidekiqMonitor.settings.api_url('jobs/graph'), (data) =>
37
37
 
38
38
  # Clear out the previously rendered graph
39
39
  $(@options.selector).text('')
40
40
 
41
- @svg = d3.select(@options.selector).append('svg:svg')
42
- .attr('width', @width)
43
- .attr('height', @height)
44
- .append('svg:g')
45
- .attr('transform', 'translate(' + @padding[3] + ',' + (@height - @padding[2]) + ')')
46
-
47
- statuses = data.statuses
48
- data = data.queues_status_counts
49
-
50
- @color_scale.domain(statuses)
51
-
52
- # Transpose the data into layers by queue
53
- queues = d3.layout.stack()(statuses.map( (queue) =>
54
- layers = []
55
- for d in data
56
- if d[queue]?
57
- layers.push {x: d.queue, y: d[queue]+0}
58
- else
59
- layers.push {x: d.queue, y: 0}
60
- layers
61
- ))
62
-
63
- # Compute the x-domain (by queue) and y-domain (by top).
64
- @x_scale.domain(queues[0].map( (d) => d.x))
65
- @y_scale.domain([0, d3.max(queues[queues.length - 1], (d) => d.y0 + d.y )])
66
-
67
- # Groups for queues
68
- queue = @svg.selectAll('g.queue')
69
- .data(queues)
70
- .enter().append('svg:g')
71
- .attr('class', 'queue')
72
- .style('fill', (d, i) => @z_scale(i) )
73
- .style('stroke', (d, i) => d3.rgb(@z_scale(i)).darker() )
74
-
75
- # Rects for queues
76
- rect = queue.selectAll('rect')
77
- .data(Object)
78
- .enter().append('svg:rect')
79
- .attr('x', (d) => @x_scale(d.x) )
80
- .attr('y', (d) => -@y_scale(d.y0) - @y_scale(d.y) )
81
- .attr('height', (d) => @y_scale(d.y) )
82
- .attr('width', @x_scale.rangeBand())
83
- .append('title')
84
- .text((d) => if d.y == 1 then "#{d.y} job" else "#{d.y} jobs")
85
-
86
- # Labels for queues
87
- label = @svg.selectAll('text')
88
- .data(@x_scale.domain())
89
- .enter().append('foreignObject')
90
- .attr('x', (d) => @x_scale(d) )
91
- .attr('y', 6)
92
- .attr('dy', '.71em')
41
+ queues = data.queues_status_counts.map (queues_status_count) -> queues_status_count.queue
42
+ graphs_config = SidekiqMonitor.settings.graphs || { 'ALL': null }
43
+ graphs_config = @standardize_graphs_config(graphs_config, queues)
44
+
45
+ for index, graph_config of graphs_config
46
+ graph_config['show_legend'] = true if index == '0'
47
+ @render_graph(data, graph_config)
48
+
49
+ render_graph: (data, graph_config) =>
50
+ @svg = d3.select(@options.selector).append('svg:svg')
51
+ .attr('width', @width)
52
+ .attr('height', @height)
53
+ .append('svg:g')
54
+ .attr('transform', 'translate(' + @padding[3] + ',' + (@height - @padding[2]) + ')')
55
+
56
+ statuses = data.statuses
57
+ queues_status_counts = []
58
+ for queue_status_counts in data.queues_status_counts
59
+ if graph_config['queues'].indexOf(queue_status_counts.queue) > -1
60
+ queues_status_counts.push queue_status_counts
61
+ data = queues_status_counts
62
+
63
+ @color_scale.domain(statuses)
64
+
65
+ # Transpose the data into layers by queue
66
+ queues = d3.layout.stack()(statuses.map( (queue) =>
67
+ layers = []
68
+ for d in data
69
+ if d[queue]?
70
+ layers.push {x: d.queue, y: d[queue]+0}
71
+ else
72
+ layers.push {x: d.queue, y: 0}
73
+ layers
74
+ ))
75
+
76
+ # Compute the x-domain (by queue) and y-domain (by top).
77
+ @x_scale.domain(queues[0].map( (d) => d.x))
78
+ @y_scale.domain([0, d3.max(queues[queues.length - 1], (d) => d.y0 + d.y )])
79
+
80
+ # Groups for queues
81
+ queue = @svg.selectAll('g.queue')
82
+ .data(queues)
83
+ .enter().append('svg:g')
84
+ .attr('class', 'queue')
85
+ .style('fill', (d, i) => @z_scale(i) )
86
+ .style('stroke', (d, i) => d3.rgb(@z_scale(i)).darker() )
87
+
88
+ # Rects for queues
89
+ rect = queue.selectAll('rect')
90
+ .data(Object)
91
+ .enter().append('svg:rect')
92
+ .attr('x', (d) => @x_scale(d.x) )
93
+ .attr('y', (d) => -@y_scale(d.y0) - @y_scale(d.y) )
94
+ .attr('height', (d) => @y_scale(d.y) )
95
+ .attr('width', @x_scale.rangeBand())
96
+ .append('title')
97
+ .text((d) => if d.y == 1 then "#{d.y} job" else "#{d.y} jobs")
98
+
99
+ # Labels for queues
100
+ label = @svg.selectAll('text')
101
+ .data(@x_scale.domain())
102
+ .enter().append('foreignObject')
103
+ .attr('x', (d) => @x_scale(d) )
104
+ .attr('y', 6)
105
+ .attr('dy', '.71em')
106
+ .attr('height', 20)
107
+ .attr('width', @x_scale.rangeBand())
108
+ .append('xhtml:div')
109
+ .text((d) => d)
110
+ .attr('style', 'font-size: 11px; text-align: center; word-wrap:break-word; padding: 0 3px')
111
+
112
+ # Y-axis rules
113
+ rule = @svg.selectAll('g.rule')
114
+ .data(@y_scale.ticks(5))
115
+ .enter().append('svg:g')
116
+ .attr('class', 'rule')
117
+ .attr('transform', (d) => 'translate(0,' + -@y_scale(d) + ')' )
118
+
119
+ rule.append('svg:text')
120
+ .attr('x', @width - @padding[1] - @padding[3] + 6)
121
+ .attr('dy', '.35em')
122
+ .text(d3.format(',d'))
123
+
124
+ # Title
125
+ if graph_config['title']
126
+ label = @svg.selectAll('g.text').data(['Test 1']).enter()
127
+ .append('text')
128
+ .attr('x', 0)
129
+ .attr('y', 90 - @height)
93
130
  .attr('height', 20)
94
- .attr('width', @x_scale.rangeBand())
95
- .append('xhtml:div')
96
- .text((d) => d)
97
- .attr('style', 'font-size: 11px; text-align: center; word-wrap:break-word; padding: 0 3px')
98
-
99
- # Y-axis rules
100
- rule = @svg.selectAll('g.rule')
101
- .data(@y_scale.ticks(5))
102
- .enter().append('svg:g')
103
- .attr('class', 'rule')
104
- .attr('transform', (d) => 'translate(0,' + -@y_scale(d) + ')' )
105
-
106
- rule.append('svg:text')
107
- .attr('x', @width - @padding[1] - @padding[3] + 6)
108
- .attr('dy', '.35em')
109
- .text(d3.format(',d'))
131
+ .attr('font-size', '24px')
132
+ .attr('fill', '#777')
133
+ .text(graph_config['title'])
110
134
 
111
- # Legend
135
+ # Legend
136
+ if graph_config['show_legend']
112
137
  legend_x = @width - 130
113
138
  legend_y = 30 - @height
114
139
  legend_box_width = 18
@@ -142,7 +167,37 @@ class SidekiqMonitor.Graph
142
167
  .attr('y', legend_y + 9)
143
168
  .attr('dy', '.35em')
144
169
  .text((d) => d)
145
- )
170
+
171
+ standardize_graphs_config: (graph_config, queues) =>
172
+ graphs = []
173
+ matched_queues = []
174
+ for pattern, title of graph_config
175
+ graph = {
176
+ title: title
177
+ pattern: pattern
178
+ }
179
+ if pattern == 'OTHER' || pattern == 'ALL'
180
+ graphs[pattern] = graph
181
+ continue
182
+ else
183
+ pattern_queues = []
184
+ for queue in queues
185
+ re = new RegExp(pattern, 'i')
186
+ if re.test(queue)
187
+ pattern_queues.push queue
188
+ matched_queues.push queue
189
+ graph['queues'] = pattern_queues
190
+ graphs[pattern] = graph
191
+ remaining_queues = @array_difference(queues, matched_queues)
192
+ graphs['OTHER']['queues'] = remaining_queues if graphs['OTHER']
193
+ graphs['ALL']['queues'] = queues if graphs['ALL']
194
+ @object_values(graphs)
195
+
196
+ array_difference: (array1, array2) =>
197
+ array1.filter (value) -> !(array2.indexOf(value) > -1)
198
+
199
+ object_values: (object) =>
200
+ value for key, value of object
146
201
 
147
202
  $ ->
148
203
  new SidekiqMonitor.Graph
@@ -3,3 +3,4 @@ window.SidekiqMonitor =
3
3
  settings:
4
4
  api_url: (path) ->
5
5
  "<%= url_helpers.sidekiq_monitor_path %>api/#{path}"
6
+ graphs: <%= Sidekiq::Monitor.options[:graphs].to_json %>
@@ -1,5 +1,5 @@
1
1
  module Sidekiq
2
2
  module Monitor
3
- VERSION = '0.0.8'
3
+ VERSION = '0.0.9'
4
4
  end
5
5
  end
@@ -9,6 +9,18 @@ Dir.glob("#{directory}/../../app/datatables/sidekiq/monitor/jobs_datatable.rb")
9
9
 
10
10
  module Sidekiq
11
11
  module Monitor
12
+ DEFAULTS = {
13
+ :graphs => nil
14
+ }
15
+
16
+ def self.options
17
+ @options ||= DEFAULTS.dup
18
+ end
19
+
20
+ def self.options=(opts)
21
+ @options = opts
22
+ end
23
+
12
24
  def self.table_name_prefix
13
25
  'sidekiq_'
14
26
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sidekiq_monitor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.8
4
+ version: 0.0.9
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-08-23 00:00:00.000000000 Z
12
+ date: 2013-09-25 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: sidekiq