sidekiq-error-groups 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.
@@ -0,0 +1,83 @@
1
+ <section>
2
+ <header>
3
+ <h1><%= t('ErrorGroups') %></h1>
4
+ </header>
5
+
6
+ <% if (notice = eg_notice) %>
7
+ <p><strong><%= h notice %></strong></p>
8
+ <% end %>
9
+
10
+ <p>
11
+ <strong><%= t('GroupBy') %>:</strong>
12
+ <% eg_group_modes.each do |mode, label| %>
13
+ <% if mode == eg_group_by %>
14
+ <strong><%= h label %></strong>
15
+ <% else %>
16
+ <a href="<%= h eg_index_path(mode) %>"><%= h label %></a>
17
+ <% end %>
18
+ <%= "&middot;" unless mode == eg_group_modes.last.first %>
19
+ <% end %>
20
+ </p>
21
+
22
+ <p>
23
+ <%= t('ScannedJobs', scanned: @summary.scanned, total: @summary.total) %>
24
+ <% @summary.set_sizes.each do |set, size| %>
25
+ &middot; <%= h set.to_s %>: <%= size %>
26
+ <% end %>
27
+ <% if @summary.truncated? %>
28
+ <br/><strong><%= t('ScanTruncated', max_scan: Sidekiq::ErrorGroups.config.max_scan) %></strong>
29
+ <% end %>
30
+ </p>
31
+
32
+ <% if @summary.empty? %>
33
+ <p><%= t('NoFailedJobs') %></p>
34
+ <% else %>
35
+ <div class="table_container">
36
+ <table>
37
+ <thead>
38
+ <tr>
39
+ <% if eg_group_by != :job %>
40
+ <th><%= t('ErrorClass') %></th>
41
+ <% end %>
42
+ <% if eg_group_by != :error %>
43
+ <th><%= t('JobClass') %></th>
44
+ <% end %>
45
+ <th><%= t('Total') %></th>
46
+ <th><%= t('Retries') %></th>
47
+ <th><%= t('Dead') %></th>
48
+ <th><%= t('LastFailure') %></th>
49
+ <th><%= t('FirstFailure') %></th>
50
+ <% if eg_group_by != :error_job %>
51
+ <th><%= (eg_group_by == :error) ? t('AffectedJobs') : t('AffectedErrors') %></th>
52
+ <% end %>
53
+ </tr>
54
+ </thead>
55
+ <tbody>
56
+ <% @summary.groups.each do |group| %>
57
+ <tr>
58
+ <% if eg_group_by != :job %>
59
+ <td>
60
+ <a href="<%= h eg_group_path(group.selector) %>"><%= h eg_label(group.error_class) %></a>
61
+ <% if group.sample && !group.sample.error_message.empty? %>
62
+ <br/><small><%= h truncate(group.sample.error_message, 120) %></small>
63
+ <% end %>
64
+ </td>
65
+ <% end %>
66
+ <% if eg_group_by != :error %>
67
+ <td><a href="<%= h eg_group_path(group.selector) %>"><%= h group.job_class.to_s %></a></td>
68
+ <% end %>
69
+ <td><%= group.count %></td>
70
+ <td><%= group.retry_count %></td>
71
+ <td><%= group.dead_count %></td>
72
+ <td><%= eg_time(group.last_failed_at) %></td>
73
+ <td><%= eg_time(group.first_failed_at) %></td>
74
+ <% if eg_group_by != :error_job %>
75
+ <td><small><%= h eg_breakdown(group) %></small></td>
76
+ <% end %>
77
+ </tr>
78
+ <% end %>
79
+ </tbody>
80
+ </table>
81
+ </div>
82
+ <% end %>
83
+ </section>
@@ -0,0 +1,114 @@
1
+ <header class="row">
2
+ <div class="col-sm-8 pull-left flip">
3
+ <h3><%= h eg_selector_label(@selector) %></h3>
4
+ <p><a href="<%= h eg_index_path %>">&larr; <%= t('BackToErrorGroups') %></a></p>
5
+ </div>
6
+ </header>
7
+
8
+ <% if @group.count.zero? %>
9
+ <div class="alert alert-success"><%= t('GroupEmpty') %></div>
10
+ <% else %>
11
+ <div class="table_container">
12
+ <table class="table table-bordered table-striped table-white">
13
+ <tbody>
14
+ <% if @selector.error_class %>
15
+ <tr><th><%= t('ErrorClass') %></th><td><%= h eg_label(@selector.error_class) %></td></tr>
16
+ <% end %>
17
+ <% if @selector.job_class %>
18
+ <tr><th><%= t('JobClass') %></th><td><%= h @selector.job_class %></td></tr>
19
+ <% end %>
20
+ <tr><th><%= t('Total') %></th><td><%= @group.count %></td></tr>
21
+ <tr><th><%= t('Retries') %></th><td><%= @group.retry_count %></td></tr>
22
+ <tr><th><%= t('Dead') %></th><td><%= @group.dead_count %></td></tr>
23
+ <tr><th><%= t('FirstFailure') %></th><td><%= eg_time(@group.first_failed_at) %></td></tr>
24
+ <tr><th><%= t('LastFailure') %></th><td><%= eg_time(@group.last_failed_at) %></td></tr>
25
+ <% unless @group.breakdown.empty? %>
26
+ <tr>
27
+ <th><%= (@group.mode == :error) ? t('AffectedJobs') : t('AffectedErrors') %></th>
28
+ <td><%= h eg_breakdown(@group) %></td>
29
+ </tr>
30
+ <% end %>
31
+ <% if @group.sample && !@group.sample.error_message.empty? %>
32
+ <tr><th><%= t('ErrorMessage') %></th><td><code><%= h @group.sample.error_message %></code></td></tr>
33
+ <% end %>
34
+ <% backtrace = eg_backtrace(@group.sample) %>
35
+ <% unless backtrace.empty? %>
36
+ <tr>
37
+ <th><%= t('Backtrace') %></th>
38
+ <td><code><%= backtrace.map { |line| h(line) }.join("<br/>") %></code></td>
39
+ </tr>
40
+ <% end %>
41
+ </tbody>
42
+ </table>
43
+ </div>
44
+
45
+ <div class="row">
46
+ <form action="<%= h eg_group_path(@selector, 'retry') %>" method="post" class="pull-left flip">
47
+ <%= csrf_tag %>
48
+ <%= eg_selector_fields(@selector) %>
49
+ <input class="btn btn-primary" type="submit" value="<%= t('RetryGroup') %>"
50
+ data-confirm="<%= t('AreYouSureRetryGroup', count: @group.count) %>" />
51
+ </form>
52
+ <% if @group.retry_count > 0 %>
53
+ <form action="<%= h eg_group_path(@selector, 'kill') %>" method="post" class="pull-left flip">
54
+ <%= csrf_tag %>
55
+ <%= eg_selector_fields(@selector) %>
56
+ <input class="btn btn-danger" type="submit" value="<%= t('KillGroup') %>"
57
+ data-confirm="<%= t('AreYouSureKillGroup', count: @group.retry_count) %>" />
58
+ </form>
59
+ <% end %>
60
+ <form action="<%= h eg_group_path(@selector, 'delete') %>" method="post" class="pull-left flip">
61
+ <%= csrf_tag %>
62
+ <%= eg_selector_fields(@selector) %>
63
+ <input class="btn btn-danger" type="submit" value="<%= t('DeleteGroup') %>"
64
+ data-confirm="<%= t('AreYouSureDeleteGroup', count: @group.count) %>" />
65
+ </form>
66
+ </div>
67
+
68
+ <% if @page.truncated %>
69
+ <p><strong><%= t('ScanTruncated', max_scan: Sidekiq::ErrorGroups.config.max_scan) %></strong></p>
70
+ <% end %>
71
+
72
+ <div class="table_container">
73
+ <table class="table table-striped table-bordered table-white">
74
+ <thead>
75
+ <tr>
76
+ <th><%= t('Set') %></th>
77
+ <th><%= t('LastFailure') %></th>
78
+ <th><%= t('Queue') %></th>
79
+ <th><%= t('Job') %></th>
80
+ <% unless @selector.error_class %>
81
+ <th><%= t('ErrorClass') %></th>
82
+ <% end %>
83
+ <th><%= t('Arguments') %></th>
84
+ <th><%= t('Attempts') %></th>
85
+ </tr>
86
+ </thead>
87
+ <tbody>
88
+ <% @page.jobs.each do |job| %>
89
+ <tr>
90
+ <td><%= h job.set.to_s %></td>
91
+ <td><a href="<%= eg_job_path(job) %>"><%= eg_time(job.last_failure_at) %></a></td>
92
+ <td><a href="<%= root_path %>queues/<%= h job.queue.to_s %>"><%= h job.queue.to_s %></a></td>
93
+ <td><%= h job.display_class %></td>
94
+ <% unless @selector.error_class %>
95
+ <td><%= h eg_label(job.error_class) %></td>
96
+ <% end %>
97
+ <td><%= display_args(job.item["args"]) %></td>
98
+ <td><%= job.retry_count %></td>
99
+ </tr>
100
+ <% end %>
101
+ </tbody>
102
+ </table>
103
+ </div>
104
+
105
+ <p>
106
+ <% if @page.page > 1 %>
107
+ <a href="<%= h eg_page_path(@selector, @page.page - 1) %>">&larr; <%= t('PreviousPage') %></a>
108
+ <% end %>
109
+ <%= t('PageOf', page: @page.page, pages: @page.pages) %>
110
+ <% if @page.page < @page.pages %>
111
+ <a href="<%= h eg_page_path(@selector, @page.page + 1) %>"><%= t('NextPage') %> &rarr;</a>
112
+ <% end %>
113
+ </p>
114
+ <% end %>
@@ -0,0 +1,83 @@
1
+ <header class="row">
2
+ <div class="col-sm-5 pull-left flip">
3
+ <h3><%= t('ErrorGroups') %></h3>
4
+ </div>
5
+ </header>
6
+
7
+ <% if (notice = eg_notice) %>
8
+ <div class="alert alert-success"><%= h notice %></div>
9
+ <% end %>
10
+
11
+ <p>
12
+ <strong><%= t('GroupBy') %>:</strong>
13
+ <% eg_group_modes.each do |mode, label| %>
14
+ <% if mode == eg_group_by %>
15
+ <strong><%= h label %></strong>
16
+ <% else %>
17
+ <a href="<%= h eg_index_path(mode) %>"><%= h label %></a>
18
+ <% end %>
19
+ <%= "&middot;" unless mode == eg_group_modes.last.first %>
20
+ <% end %>
21
+ </p>
22
+
23
+ <p>
24
+ <%= t('ScannedJobs', scanned: @summary.scanned, total: @summary.total) %>
25
+ <% @summary.set_sizes.each do |set, size| %>
26
+ &middot; <%= h set.to_s %>: <%= size %>
27
+ <% end %>
28
+ <% if @summary.truncated? %>
29
+ <br/><strong><%= t('ScanTruncated', max_scan: Sidekiq::ErrorGroups.config.max_scan) %></strong>
30
+ <% end %>
31
+ </p>
32
+
33
+ <% if @summary.empty? %>
34
+ <div class="alert alert-success"><%= t('NoFailedJobs') %></div>
35
+ <% else %>
36
+ <div class="table_container">
37
+ <table class="table table-striped table-bordered table-white">
38
+ <thead>
39
+ <tr>
40
+ <% if eg_group_by != :job %>
41
+ <th><%= t('ErrorClass') %></th>
42
+ <% end %>
43
+ <% if eg_group_by != :error %>
44
+ <th><%= t('JobClass') %></th>
45
+ <% end %>
46
+ <th><%= t('Total') %></th>
47
+ <th><%= t('Retries') %></th>
48
+ <th><%= t('Dead') %></th>
49
+ <th><%= t('LastFailure') %></th>
50
+ <th><%= t('FirstFailure') %></th>
51
+ <% if eg_group_by != :error_job %>
52
+ <th><%= (eg_group_by == :error) ? t('AffectedJobs') : t('AffectedErrors') %></th>
53
+ <% end %>
54
+ </tr>
55
+ </thead>
56
+ <tbody>
57
+ <% @summary.groups.each do |group| %>
58
+ <tr>
59
+ <% if eg_group_by != :job %>
60
+ <td>
61
+ <a href="<%= h eg_group_path(group.selector) %>"><%= h eg_label(group.error_class) %></a>
62
+ <% if group.sample && !group.sample.error_message.empty? %>
63
+ <br/><small><%= h truncate(group.sample.error_message, 120) %></small>
64
+ <% end %>
65
+ </td>
66
+ <% end %>
67
+ <% if eg_group_by != :error %>
68
+ <td><a href="<%= h eg_group_path(group.selector) %>"><%= h group.job_class.to_s %></a></td>
69
+ <% end %>
70
+ <td><%= group.count %></td>
71
+ <td><%= group.retry_count %></td>
72
+ <td><%= group.dead_count %></td>
73
+ <td><%= eg_time(group.last_failed_at) %></td>
74
+ <td><%= eg_time(group.first_failed_at) %></td>
75
+ <% if eg_group_by != :error_job %>
76
+ <td><small><%= h eg_breakdown(group) %></small></td>
77
+ <% end %>
78
+ </tr>
79
+ <% end %>
80
+ </tbody>
81
+ </table>
82
+ </div>
83
+ <% end %>
metadata ADDED
@@ -0,0 +1,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sidekiq-error-groups
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - r3cha
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: sidekiq
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '6.0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '6.0'
26
+ description: Adds an "Error groups" tab to the Sidekiq Web UI that groups retrying
27
+ and dead jobs by their error class, with per-group retry/delete/kill actions, plus
28
+ a Ruby API, a rake task and a CLI. Works with Sidekiq 6, 7 and 8.
29
+ email:
30
+ - frontandstart@gmail.com
31
+ executables:
32
+ - sidekiq-error-groups
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - CHANGELOG.md
37
+ - LICENSE.txt
38
+ - README.md
39
+ - exe/sidekiq-error-groups
40
+ - lib/sidekiq-error-groups.rb
41
+ - lib/sidekiq/error_groups.rb
42
+ - lib/sidekiq/error_groups/actions.rb
43
+ - lib/sidekiq/error_groups/aggregator.rb
44
+ - lib/sidekiq/error_groups/cli.rb
45
+ - lib/sidekiq/error_groups/compat.rb
46
+ - lib/sidekiq/error_groups/config.rb
47
+ - lib/sidekiq/error_groups/formatter.rb
48
+ - lib/sidekiq/error_groups/group.rb
49
+ - lib/sidekiq/error_groups/job.rb
50
+ - lib/sidekiq/error_groups/job_query.rb
51
+ - lib/sidekiq/error_groups/railtie.rb
52
+ - lib/sidekiq/error_groups/scanner.rb
53
+ - lib/sidekiq/error_groups/selector.rb
54
+ - lib/sidekiq/error_groups/summary.rb
55
+ - lib/sidekiq/error_groups/tasks.rake
56
+ - lib/sidekiq/error_groups/version.rb
57
+ - lib/sidekiq/error_groups/web.rb
58
+ - lib/sidekiq/error_groups/web_extension.rb
59
+ - sidekiq-error-groups.gemspec
60
+ - web/locales/en.yml
61
+ - web/locales/ru.yml
62
+ - web/views/group.erb
63
+ - web/views/index.erb
64
+ - web/views/legacy/group.erb
65
+ - web/views/legacy/index.erb
66
+ homepage: https://github.com/r3cha/sidekiq-error-groups
67
+ licenses:
68
+ - MIT
69
+ metadata:
70
+ homepage_uri: https://github.com/r3cha/sidekiq-error-groups
71
+ source_code_uri: https://github.com/r3cha/sidekiq-error-groups
72
+ changelog_uri: https://github.com/r3cha/sidekiq-error-groups/blob/main/CHANGELOG.md
73
+ bug_tracker_uri: https://github.com/r3cha/sidekiq-error-groups/issues
74
+ documentation_uri: https://github.com/r3cha/sidekiq-error-groups#readme
75
+ rubygems_mfa_required: 'true'
76
+ rdoc_options: []
77
+ require_paths:
78
+ - lib
79
+ required_ruby_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: 2.7.0
84
+ required_rubygems_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ requirements: []
90
+ rubygems_version: 3.6.9
91
+ specification_version: 4
92
+ summary: Sidekiq Web UI tab with failed jobs grouped by error class.
93
+ test_files: []