sidekiq 7.1.2 → 7.2.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.

Potentially problematic release.


This version of sidekiq might be problematic. Click here for more details.

Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +69 -0
  3. data/README.md +2 -2
  4. data/lib/sidekiq/api.rb +3 -3
  5. data/lib/sidekiq/client.rb +6 -3
  6. data/lib/sidekiq/config.rb +13 -4
  7. data/lib/sidekiq/deploy.rb +1 -1
  8. data/lib/sidekiq/job_retry.rb +19 -3
  9. data/lib/sidekiq/job_util.rb +2 -0
  10. data/lib/sidekiq/metrics/query.rb +3 -1
  11. data/lib/sidekiq/metrics/shared.rb +1 -1
  12. data/lib/sidekiq/paginator.rb +2 -2
  13. data/lib/sidekiq/processor.rb +27 -26
  14. data/lib/sidekiq/rails.rb +10 -15
  15. data/lib/sidekiq/redis_client_adapter.rb +17 -2
  16. data/lib/sidekiq/redis_connection.rb +1 -0
  17. data/lib/sidekiq/scheduled.rb +1 -1
  18. data/lib/sidekiq/testing.rb +25 -6
  19. data/lib/sidekiq/version.rb +1 -1
  20. data/lib/sidekiq/web/action.rb +3 -3
  21. data/lib/sidekiq/web/application.rb +72 -6
  22. data/lib/sidekiq/web/csrf_protection.rb +1 -1
  23. data/lib/sidekiq/web/helpers.rb +31 -23
  24. data/lib/sidekiq/web.rb +13 -1
  25. data/web/assets/javascripts/application.js +16 -0
  26. data/web/assets/javascripts/dashboard-charts.js +17 -1
  27. data/web/assets/javascripts/dashboard.js +7 -9
  28. data/web/assets/javascripts/metrics.js +34 -0
  29. data/web/assets/stylesheets/application.css +9 -0
  30. data/web/locales/en.yml +2 -0
  31. data/web/locales/pt-br.yml +20 -0
  32. data/web/views/_job_info.erb +1 -1
  33. data/web/views/_metrics_period_select.erb +1 -1
  34. data/web/views/_summary.erb +7 -7
  35. data/web/views/busy.erb +3 -3
  36. data/web/views/dashboard.erb +23 -33
  37. data/web/views/filtering.erb +7 -0
  38. data/web/views/metrics.erb +36 -27
  39. data/web/views/metrics_for_job.erb +26 -35
  40. data/web/views/queues.erb +6 -2
  41. metadata +4 -3
@@ -15,7 +15,7 @@ module Sidekiq
15
15
  "manifest-src 'self'",
16
16
  "media-src 'self'",
17
17
  "object-src 'none'",
18
- "script-src 'self' https: http: 'unsafe-inline'",
18
+ "script-src 'self' https: http:",
19
19
  "style-src 'self' https: http: 'unsafe-inline'",
20
20
  "worker-src 'self'",
21
21
  "base-uri 'self'"
@@ -328,9 +328,75 @@ module Sidekiq
328
328
  json Sidekiq::Stats.new.queues
329
329
  end
330
330
 
331
+ ########
332
+ # Filtering
333
+
334
+ get "/filter/metrics" do
335
+ redirect "#{root_path}metrics"
336
+ end
337
+
338
+ post "/filter/metrics" do
339
+ x = params[:substr]
340
+ q = Sidekiq::Metrics::Query.new
341
+ @period = h((params[:period] || "")[0..1])
342
+ @periods = METRICS_PERIODS
343
+ minutes = @periods.fetch(@period, @periods.values.first)
344
+ @query_result = q.top_jobs(minutes: minutes, class_filter: Regexp.new(Regexp.escape(x), Regexp::IGNORECASE))
345
+
346
+ erb :metrics
347
+ end
348
+
349
+ get "/filter/retries" do
350
+ x = params[:substr]
351
+ return redirect "#{root_path}retries" unless x && x != ""
352
+
353
+ @retries = search(Sidekiq::RetrySet.new, params[:substr])
354
+ erb :retries
355
+ end
356
+
357
+ post "/filter/retries" do
358
+ x = params[:substr]
359
+ return redirect "#{root_path}retries" unless x && x != ""
360
+
361
+ @retries = search(Sidekiq::RetrySet.new, params[:substr])
362
+ erb :retries
363
+ end
364
+
365
+ get "/filter/scheduled" do
366
+ x = params[:substr]
367
+ return redirect "#{root_path}scheduled" unless x && x != ""
368
+
369
+ @scheduled = search(Sidekiq::ScheduledSet.new, params[:substr])
370
+ erb :scheduled
371
+ end
372
+
373
+ post "/filter/scheduled" do
374
+ x = params[:substr]
375
+ return redirect "#{root_path}scheduled" unless x && x != ""
376
+
377
+ @scheduled = search(Sidekiq::ScheduledSet.new, params[:substr])
378
+ erb :scheduled
379
+ end
380
+
381
+ get "/filter/dead" do
382
+ x = params[:substr]
383
+ return redirect "#{root_path}morgue" unless x && x != ""
384
+
385
+ @dead = search(Sidekiq::DeadSet.new, params[:substr])
386
+ erb :morgue
387
+ end
388
+
389
+ post "/filter/dead" do
390
+ x = params[:substr]
391
+ return redirect "#{root_path}morgue" unless x && x != ""
392
+
393
+ @dead = search(Sidekiq::DeadSet.new, params[:substr])
394
+ erb :morgue
395
+ end
396
+
331
397
  def call(env)
