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
|
-
{
|
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
|
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
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
.
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
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('
|
95
|
-
.
|
96
|
-
|
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
|
-
|
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
|
data/lib/sidekiq/monitor.rb
CHANGED
@@ -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.
|
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-
|
12
|
+
date: 2013-09-25 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: sidekiq
|