332
398
  action = self.class.match(env)
333
- return [404, {"content-type" => "text/plain", "x-cascade" => "pass"}, ["Not Found"]] unless action
399
+ return [404, {Rack::CONTENT_TYPE => "text/plain", Web::X_CASCADE => "pass"}, ["Not Found"]] unless action
334
400
 
335
401
  app = @klass
336
402
  resp = catch(:halt) do
@@ -347,10 +413,10 @@ module Sidekiq
347
413
  else
348
414
  # rendered content goes here
349
415
  headers = {
350
- "content-type" => "text/html",
351
- "cache-control" => "private, no-store",
352
- "content-language" => action.locale,
353
- "content-security-policy" => CSP_HEADER
416
+ Rack::CONTENT_TYPE => "text/html",
417
+ Rack::CACHE_CONTROL => "private, no-store",
418
+ Web::CONTENT_LANGUAGE => action.locale,
419
+ Web::CONTENT_SECURITY_POLICY => CSP_HEADER
354
420
  }
355
421
  # we'll let Rack calculate Content-Length for us.
356
422
  [200, headers, [resp]]
@@ -62,7 +62,7 @@ module Sidekiq
62
62
 
63
63
  def deny(env)
64
64
  logger(env).warn "attack prevented by #{self.class}"
65
- [403, {"Content-Type" => "text/plain"}, ["Forbidden"]]
65
+ [403, {Rack::CONTENT_TYPE => "text/plain"}, ["Forbidden"]]
66
66
  end
67
67
 
68
68
  def session(env)
@@ -21,6 +21,10 @@ module Sidekiq
21
21
  end
22
22
  end
23
23
 
24
+ def to_json(x)
25
+ Sidekiq.dump_json(x)
26
+ end
27
+
24
28
  def singularize(str, count)
25
29
  if count == 1 && str.respond_to?(:singularize) # rails
26
30
  str.singularize
@@ -49,8 +53,29 @@ module Sidekiq
49
53
  locale_files.select { |file| file =~ /\/#{lang}\.yml$/ }
50
54
  end
51
55
 
52
- # This is a hook for a Sidekiq Pro feature. Please don't touch.
53
- def filtering(*)
56
+ def search(jobset, substr)
57
+ resultset = jobset.scan(substr).to_a
58
+ @current_page = 1
59
+ @count = @total_size = resultset.size
60
+ resultset
61
+ end
62
+
63
+ def filtering(which)
64
+ erb(:filtering, locals: {which: which})
65
+ end
66
+
67
+ def filter_link(jid, within = "retries")
68
+ if within.nil?
69
+ ::Rack::Utils.escape_html(jid)
70
+ else
71
+ "<a href='#{root_path}filter/#{within}?substr=#{jid}'>#{::Rack::Utils.escape_html(jid)}</a>"
72
+ end
73
+ end
74
+
75
+ def display_tags(job, within = "retries")
76
+ job.tags.map { |tag|
77
+ "<span class='label label-info jobtag'>#{filter_link(tag, within)}</span>"
78
+ }.join(" ")
54
79
  end
55
80
 
56
81
  # This view helper provide ability display you html code in
@@ -111,13 +136,6 @@ module Sidekiq
111
136
  end
112
137
  end
113
138
 
114
- # within is used by Sidekiq Pro
115
- def display_tags(job, within = nil)
116
- job.tags.map { |tag|
117
- "<span class='label label-info jobtag'>#{::Rack::Utils.escape_html(tag)}</span>"
118
- }.join(" ")
119
- end
120
-
121
139
  # sidekiq/sidekiq#3243
122
140
  def unfiltered?
123
141
  yield unless env["PATH_INFO"].start_with?("/filter/")
@@ -278,23 +296,13 @@ module Sidekiq
278
296
  elsif rss_kb < 10_000_000
279
297
  "#{number_with_delimiter((rss_kb / 1024.0).to_i)} MB"
280
298
  else
281
- "#{number_with_delimiter((rss_kb / (1024.0 * 1024.0)).round(1))} GB"
299
+ "#{number_with_delimiter((rss_kb / (1024.0 * 1024.0)), precision: 1)} GB"
282
300
  end
283
301
  end
284
302
 
285
- def number_with_delimiter(number)
286
- return "" if number.nil?
287
-
288
- begin
289
- Float(number)
290
- rescue ArgumentError, TypeError
291
- return number
292
- end
293
-
294
- options = {delimiter: ",", separator: "."}
295
- parts = number.to_s.to_str.split(".")
296
- parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}")
297
- parts.join(options[:separator])
303
+ def number_with_delimiter(number, options = {})
304
+ precision = options[:precision] || 0
305
+ %(<span data-nwp="#{precision}">#{number.round(precision)}</span>)
298
306
  end
299
307
 
300
308
  def h(text)
data/lib/sidekiq/web.rb CHANGED
@@ -34,6 +34,18 @@ module Sidekiq
34
34
  "Metrics" => "metrics"
35
35
  }
36
36
 
37
+ if Gem::Version.new(Rack::RELEASE) < Gem::Version.new("3")
38
+ CONTENT_LANGUAGE = "Content-Language"
39
+ CONTENT_SECURITY_POLICY = "Content-Security-Policy"
40
+ LOCATION = "Location"
41
+ X_CASCADE = "X-Cascade"
42
+ else
43
+ CONTENT_LANGUAGE = "content-language"
44
+ CONTENT_SECURITY_POLICY = "content-security-policy"
45
+ LOCATION = "location"
46
+ X_CASCADE = "x-cascade"
47
+ end
48
+
37
49
  class << self
38
50
  def settings
39
51
  self
@@ -137,7 +149,7 @@ module Sidekiq
137
149
  m = middlewares
138
150
 
139
151
  rules = []
140
- rules = [[:all, {"Cache-Control" => "private, max-age=86400"}]] unless ENV["SIDEKIQ_WEB_TESTING"]
152
+ rules = [[:all, {Rack::CACHE_CONTROL => "private, max-age=86400"}]] unless ENV["SIDEKIQ_WEB_TESTING"]
141
153
 
142
154
  ::Rack::Builder.new do
143
155
  use Rack::Static, urls: ["/stylesheets", "/images", "/javascripts"],
@@ -33,6 +33,7 @@ function addListeners() {
33
33
 
34
34
  addShiftClickListeners()
35
35
  updateFuzzyTimes();
36
+ updateNumbers();
36
37
  setLivePollFromUrl();
37
38
 
38
39
  var buttons = document.querySelectorAll(".live-poll");
@@ -102,6 +103,20 @@ function updateFuzzyTimes() {
102
103
  t.cancel();
103
104
  }
104
105
 
106
+ function updateNumbers() {
107
+ document.querySelectorAll("[data-nwp]").forEach(node => {
108
+ let number = parseFloat(node.textContent);
109
+ let precision = parseInt(node.dataset["nwp"] || 0);
110
+ if (typeof number === "number") {
111
+ let formatted = number.toLocaleString(undefined, {
112
+ minimumFractionDigits: precision,
113
+ maximumFractionDigits: precision,
114
+ });
115
+ node.textContent = formatted;
116
+ }
117
+ });
118
+ }
119
+
105
120
  function setLivePollFromUrl() {
106
121
  var url_params = new URL(window.location.href).searchParams
107
122
 
@@ -140,6 +155,7 @@ function checkResponse(resp) {
140
155
 
141
156
  function scheduleLivePoll() {
142
157
  let ti = parseInt(localStorage.sidekiqTimeInterval) || 5000;
158
+ if (ti < 2000) { ti = 2000 }
143
159
  livePollTimer = setTimeout(livePollCallback, ti);
144
160
  }
145
161
 
@@ -57,7 +57,9 @@ class DashboardChart extends BaseChart {
57
57
  class RealtimeChart extends DashboardChart {
58
58
  constructor(el, options) {
59
59
  super(el, options);
60
- this.delay = parseInt(localStorage.sidekiqTimeInterval) || 5000;
60
+ let d = parseInt(localStorage.sidekiqTimeInterval) || 5000;
61
+ if (d < 2000) { d = 2000; }
62
+ this.delay = d
61
63
  this.startPolling();
62
64
  document.addEventListener("interval:update", this.handleUpdate.bind(this));
63
65
  }
@@ -84,6 +86,7 @@ class RealtimeChart extends DashboardChart {
84
86
  updateStatsSummary(this.stats.sidekiq);
85
87
  updateRedisStats(this.stats.redis);
86
88
  updateFooterUTCTime(this.stats.server_utc_time);
89
+ updateNumbers();
87
90
  pulseBeacon();
88
91
 
89
92
  this.stats = stats;
@@ -164,3 +167,16 @@ class RealtimeChart extends DashboardChart {
164
167
  };
165
168
  }
166
169
  }
170
+
171
+ var rc = document.getElementById("realtime-chart")
172
+ if (rc != null) {
173
+ var rtc = new RealtimeChart(rc, JSON.parse(rc.textContent))
174
+ rtc.registerLegend(document.getElementById("realtime-legend"))
175
+ window.realtimeChart = rtc
176
+ }
177
+
178
+ var hc = document.getElementById("history-chart")
179
+ if (hc != null) {
180
+ var htc = new DashboardChart(hc, JSON.parse(hc.textContent))
181
+ window.historyChart = htc
182
+ }
@@ -1,15 +1,13 @@
1
1
  Sidekiq = {};
2
2
 
3
- var nf = new Intl.NumberFormat();
4
-
5
3
  var updateStatsSummary = function(data) {
6
- document.getElementById("txtProcessed").innerText = nf.format(data.processed);
7
- document.getElementById("txtFailed").innerText = nf.format(data.failed);
8
- document.getElementById("txtBusy").innerText = nf.format(data.busy);
9
- document.getElementById("txtScheduled").innerText = nf.format(data.scheduled);
10
- document.getElementById("txtRetries").innerText = nf.format(data.retries);
11
- document.getElementById("txtEnqueued").innerText = nf.format(data.enqueued);
12
- document.getElementById("txtDead").innerText = nf.format(data.dead);
4
+ document.getElementById("txtProcessed").innerText = data.processed;
5
+ document.getElementById("txtFailed").innerText = data.failed;
6
+ document.getElementById("txtBusy").innerText = data.busy;
7
+ document.getElementById("txtScheduled").innerText = data.scheduled;
8
+ document.getElementById("txtRetries").innerText = data.retries;
9
+ document.getElementById("txtEnqueued").innerText = data.enqueued;
10
+ document.getElementById("txtDead").innerText = data.dead;
13
11
  }
14
12
 
15
13
  var updateRedisStats = function(data) {
@@ -262,3 +262,37 @@ class HistBubbleChart extends BaseChart {
262
262
  };
263
263
  }
264
264
  }
265
+
266
+ var ch = document.getElementById("job-metrics-overview-chart");
267
+ if (ch != null) {
268
+ var jm = new JobMetricsOverviewChart(ch, JSON.parse(ch.textContent));
269
+ document.querySelectorAll(".metrics-swatch-wrapper > input[type=checkbox]").forEach((imp) => {
270
+ jm.registerSwatch(imp.id)
271
+ });
272
+ window.jobMetricsChart = jm;
273
+ }
274
+
275
+ var htc = document.getElementById("hist-totals-chart");
276
+ if (htc != null) {
277
+ var tc = new HistTotalsChart(htc, JSON.parse(htc.textContent));
278
+ window.histTotalsChart = tc
279
+ }
280
+
281
+ var hbc = document.getElementById("hist-bubble-chart");
282
+ if (hbc != null) {
283
+ var bc = new HistBubbleChart(hbc, JSON.parse(hbc.textContent));
284
+ window.histBubbleChart = bc
285
+ }
286
+
287
+ var form = document.getElementById("metrics-form")
288
+ document.querySelectorAll("#period-selector").forEach(node => {
289
+ node.addEventListener("input", debounce(() => form.submit()))
290
+ })
291
+
292
+ function debounce(func, timeout = 300) {
293
+ let timer;
294
+ return (...args) => {
295
+ clearTimeout(timer);
296
+ timer = setTimeout(() => { func.apply(this, args); }, timeout);
297
+ };
298
+ }
@@ -370,6 +370,15 @@ img.smallogo {
370
370
  .stat p {
371
371
  font-size: 0.9em;
372
372
  }
373
+
374
+ .num {
375
+ font-family: monospace;
376
+ }
377
+
378
+ td.num {
379
+ text-align: right;
380
+ }
381
+
373
382
  @media (max-width: 767px) {
374
383
  .stats-container {
375
384
  display: block;
data/web/locales/en.yml CHANGED
@@ -97,3 +97,5 @@ en:
97
97
  Context: Context
98
98
  Bucket: Bucket
99
99
  NoJobMetricsFound: No recent job metrics were found
100
+ Filter: Filter
101
+ AnyJobContent: Any job content
@@ -5,10 +5,13 @@
5
5
  AreYouSureDeleteJob: Deseja deletar esta tarefa?
6
6
  AreYouSureDeleteQueue: Deseja deletar a fila %{queue}? Isso irá deletar todas as tarefas desta fila.
7
7
  Arguments: Argumentos
8
+ AvgExecutionTime: Tempo médio de execução
8
9
  BackToApp: De volta ao aplicativo
10
+ Bucket: Bucket
9
11
  Busy: Ocupados
10
12
  Class: Classe
11
13
  Connections: Conexões
14
+ Context: Contexto
12
15
  CreatedAt: Criado em
13
16
  CurrentMessagesInQueue: Mensagens atualmente na <span class='title'>%{queue}</span>
14
17
  Dashboard: Painel
@@ -16,13 +19,16 @@
16
19
  DeadJobs: Tarefas mortas
17
20
  Delete: Apagar
18
21
  DeleteAll: Apagar tudo
22
+ Deploy: Deploy
19
23
  Enqueued: Na fila
20
24
  Error: Erro
21
25
  ErrorBacktrace: Rastreamento do erro
22
26
  ErrorClass: Classe de erro
23
27
  ErrorMessage: Mensagem de erro
28
+ ExecutionTime: Tempo de execução
24
29
  Extras: Extras
25
30
  Failed: Falhas
31
+ Failure: Falha
26
32
  Failures: Falhas
27
33
  GoBack: ← Voltar
28
34
  History: Histórico
@@ -34,10 +40,13 @@
34
40
  Latency: Latência
35
41
  LivePoll: Live Poll
36
42
  MemoryUsage: Uso de memória
43
+ Metrics: Métricas
37
44
  Name: Nome
38
45
  Namespace: Namespace
39
46
  NextRetry: Próxima Tentativa
47
+ NoDataFound: Nenhum dado encontrado
40
48
  NoDeadJobsFound: Nenhuma tarefa morta foi encontrada
49
+ NoJobMetricsFound: Nenhuma métrica de tarefa encontrada
41
50
  NoRetriesFound: Nenhuma tentativa encontrada
42
51
  NoScheduledFound: Nenhuma tarefa agendada foi encontrada
43
52
  NotYetEnqueued: Ainda não enfileirado
@@ -47,6 +56,7 @@
47
56
  Pause: Pausar
48
57
  Paused: Pausado
49
58
  PeakMemoryUsage: Pico de uso de memória
59
+ Uptime: Tempo de atividade
50
60
  Plugins: Plug-ins
51
61
  PollingInterval: Intervalo de Polling
52
62
  Process: Processo
@@ -63,14 +73,24 @@
63
73
  RetryNow: Tentar novamente agora
64
74
  Scheduled: Agendados
65
75
  ScheduledJobs: Tarefas agendadas
76
+ idle: Ocioso
77
+ active: Ativo
78
+ Seconds: Segundos
66
79
  ShowAll: Mostrar todos
67
80
  SixMonths: 6 meses
68
81
  Size: Tamanho
82
+ Started: Iniciado
69
83
  Stop: Parar
70
84
  StopAll: Parar Todos
71
85
  StopPolling: Parar Polling
86
+ Success: Sucesso
87
+ Summary: Resumo
72
88
  Thread: Thread
73
89
  Threads: Threads
90
+ ThreeMonths: 3 meses
91
+ TotalExecutionTime: Tempo total de execução
74
92
  Unpause: Despausar
75
93
  Utilization: Utilização
94
+ Version: Versão
95
+ When: Quando
76
96
  Worker: Trabalhador
@@ -37,7 +37,7 @@
37
37
  <tr>
38
38
  <th>BID</th>
39
39
  <td>
40
- <a href="<%= root_path %>batches/<%= job.bid %>"><%= job.bid %>
40
+ <a href="<%= root_path %>batches/<%= job.bid %>"><%= job.bid %></a>
41
41
  </td>
42
42
  </tr>
43
43
  <% end %>
@@ -1,5 +1,5 @@
1
1
  <div>
2
- <select class="form-control" onchange="window.location.href = '<%= path %>?period=' + event.target.value">
2
+ <select class="form-control" data-metric-period="<%= path %>">
3
3
  <% periods.each_key do |code| %>
4
4
 
5
5
  <% if code == period %>
@@ -1,39 +1,39 @@
1
1
  <ul class="list-unstyled summary row">
2
2
  <li class="processed col-sm-1">
3
- <span id="txtProcessed" class="count"><%= number_with_delimiter(stats.processed) %></span>
3
+ <span id="txtProcessed" class="count" data-nwp><%= stats.processed %></span>
4
4
  <span class="desc"><%= t('Processed') %></span>
5
5
  </li>
6
6
  <li class="failed col-sm-1">
7
- <span id="txtFailed" class="count"><%= number_with_delimiter(stats.failed) %></span>
7
+ <span id="txtFailed" class="count" data-nwp><%= stats.failed %></span>
8
8
  <span class="desc"><%= t('Failed') %></span>
9
9
  </li>
10
10
  <li class="busy col-sm-1">
11
11
  <a href="<%= root_path %>busy">
12
- <span id="txtBusy" class="count"><%= number_with_delimiter(workset.size) %></span>
12
+ <span id="txtBusy" class="count" data-nwp><%= workset.size %></span>
13
13
  <span class="desc"><%= t('Busy') %></span>
14
14
  </a>
15
15
  </li>
16
16
  <li class="enqueued col-sm-1">
17
17
  <a href="<%= root_path %>queues">
18
- <span id="txtEnqueued" class="count"><%= number_with_delimiter(stats.enqueued) %></span>
18
+ <span id="txtEnqueued" class="count" data-nwp><%= stats.enqueued %></span>
19
19
  <span class="desc"><%= t('Enqueued') %></span>
20
20
  </a>
21
21
  </li>
22
22
  <li class="retries col-sm-1">
23
23
  <a href="<%= root_path %>retries">
24
- <span id="txtRetries" class="count"><%= number_with_delimiter(stats.retry_size) %></span>
24
+ <span id="txtRetries" class="count" data-nwp><%= stats.retry_size %></span>
25
25
  <span class="desc"><%= t('Retries') %></span>
26
26
  </a>
27
27
  </li>
28
28
  <li class="scheduled col-sm-1">
29
29
  <a href="<%= root_path %>scheduled">
30
- <span id="txtScheduled" class="count"><%= number_with_delimiter(stats.scheduled_size) %></span>
30
+ <span id="txtScheduled" class="count" data-nwp><%= stats.scheduled_size %></span>
31
31
  <span class="desc"><%= t('Scheduled') %></span>
32
32
  </a>
33
33
  </li>
34
34
  <li class="dead col-sm-1">
35
35
  <a href="<%= root_path %>morgue">
36
- <span id="txtDead" class="count"><%= number_with_delimiter(stats.dead_size) %></span>
36
+ <span id="txtDead" class="count" data-nwp><%= stats.dead_size %></span>
37
37
  <span class="desc"><%= t('Dead') %></span>
38
38
  </a>
39
39
  </li>
data/web/views/busy.erb CHANGED
@@ -86,9 +86,9 @@
86
86
  <% end %>
87
87
  </td>
88
88
  <td><%= relative_time(Time.at(process['started_at'])) %></td>
89
- <td><%= format_memory(process['rss'].to_i) %></td>
90
- <td><%= process['concurrency'] %></td>
91
- <td><%= process['busy'] %></td>
89
+ <td class="num"><%= format_memory(process['rss'].to_i) %></td>
90
+ <td class="num"><%= number_with_delimiter(process['concurrency']) %></td>
91
+ <td class="num"><%= number_with_delimiter(process['busy']) %></td>
92
92
  <td>
93
93
  <% unless process.embedded? %>
94
94
  <form method="POST">
@@ -1,7 +1,3 @@
1
- <script type="text/javascript" src="<%= root_path %>javascripts/chart.min.js"></script>
2
- <script type="text/javascript" src="<%= root_path %>javascripts/chartjs-plugin-annotation.min.js"></script>
3
- <script type="text/javascript" src="<%= root_path %>javascripts/base-charts.js"></script>
4
- <script type="text/javascript" src="<%= root_path %>javascripts/dashboard-charts.js"></script>
5
1
  <script type="text/javascript" src="<%= root_path %>javascripts/dashboard.js"></script>
6
2
  <div class= "dashboard clearfix">
7
3
  <h3 >
@@ -20,26 +16,19 @@
20
16
  </div>
21
17
 
22
18
  <div class="row chart">
23
- <canvas id="realtime-chart"></canvas>
24
- <script>
25
- window.realtimeChart = new RealtimeChart(
26
- document.getElementById("realtime-chart"),
27
- <%= Sidekiq.dump_json({
28
- processedLabel: t('Processed'),
29
- failedLabel: t('Failed'),
30
- labels: Array.new(50, ""),
31
- processed: Array.new(50),
32
- failed: Array.new(50),
33
- updateUrl: "#{root_path}stats",
34
- }) %>
35
- )
36
- </script>
19
+ <canvas id="realtime-chart">
20
+ <%= to_json({
21
+ processedLabel: t('Processed'),
22
+ failedLabel: t('Failed'),
23
+ labels: Array.new(50, ""),
24
+ processed: Array.new(50),
25
+ failed: Array.new(50),
26
+ updateUrl: "#{root_path}stats",
27
+ }) %>
28
+ </canvas>
37
29
 
38
30
  <!-- start with a space in the legend so the height doesn't change when we add content dynamically -->
39
31
  <div id="realtime-legend">&nbsp;</div>
40
- <script>
41
- realtimeChart.registerLegend(document.getElementById("realtime-legend"))
42
- </script>
43
32
  </div>
44
33
 
45
34
  <div class="row header">
@@ -55,18 +44,14 @@
55
44
  <a href="<%= root_path %>?days=180" class="history-graph <%= "active" if params[:days] == "180" %>"><%= t('SixMonths') %></a>
56
45
  </div>
57
46
 
58
- <canvas id="history-chart"></canvas>
59
- <script>
60
- window.historyChart = new DashboardChart(
61
- document.getElementById("history-chart"),
62
- <%= Sidekiq.dump_json({
63
- processedLabel: t('Processed'),
64
- failedLabel: t('Failed'),
65
- processed: @processed_history.to_a.reverse,
66
- failed: @failed_history.to_a.reverse,
67
- }) %>
68
- )
69
- </script>
47
+ <canvas id="history-chart">
48
+ <%= to_json({
49
+ processedLabel: t('Processed'),
50
+ failedLabel: t('Failed'),
51
+ processed: @processed_history.to_a.reverse,
52
+ failed: @failed_history.to_a.reverse,
53
+ }) %>
54
+ </canvas>
70
55
  </div>
71
56
 
72
57
  <br/>
@@ -113,3 +98,8 @@
113
98
  <% end %>
114
99
  </div>
115
100
  </div>
101
+
102
+ <script type="text/javascript" src="<%= root_path %>javascripts/chart.min.js"></script>
103
+ <script type="text/javascript" src="<%= root_path %>javascripts/chartjs-plugin-annotation.min.js"></script>
104
+ <script type="text/javascript" src="<%= root_path %>javascripts/base-charts.js"></script>
105
+ <script type="text/javascript" src="<%= root_path %>javascripts/dashboard-charts.js"></script>
@@ -0,0 +1,7 @@
1
+ <div>
2
+ <form method="post" class="form-inline" action='<%= root_path %>filter/<%= which %>'>
3
+ <%= csrf_tag %>
4
+ <label for="substr"><%= t('Filter') %></label>
5
+ <input class="search form-control" type="search" name="substr" value="<%= h params[:substr] %>" placeholder="<%= t('AnyJobContent') %>"/>
6
+ </form>
7
+ </div>