rails_error_dashboard 0.8.3 → 0.9.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 +4 -4
- data/README.md +35 -0
- data/app/controllers/rails_error_dashboard/application_controller.rb +156 -0
- data/app/controllers/rails_error_dashboard/errors_controller.rb +95 -73
- data/app/controllers/rails_error_dashboard/locales_controller.rb +87 -0
- data/app/helpers/rails_error_dashboard/application_helper.rb +20 -14
- data/app/helpers/rails_error_dashboard/backtrace_helper.rb +15 -6
- data/app/helpers/rails_error_dashboard/i18n_helper.rb +154 -0
- data/app/helpers/rails_error_dashboard/mailer_i18n_helper.rb +69 -0
- data/app/helpers/rails_error_dashboard/overview_helper.rb +18 -6
- data/app/helpers/rails_error_dashboard/user_agent_helper.rb +25 -11
- data/app/jobs/rails_error_dashboard/application_job.rb +5 -0
- data/app/jobs/rails_error_dashboard/baseline_alert_job.rb +18 -16
- data/app/jobs/rails_error_dashboard/concerns/localized_job.rb +83 -0
- data/app/jobs/rails_error_dashboard/discord_error_notification_job.rb +4 -2
- data/app/jobs/rails_error_dashboard/email_error_notification_job.rb +4 -2
- data/app/jobs/rails_error_dashboard/pagerduty_error_notification_job.rb +6 -2
- data/app/jobs/rails_error_dashboard/scheduled_digest_job.rb +8 -3
- data/app/jobs/rails_error_dashboard/slack_error_notification_job.rb +6 -4
- data/app/jobs/rails_error_dashboard/storm_notification_job.rb +26 -8
- data/app/jobs/rails_error_dashboard/webhook_error_notification_job.rb +4 -2
- data/app/mailers/rails_error_dashboard/application_mailer.rb +14 -0
- data/app/mailers/rails_error_dashboard/digest_mailer.rb +18 -2
- data/app/mailers/rails_error_dashboard/error_notification_mailer.rb +19 -2
- data/app/views/layouts/rails_error_dashboard.html.erb +255 -92
- data/app/views/rails_error_dashboard/digest_mailer/digest_summary.html.erb +23 -22
- data/app/views/rails_error_dashboard/digest_mailer/digest_summary.text.erb +30 -19
- data/app/views/rails_error_dashboard/error_notification_mailer/error_alert.html.erb +18 -16
- data/app/views/rails_error_dashboard/error_notification_mailer/error_alert.text.erb +21 -14
- data/app/views/rails_error_dashboard/errors/_ai_help_panel.html.erb +11 -7
- data/app/views/rails_error_dashboard/errors/_breadcrumbs_group.html.erb +45 -38
- data/app/views/rails_error_dashboard/errors/_co_occurring_errors.html.erb +17 -14
- data/app/views/rails_error_dashboard/errors/_discussion.html.erb +27 -9
- data/app/views/rails_error_dashboard/errors/_error_cascades.html.erb +25 -21
- data/app/views/rails_error_dashboard/errors/_error_info.html.erb +25 -22
- data/app/views/rails_error_dashboard/errors/_error_row.html.erb +24 -12
- data/app/views/rails_error_dashboard/errors/_instance_variables.html.erb +12 -11
- data/app/views/rails_error_dashboard/errors/_issue_section.html.erb +19 -13
- data/app/views/rails_error_dashboard/errors/_llm_summary.html.erb +14 -14
- data/app/views/rails_error_dashboard/errors/_local_variables.html.erb +8 -8
- data/app/views/rails_error_dashboard/errors/_modals.html.erb +60 -52
- data/app/views/rails_error_dashboard/errors/_pattern_insights.html.erb +59 -49
- data/app/views/rails_error_dashboard/errors/_request_context.html.erb +23 -20
- data/app/views/rails_error_dashboard/errors/_show_scripts.html.erb +14 -14
- data/app/views/rails_error_dashboard/errors/_sidebar_metadata.html.erb +118 -93
- data/app/views/rails_error_dashboard/errors/_similar_errors.html.erb +18 -12
- data/app/views/rails_error_dashboard/errors/_source_code.html.erb +20 -5
- data/app/views/rails_error_dashboard/errors/_stats.html.erb +5 -5
- data/app/views/rails_error_dashboard/errors/_timeline.html.erb +7 -7
- data/app/views/rails_error_dashboard/errors/_user_errors_table.html.erb +15 -12
- data/app/views/rails_error_dashboard/errors/actioncable_health_summary.html.erb +25 -25
- data/app/views/rails_error_dashboard/errors/activestorage_health_summary.html.erb +28 -28
- data/app/views/rails_error_dashboard/errors/analytics.html.erb +82 -82
- data/app/views/rails_error_dashboard/errors/cache_health_summary.html.erb +27 -27
- data/app/views/rails_error_dashboard/errors/correlation.html.erb +61 -57
- data/app/views/rails_error_dashboard/errors/database_health_summary.html.erb +64 -65
- data/app/views/rails_error_dashboard/errors/deprecations.html.erb +23 -23
- data/app/views/rails_error_dashboard/errors/diagnostic_dumps.html.erb +48 -36
- data/app/views/rails_error_dashboard/errors/index.html.erb +121 -70
- data/app/views/rails_error_dashboard/errors/job_health_summary.html.erb +30 -30
- data/app/views/rails_error_dashboard/errors/llm_health_summary.html.erb +38 -39
- data/app/views/rails_error_dashboard/errors/n_plus_one_summary.html.erb +28 -25
- data/app/views/rails_error_dashboard/errors/overview.html.erb +45 -39
- data/app/views/rails_error_dashboard/errors/platform_comparison.html.erb +45 -44
- data/app/views/rails_error_dashboard/errors/rack_attack_summary.html.erb +39 -28
- data/app/views/rails_error_dashboard/errors/releases.html.erb +46 -47
- data/app/views/rails_error_dashboard/errors/settings/_value_badge.html.erb +48 -44
- data/app/views/rails_error_dashboard/errors/settings.html.erb +122 -112
- data/app/views/rails_error_dashboard/errors/show.html.erb +44 -37
- data/app/views/rails_error_dashboard/errors/storms.html.erb +30 -24
- data/app/views/rails_error_dashboard/errors/swallowed_exceptions.html.erb +26 -26
- data/app/views/rails_error_dashboard/errors/user_impact.html.erb +31 -30
- data/config/locales/de.yml +2007 -0
- data/config/locales/en.yml +2719 -0
- data/config/locales/es.yml +2079 -0
- data/config/locales/fr.yml +2082 -0
- data/config/locales/it.yml +2060 -0
- data/config/locales/ja.yml +1802 -0
- data/config/locales/pl.yml +2108 -0
- data/config/locales/pt-BR.yml +2064 -0
- data/config/locales/ru.yml +2109 -0
- data/config/locales/uk.yml +2106 -0
- data/config/locales/zh-CN.yml +1791 -0
- data/config/routes.rb +5 -0
- data/db/migrate/20260325000001_fix_swallowed_exceptions_index_for_mysql.rb +9 -3
- data/lib/rails_error_dashboard/commands/batch_delete_errors.rb +3 -1
- data/lib/rails_error_dashboard/commands/batch_mute_errors.rb +4 -2
- data/lib/rails_error_dashboard/commands/batch_resolve_errors.rb +4 -2
- data/lib/rails_error_dashboard/commands/batch_unmute_errors.rb +4 -2
- data/lib/rails_error_dashboard/commands/create_issue.rb +5 -3
- data/lib/rails_error_dashboard/commands/link_existing_issue.rb +4 -2
- data/lib/rails_error_dashboard/commands/log_error.rb +1 -1
- data/lib/rails_error_dashboard/configuration.rb +23 -0
- data/lib/rails_error_dashboard/current.rb +56 -0
- data/lib/rails_error_dashboard/i18n_store.rb +293 -0
- data/lib/rails_error_dashboard/private_backend.rb +213 -0
- data/lib/rails_error_dashboard/services/baseline_alert_payload_builder.rb +89 -22
- data/lib/rails_error_dashboard/services/digest_builder.rb +21 -7
- data/lib/rails_error_dashboard/services/discord_payload_builder.rb +27 -15
- data/lib/rails_error_dashboard/services/error_notification_dispatcher.rb +10 -5
- data/lib/rails_error_dashboard/services/localized_time_formatter.rb +117 -0
- data/lib/rails_error_dashboard/services/notification_helpers.rb +87 -4
- data/lib/rails_error_dashboard/services/pagerduty_payload_builder.rb +22 -5
- data/lib/rails_error_dashboard/services/slack_payload_builder.rb +48 -28
- data/lib/rails_error_dashboard/services/storm_protection/gate.rb +2 -1
- data/lib/rails_error_dashboard/services/webhook_payload_builder.rb +15 -1
- data/lib/rails_error_dashboard/translation.rb +39 -0
- data/lib/rails_error_dashboard/version.rb +1 -1
- data/lib/rails_error_dashboard.rb +4 -0
- data/lib/tasks/error_dashboard.rake +5 -1
- metadata +24 -4
|
@@ -8,25 +8,25 @@
|
|
|
8
8
|
<div class="card mb-4" id="section-deprecation-warnings" style="border-color: var(--bs-danger);">
|
|
9
9
|
<div class="card-header">
|
|
10
10
|
<h5 class="mb-0">
|
|
11
|
-
<i class="bi bi-exclamation-triangle text-danger"></i>
|
|
11
|
+
<i class="bi bi-exclamation-triangle text-danger"></i> <%= red_t("red.errors.deprecations.title") %>
|
|
12
12
|
<span class="badge bg-danger"><%= deprecation_crumbs.size %></span>
|
|
13
13
|
</h5>
|
|
14
|
-
<small class="text-muted"
|
|
14
|
+
<small class="text-muted"><%= red_t("red.errors.deprecations.hint") %></small>
|
|
15
15
|
</div>
|
|
16
16
|
<div class="card-body p-0">
|
|
17
17
|
<div class="table-responsive">
|
|
18
18
|
<table class="table table-sm table-hover mb-0">
|
|
19
19
|
<thead class="table-light">
|
|
20
20
|
<tr>
|
|
21
|
-
<th
|
|
22
|
-
<th width="300"
|
|
21
|
+
<th><%= red_t("red.errors.deprecations.column_warning") %></th>
|
|
22
|
+
<th width="300"><%= red_t("red.errors.deprecations.column_source") %></th>
|
|
23
23
|
</tr>
|
|
24
24
|
</thead>
|
|
25
25
|
<tbody>
|
|
26
26
|
<% deprecation_crumbs.each do |crumb| %>
|
|
27
27
|
<tr>
|
|
28
28
|
<td><code style="font-size: 0.85em;"><%= crumb["m"] %></code></td>
|
|
29
|
-
<td><small class="text-muted"><%= crumb.dig("meta", "caller") || "
|
|
29
|
+
<td><small class="text-muted"><%= crumb.dig("meta", "caller") || red_t("red.common.not_available") %></small></td>
|
|
30
30
|
</tr>
|
|
31
31
|
<% end %>
|
|
32
32
|
</tbody>
|
|
@@ -35,11 +35,11 @@
|
|
|
35
35
|
</div>
|
|
36
36
|
<div class="card-footer bg-white border-top">
|
|
37
37
|
<small class="text-muted">
|
|
38
|
-
<i class="bi bi-lightbulb text-warning"></i>
|
|
38
|
+
<i class="bi bi-lightbulb text-warning"></i> <%= red_t("red.errors.deprecations.footer_tip") %>
|
|
39
39
|
</small>
|
|
40
40
|
<small class="float-end">
|
|
41
41
|
<a href="https://guides.rubyonrails.org/upgrading_ruby_on_rails.html" target="_blank" rel="noopener" class="text-decoration-none">
|
|
42
|
-
<i class="bi bi-book"></i>
|
|
42
|
+
<i class="bi bi-book"></i> <%= red_t("red.errors.deprecations.upgrade_guide") %> <i class="bi bi-box-arrow-up-right" style="font-size: 0.7em;"></i>
|
|
43
43
|
</a>
|
|
44
44
|
</small>
|
|
45
45
|
</div>
|
|
@@ -52,19 +52,19 @@
|
|
|
52
52
|
<div class="card mb-4" id="section-n-plus-one" style="border-color: var(--bs-warning);">
|
|
53
53
|
<div class="card-header">
|
|
54
54
|
<h5 class="mb-0">
|
|
55
|
-
<i class="bi bi-arrow-repeat text-warning"></i>
|
|
56
|
-
<span class="badge bg-warning text-dark"><%=
|
|
55
|
+
<i class="bi bi-arrow-repeat text-warning"></i> <%= red_t("red.errors.n_plus_one.title") %>
|
|
56
|
+
<span class="badge bg-warning text-dark"><%= red_tp("red.errors.n_plus_one.patterns", count: n_plus_one_patterns.size) %></span>
|
|
57
57
|
</h5>
|
|
58
|
-
<small class="text-muted"
|
|
58
|
+
<small class="text-muted"><%= red_t("red.errors.n_plus_one.hint") %></small>
|
|
59
59
|
</div>
|
|
60
60
|
<div class="card-body p-0">
|
|
61
61
|
<div class="table-responsive">
|
|
62
62
|
<table class="table table-sm table-hover mb-0">
|
|
63
63
|
<thead class="table-light">
|
|
64
64
|
<tr>
|
|
65
|
-
<th width="80"
|
|
66
|
-
<th
|
|
67
|
-
<th width="100"
|
|
65
|
+
<th width="80"><%= red_t("red.errors.n_plus_one.column_repeats") %></th>
|
|
66
|
+
<th><%= red_t("red.errors.n_plus_one.column_query") %></th>
|
|
67
|
+
<th width="100"><%= red_t("red.errors.n_plus_one.column_total_time") %></th>
|
|
68
68
|
</tr>
|
|
69
69
|
</thead>
|
|
70
70
|
<tbody>
|
|
@@ -73,14 +73,21 @@
|
|
|
73
73
|
<td><span class="badge bg-warning text-dark"><%= pattern[:count] %>x</span></td>
|
|
74
74
|
<td>
|
|
75
75
|
<code style="word-break: break-all; font-size: 0.85em;"><%= truncate(pattern[:sample_query], length: 200) %></code>
|
|
76
|
-
<br><small class="text-muted"
|
|
76
|
+
<br><small class="text-muted"><%= red_t("red.errors.n_plus_one.fingerprint", fingerprint: truncate(pattern[:fingerprint], length: 120)) %></small>
|
|
77
77
|
<% table_name = extract_table_from_sql(pattern[:sample_query]) %>
|
|
78
78
|
<br><small class="text-muted">
|
|
79
79
|
<i class="bi bi-lightbulb text-warning"></i>
|
|
80
|
+
<%# Method names stay English; only the words
|
|
81
|
+
around them are translated. %>
|
|
80
82
|
<% if table_name %>
|
|
81
|
-
|
|
83
|
+
<%= red_t("red.errors.n_plus_one.tip_preload_html",
|
|
84
|
+
table: tag.strong(table_name),
|
|
85
|
+
includes: tag.code(".includes(:association)"),
|
|
86
|
+
preload: tag.code(".preload(:association)")) %>
|
|
82
87
|
<% else %>
|
|
83
|
-
|
|
88
|
+
<%= red_t("red.errors.n_plus_one.tip_generic_html",
|
|
89
|
+
includes: tag.code(".includes"),
|
|
90
|
+
preload: tag.code(".preload")) %>
|
|
84
91
|
<% end %>
|
|
85
92
|
</small>
|
|
86
93
|
</td>
|
|
@@ -98,7 +105,7 @@
|
|
|
98
105
|
<div class="card-footer bg-white border-top">
|
|
99
106
|
<small class="float-end">
|
|
100
107
|
<a href="https://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations" target="_blank" rel="noopener" class="text-decoration-none">
|
|
101
|
-
<i class="bi bi-book"></i>
|
|
108
|
+
<i class="bi bi-book"></i> <%= red_t("red.errors.n_plus_one.eager_loading_guide") %> <i class="bi bi-box-arrow-up-right" style="font-size: 0.7em;"></i>
|
|
102
109
|
</a>
|
|
103
110
|
</small>
|
|
104
111
|
</div>
|
|
@@ -111,30 +118,30 @@
|
|
|
111
118
|
<div class="card mb-4" id="section-cache-health" style="border-color: var(--bs-info);">
|
|
112
119
|
<div class="card-header">
|
|
113
120
|
<h5 class="mb-0">
|
|
114
|
-
<i class="bi bi-lightning-charge text-info"></i>
|
|
115
|
-
<span class="badge bg-info text-dark"><%= cache_analysis[:reads] + cache_analysis[:writes]
|
|
121
|
+
<i class="bi bi-lightning-charge text-info"></i> <%= red_t("red.errors.cache_health.title") %>
|
|
122
|
+
<span class="badge bg-info text-dark"><%= red_tp("red.errors.cache_health.operations", count: cache_analysis[:reads] + cache_analysis[:writes]) %></span>
|
|
116
123
|
</h5>
|
|
117
|
-
<small class="text-muted"
|
|
124
|
+
<small class="text-muted"><%= red_t("red.errors.cache_health.hint") %></small>
|
|
118
125
|
</div>
|
|
119
126
|
<div class="card-body">
|
|
120
127
|
<div class="row text-center mb-3">
|
|
121
128
|
<div class="col-4">
|
|
122
129
|
<div class="fs-4 fw-bold"><%= cache_analysis[:reads] %></div>
|
|
123
|
-
<small class="text-muted"
|
|
130
|
+
<small class="text-muted"><%= red_t("red.errors.cache_health.reads") %></small>
|
|
124
131
|
</div>
|
|
125
132
|
<div class="col-4">
|
|
126
133
|
<div class="fs-4 fw-bold"><%= cache_analysis[:writes] %></div>
|
|
127
|
-
<small class="text-muted"
|
|
134
|
+
<small class="text-muted"><%= red_t("red.errors.cache_health.writes") %></small>
|
|
128
135
|
</div>
|
|
129
136
|
<div class="col-4">
|
|
130
137
|
<% if cache_analysis[:hit_rate] %>
|
|
131
138
|
<div class="fs-4 fw-bold text-<%= cache_analysis[:hit_rate] >= 80 ? 'success' : cache_analysis[:hit_rate] >= 50 ? 'warning' : 'danger' %>">
|
|
132
139
|
<%= cache_analysis[:hit_rate] %>%
|
|
133
140
|
</div>
|
|
134
|
-
<small class="text-muted"
|
|
141
|
+
<small class="text-muted"><%= red_t("red.errors.cache_health.hit_rate") %></small>
|
|
135
142
|
<% else %>
|
|
136
143
|
<div class="fs-4 fw-bold text-muted">--</div>
|
|
137
|
-
<small class="text-muted"
|
|
144
|
+
<small class="text-muted"><%= red_t("red.errors.cache_health.hit_rate") %></small>
|
|
138
145
|
<% end %>
|
|
139
146
|
</div>
|
|
140
147
|
</div>
|
|
@@ -142,8 +149,8 @@
|
|
|
142
149
|
<% if cache_analysis[:hits] > 0 || cache_analysis[:misses] > 0 %>
|
|
143
150
|
<div class="mb-3">
|
|
144
151
|
<div class="d-flex justify-content-between mb-1">
|
|
145
|
-
<small><span class="badge bg-success"><%= cache_analysis[:hits]
|
|
146
|
-
<small><span class="badge bg-danger"><%= cache_analysis[:misses]
|
|
152
|
+
<small><span class="badge bg-success"><%= red_tp("red.errors.cache_health.hits", count: cache_analysis[:hits]) %></span></small>
|
|
153
|
+
<small><span class="badge bg-danger"><%= red_tp("red.errors.cache_health.misses", count: cache_analysis[:misses]) %></span></small>
|
|
147
154
|
</div>
|
|
148
155
|
<% hit_pct = cache_analysis[:hit_rate] || 0 %>
|
|
149
156
|
<div class="progress" style="height: 6px;">
|
|
@@ -155,14 +162,14 @@
|
|
|
155
162
|
|
|
156
163
|
<% if cache_analysis[:unknown] > 0 %>
|
|
157
164
|
<small class="text-muted d-block mb-2">
|
|
158
|
-
<i class="bi bi-info-circle"></i> <%=
|
|
165
|
+
<i class="bi bi-info-circle"></i> <%= red_tp("red.errors.cache_health.unknown_status", count: cache_analysis[:unknown]) %>
|
|
159
166
|
</small>
|
|
160
167
|
<% end %>
|
|
161
168
|
|
|
162
169
|
<div class="d-flex justify-content-between">
|
|
163
|
-
<small class="text-muted"
|
|
170
|
+
<small class="text-muted"><%= red_t("red.errors.cache_health.total_time_html", duration: tag.strong("#{"%.1f" % cache_analysis[:total_duration_ms]}ms")) %></small>
|
|
164
171
|
<% if cache_analysis[:slowest] && cache_analysis[:slowest][:duration_ms] > 0 %>
|
|
165
|
-
<small class="text-muted"
|
|
172
|
+
<small class="text-muted"><%= red_t("red.errors.cache_health.slowest_html", duration: tag.strong("#{"%.1f" % cache_analysis[:slowest][:duration_ms]}ms")) %></small>
|
|
166
173
|
<% end %>
|
|
167
174
|
</div>
|
|
168
175
|
|
|
@@ -171,9 +178,9 @@
|
|
|
171
178
|
<small>
|
|
172
179
|
<i class="bi bi-lightbulb text-warning"></i>
|
|
173
180
|
<% if cache_analysis[:hit_rate] < 50 %>
|
|
174
|
-
|
|
181
|
+
<%= red_t("red.errors.cache_health.low_hit_rate") %>
|
|
175
182
|
<% else %>
|
|
176
|
-
|
|
183
|
+
<%= red_t("red.errors.cache_health.moderate_hit_rate") %>
|
|
177
184
|
<% end %>
|
|
178
185
|
</small>
|
|
179
186
|
</div>
|
|
@@ -182,7 +189,7 @@
|
|
|
182
189
|
<div class="card-footer bg-white border-top">
|
|
183
190
|
<small class="float-end">
|
|
184
191
|
<a href="https://guides.rubyonrails.org/caching_with_rails.html" target="_blank" rel="noopener" class="text-decoration-none">
|
|
185
|
-
<i class="bi bi-book"></i>
|
|
192
|
+
<i class="bi bi-book"></i> <%= red_t("red.errors.cache_health.caching_guide") %> <i class="bi bi-box-arrow-up-right" style="font-size: 0.7em;"></i>
|
|
186
193
|
</a>
|
|
187
194
|
</small>
|
|
188
195
|
</div>
|
|
@@ -192,10 +199,10 @@
|
|
|
192
199
|
<div class="card mb-4" id="section-breadcrumbs">
|
|
193
200
|
<div class="card-header">
|
|
194
201
|
<h5 class="mb-0">
|
|
195
|
-
<i class="bi bi-signpost-2"></i>
|
|
196
|
-
<span class="badge bg-secondary"><%= breadcrumbs.size
|
|
202
|
+
<i class="bi bi-signpost-2"></i> <%= red_t("red.errors.breadcrumbs.title") %>
|
|
203
|
+
<span class="badge bg-secondary"><%= red_tp("red.errors.breadcrumbs.events", count: breadcrumbs.size) %></span>
|
|
197
204
|
</h5>
|
|
198
|
-
<small class="text-muted"
|
|
205
|
+
<small class="text-muted"><%= red_t("red.errors.breadcrumbs.hint") %></small>
|
|
199
206
|
</div>
|
|
200
207
|
<div class="card-body p-0" style="max-height: 400px; overflow-y: auto;">
|
|
201
208
|
<div class="table-responsive">
|
|
@@ -203,9 +210,9 @@
|
|
|
203
210
|
<thead class="table-light">
|
|
204
211
|
<tr>
|
|
205
212
|
<th width="60">#</th>
|
|
206
|
-
<th width="100"
|
|
207
|
-
<th
|
|
208
|
-
<th width="100"
|
|
213
|
+
<th width="100"><%= red_t("red.errors.breadcrumbs.column_category") %></th>
|
|
214
|
+
<th><%= red_t("red.errors.breadcrumbs.column_message") %></th>
|
|
215
|
+
<th width="100"><%= red_t("red.errors.breadcrumbs.column_duration") %></th>
|
|
209
216
|
</tr>
|
|
210
217
|
</thead>
|
|
211
218
|
<tbody>
|
|
@@ -1,25 +1,28 @@
|
|
|
1
1
|
<!-- Co-occurring Errors -->
|
|
2
2
|
<% if RailsErrorDashboard.configuration.enable_co_occurring_errors && error.respond_to?(:co_occurring_errors) %>
|
|
3
|
-
|
|
3
|
+
<%# The window feeds both the query and the hint below, so it is named
|
|
4
|
+
once rather than written as a literal in the prose too. %>
|
|
5
|
+
<% co_occurring_window_minutes = 5 %>
|
|
6
|
+
<% co_occurring = error.co_occurring_errors(window_minutes: co_occurring_window_minutes, min_frequency: 2, limit: 5) %>
|
|
4
7
|
<% if co_occurring.any? %>
|
|
5
8
|
<div class="card mb-4" id="section-co-occurring">
|
|
6
9
|
<div class="card-header">
|
|
7
10
|
<h5 class="mb-0">
|
|
8
|
-
<i class="bi bi-clock-history"></i>
|
|
9
|
-
<span class="badge bg-info text-dark"
|
|
11
|
+
<i class="bi bi-clock-history"></i> <%= red_t("red.errors.co_occurring.title") %>
|
|
12
|
+
<span class="badge bg-info text-dark"><%= red_t("red.errors.co_occurring.badge") %></span>
|
|
10
13
|
</h5>
|
|
11
|
-
<small class="text-muted"
|
|
14
|
+
<small class="text-muted"><%= red_t("red.errors.co_occurring.hint", minutes: co_occurring_window_minutes) %></small>
|
|
12
15
|
</div>
|
|
13
16
|
<div class="card-body p-0">
|
|
14
17
|
<div class="table-responsive">
|
|
15
18
|
<table class="table table-hover mb-0">
|
|
16
19
|
<thead class="table-light">
|
|
17
20
|
<tr>
|
|
18
|
-
<th
|
|
19
|
-
<th
|
|
20
|
-
<th
|
|
21
|
-
<th
|
|
22
|
-
<th
|
|
21
|
+
<th><%= red_t("red.errors.co_occurring.column_error_type") %></th>
|
|
22
|
+
<th><%= red_t("red.errors.co_occurring.column_message") %></th>
|
|
23
|
+
<th><%= red_t("red.errors.co_occurring.column_platform") %></th>
|
|
24
|
+
<th><%= red_t("red.errors.co_occurring.column_frequency") %></th>
|
|
25
|
+
<th><%= red_t("red.errors.co_occurring.column_avg_delay") %></th>
|
|
23
26
|
<th></th>
|
|
24
27
|
</tr>
|
|
25
28
|
</thead>
|
|
@@ -44,20 +47,20 @@
|
|
|
44
47
|
</td>
|
|
45
48
|
<td>
|
|
46
49
|
<span class="badge bg-info text-dark">
|
|
47
|
-
<%=
|
|
50
|
+
<%= red_tp("red.errors.co_occurring.times", count: frequency) %>
|
|
48
51
|
</span>
|
|
49
52
|
</td>
|
|
50
53
|
<td>
|
|
51
54
|
<% if avg_delay < 0 %>
|
|
52
|
-
<span class="text-warning"><%= number_to_human(avg_delay.abs, units: {unit: "s", thousand: "s"}, precision: 1)
|
|
55
|
+
<span class="text-warning"><%= red_t("red.errors.co_occurring.before", duration: number_to_human(avg_delay.abs, units: { unit: "s", thousand: "s" }, precision: 1)) %></span>
|
|
53
56
|
<% elsif avg_delay > 0 %>
|
|
54
|
-
<span class="text-success"><%= number_to_human(avg_delay, units: {unit: "s", thousand: "s"}, precision: 1)
|
|
57
|
+
<span class="text-success"><%= red_t("red.errors.co_occurring.after", duration: number_to_human(avg_delay, units: { unit: "s", thousand: "s" }, precision: 1)) %></span>
|
|
55
58
|
<% else %>
|
|
56
|
-
<span class="text-muted"
|
|
59
|
+
<span class="text-muted"><%= red_t("red.errors.co_occurring.simultaneous") %></span>
|
|
57
60
|
<% end %>
|
|
58
61
|
</td>
|
|
59
62
|
<td class="text-end">
|
|
60
|
-
<%= link_to "
|
|
63
|
+
<%= link_to red_t("red.errors.co_occurring.view"), error_path(error_record), class: "btn btn-sm btn-outline-primary" %>
|
|
61
64
|
</td>
|
|
62
65
|
</tr>
|
|
63
66
|
<% end %>
|
|
@@ -3,17 +3,35 @@
|
|
|
3
3
|
<% platform_comments = @platform_comments || [] %>
|
|
4
4
|
<% issue_tracking_enabled = RailsErrorDashboard.configuration.enable_issue_tracking %>
|
|
5
5
|
|
|
6
|
+
<%#
|
|
7
|
+
Provider display name. github -> "GitHub", not provider.capitalize's "Github".
|
|
8
|
+
An unrecognised provider falls back to its raw value; a missing one falls back
|
|
9
|
+
to the translated generic noun.
|
|
10
|
+
%>
|
|
11
|
+
<%
|
|
12
|
+
discussion_provider = if error.external_issue_provider.present?
|
|
13
|
+
red_t("red.errors.providers.#{error.external_issue_provider}",
|
|
14
|
+
default: error.external_issue_provider.to_s)
|
|
15
|
+
else
|
|
16
|
+
red_t("red.errors.providers.generic")
|
|
17
|
+
end
|
|
18
|
+
discussion_provider_lc = if error.external_issue_provider.present?
|
|
19
|
+
discussion_provider
|
|
20
|
+
else
|
|
21
|
+
red_t("red.errors.providers.generic_lowercase")
|
|
22
|
+
end
|
|
23
|
+
%>
|
|
6
24
|
<% if has_linked_issue && (platform_comments.any? || has_audit_comments) %>
|
|
7
25
|
<div class="card mb-4" id="section-discussion">
|
|
8
26
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
9
27
|
<h5 class="mb-0">
|
|
10
|
-
<i class="bi bi-chat-dots"></i>
|
|
28
|
+
<i class="bi bi-chat-dots"></i> <%= red_t("red.errors.discussion.title") %>
|
|
11
29
|
<% if platform_comments.any? %>
|
|
12
30
|
<span class="badge bg-secondary ms-2"><%= platform_comments.size %></span>
|
|
13
31
|
<% end %>
|
|
14
32
|
</h5>
|
|
15
33
|
<a href="<%= error.external_issue_url %>" target="_blank" rel="noopener" class="btn btn-sm btn-outline-primary">
|
|
16
|
-
<i class="bi bi-box-arrow-up-right me-1"></i>
|
|
34
|
+
<i class="bi bi-box-arrow-up-right me-1"></i> <%= red_t("red.errors.discussion.reply_on", provider: discussion_provider) %>
|
|
17
35
|
</a>
|
|
18
36
|
</div>
|
|
19
37
|
<div class="card-body p-0">
|
|
@@ -28,8 +46,8 @@
|
|
|
28
46
|
<i class="bi bi-person-circle" style="font-size: 1.3em; flex-shrink: 0;"></i>
|
|
29
47
|
<% end %>
|
|
30
48
|
<div>
|
|
31
|
-
<strong class="text-primary"><%= comment[:author] || "
|
|
32
|
-
<small class="text-muted ms-2"><%= comment[:created_at] ? Time.parse(comment[:created_at]).strftime(
|
|
49
|
+
<strong class="text-primary"><%= comment[:author] || red_t("red.common.unknown") %></strong>
|
|
50
|
+
<small class="text-muted ms-2"><%= comment[:created_at] ? Time.parse(comment[:created_at]).strftime(red_time_format(:datetime)) : "" %></small>
|
|
33
51
|
</div>
|
|
34
52
|
</div>
|
|
35
53
|
<div class="ms-4 text-break" style="font-size: 0.9em;">
|
|
@@ -43,7 +61,7 @@
|
|
|
43
61
|
<% if has_audit_comments %>
|
|
44
62
|
<!-- Audit trail — workflow actions -->
|
|
45
63
|
<div class="px-3 py-2 bg-light border-top">
|
|
46
|
-
<small class="text-muted fw-bold"><i class="bi bi-clock-history"></i>
|
|
64
|
+
<small class="text-muted fw-bold"><i class="bi bi-clock-history"></i> <%= red_t("red.errors.discussion.activity_log") %></small>
|
|
47
65
|
</div>
|
|
48
66
|
<% error.comments.recent_first.each_with_index do |comment, index| %>
|
|
49
67
|
<div class="px-3 py-2 <%= index < error.comments.count - 1 ? 'border-bottom' : '' %>">
|
|
@@ -64,14 +82,14 @@
|
|
|
64
82
|
<div class="card mb-4" id="section-discussion">
|
|
65
83
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
66
84
|
<h5 class="mb-0">
|
|
67
|
-
<i class="bi bi-chat-dots"></i>
|
|
85
|
+
<i class="bi bi-chat-dots"></i> <%= red_t("red.errors.discussion.title") %>
|
|
68
86
|
</h5>
|
|
69
87
|
<a href="<%= error.external_issue_url %>" target="_blank" rel="noopener" class="btn btn-sm btn-outline-primary">
|
|
70
|
-
<i class="bi bi-box-arrow-up-right me-1"></i>
|
|
88
|
+
<i class="bi bi-box-arrow-up-right me-1"></i> <%= red_t("red.errors.discussion.start_on", provider: discussion_provider) %>
|
|
71
89
|
</a>
|
|
72
90
|
</div>
|
|
73
91
|
<div class="card-body text-center py-3">
|
|
74
|
-
<p class="text-muted small mb-0"
|
|
92
|
+
<p class="text-muted small mb-0"><%= red_t("red.errors.discussion.empty", provider: discussion_provider_lc) %></p>
|
|
75
93
|
</div>
|
|
76
94
|
</div>
|
|
77
95
|
<% elsif has_audit_comments %>
|
|
@@ -79,7 +97,7 @@
|
|
|
79
97
|
<div class="card mb-4" id="section-discussion">
|
|
80
98
|
<div class="card-header">
|
|
81
99
|
<h5 class="mb-0">
|
|
82
|
-
<i class="bi bi-clock-history"></i>
|
|
100
|
+
<i class="bi bi-clock-history"></i> <%= red_t("red.errors.discussion.activity_log") %>
|
|
83
101
|
</h5>
|
|
84
102
|
</div>
|
|
85
103
|
<div class="card-body p-0">
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
<!-- Error Cascades -->
|
|
2
2
|
<% if RailsErrorDashboard.configuration.enable_error_cascades %>
|
|
3
3
|
<% cascades = error.error_cascades %>
|
|
4
|
+
<%# The thresholds are named once: they drive the badge colour on each
|
|
5
|
+
row AND the footnote that cites them, which must not drift apart. %>
|
|
6
|
+
<% strong_cascade_probability = 70 %>
|
|
7
|
+
<% strong_cascade_frequency = 3 %>
|
|
4
8
|
<% if cascades[:parents].any? || cascades[:children].any? %>
|
|
5
9
|
<div class="card" id="section-error-cascades">
|
|
6
10
|
<div class="card-header">
|
|
7
11
|
<h5 class="mb-0">
|
|
8
|
-
<i class="bi bi-diagram-3"></i>
|
|
9
|
-
<small class="text-muted ms-2"
|
|
12
|
+
<i class="bi bi-diagram-3"></i> <%= red_t("red.errors.cascades.title") %>
|
|
13
|
+
<small class="text-muted ms-2"><%= red_t("red.errors.cascades.hint") %></small>
|
|
10
14
|
</h5>
|
|
11
15
|
</div>
|
|
12
16
|
<div class="card-body">
|
|
@@ -14,18 +18,18 @@
|
|
|
14
18
|
<% if cascades[:parents].any? %>
|
|
15
19
|
<div class="mb-4">
|
|
16
20
|
<h6 class="text-danger">
|
|
17
|
-
<i class="bi bi-arrow-down-circle"></i>
|
|
18
|
-
<small class="text-muted"
|
|
21
|
+
<i class="bi bi-arrow-down-circle"></i> <%= red_t("red.errors.cascades.triggered_by") %>
|
|
22
|
+
<small class="text-muted"><%= red_t("red.errors.cascades.triggered_by_hint") %></small>
|
|
19
23
|
</h6>
|
|
20
24
|
<div class="table-responsive">
|
|
21
25
|
<table class="table table-sm table-hover mb-0">
|
|
22
26
|
<thead class="table-light">
|
|
23
27
|
<tr>
|
|
24
|
-
<th
|
|
25
|
-
<th
|
|
26
|
-
<th
|
|
27
|
-
<th
|
|
28
|
-
<th
|
|
28
|
+
<th><%= red_t("red.errors.cascades.column_error_type") %></th>
|
|
29
|
+
<th><%= red_t("red.errors.cascades.column_message") %></th>
|
|
30
|
+
<th><%= red_t("red.errors.cascades.column_probability") %></th>
|
|
31
|
+
<th><%= red_t("red.errors.cascades.column_frequency") %></th>
|
|
32
|
+
<th><%= red_t("red.errors.cascades.column_avg_delay") %></th>
|
|
29
33
|
<th></th>
|
|
30
34
|
</tr>
|
|
31
35
|
</thead>
|
|
@@ -45,7 +49,7 @@
|
|
|
45
49
|
</td>
|
|
46
50
|
<td>
|
|
47
51
|
<% probability_pct = ((cascade[:probability] || 0) * 100).round(1) %>
|
|
48
|
-
<span class="badge <%= probability_pct >=
|
|
52
|
+
<span class="badge <%= probability_pct >= strong_cascade_probability ? 'bg-danger' : 'bg-warning' %>">
|
|
49
53
|
<%= probability_pct %>%
|
|
50
54
|
</span>
|
|
51
55
|
</td>
|
|
@@ -58,7 +62,7 @@
|
|
|
58
62
|
</small>
|
|
59
63
|
</td>
|
|
60
64
|
<td>
|
|
61
|
-
<%= link_to "
|
|
65
|
+
<%= link_to red_t("red.errors.cascades.view"), error_path(parent), class: "btn btn-sm btn-outline-primary" %>
|
|
62
66
|
</td>
|
|
63
67
|
</tr>
|
|
64
68
|
<% end %>
|
|
@@ -72,18 +76,18 @@
|
|
|
72
76
|
<% if cascades[:children].any? %>
|
|
73
77
|
<div>
|
|
74
78
|
<h6 class="text-warning">
|
|
75
|
-
<i class="bi bi-arrow-up-circle"></i>
|
|
76
|
-
<small class="text-muted"
|
|
79
|
+
<i class="bi bi-arrow-up-circle"></i> <%= red_t("red.errors.cascades.triggers") %>
|
|
80
|
+
<small class="text-muted"><%= red_t("red.errors.cascades.triggers_hint") %></small>
|
|
77
81
|
</h6>
|
|
78
82
|
<div class="table-responsive">
|
|
79
83
|
<table class="table table-sm table-hover mb-0">
|
|
80
84
|
<thead class="table-light">
|
|
81
85
|
<tr>
|
|
82
|
-
<th
|
|
83
|
-
<th
|
|
84
|
-
<th
|
|
85
|
-
<th
|
|
86
|
-
<th
|
|
86
|
+
<th><%= red_t("red.errors.cascades.column_error_type") %></th>
|
|
87
|
+
<th><%= red_t("red.errors.cascades.column_message") %></th>
|
|
88
|
+
<th><%= red_t("red.errors.cascades.column_probability") %></th>
|
|
89
|
+
<th><%= red_t("red.errors.cascades.column_frequency") %></th>
|
|
90
|
+
<th><%= red_t("red.errors.cascades.column_avg_delay") %></th>
|
|
87
91
|
<th></th>
|
|
88
92
|
</tr>
|
|
89
93
|
</thead>
|
|
@@ -103,7 +107,7 @@
|
|
|
103
107
|
</td>
|
|
104
108
|
<td>
|
|
105
109
|
<% probability_pct = ((cascade[:probability] || 0) * 100).round(1) %>
|
|
106
|
-
<span class="badge <%= probability_pct >=
|
|
110
|
+
<span class="badge <%= probability_pct >= strong_cascade_probability ? 'bg-danger' : 'bg-warning' %>">
|
|
107
111
|
<%= probability_pct %>%
|
|
108
112
|
</span>
|
|
109
113
|
</td>
|
|
@@ -116,7 +120,7 @@
|
|
|
116
120
|
</small>
|
|
117
121
|
</td>
|
|
118
122
|
<td>
|
|
119
|
-
<%= link_to "
|
|
123
|
+
<%= link_to red_t("red.errors.cascades.view"), error_path(child), class: "btn btn-sm btn-outline-primary" %>
|
|
120
124
|
</td>
|
|
121
125
|
</tr>
|
|
122
126
|
<% end %>
|
|
@@ -129,7 +133,7 @@
|
|
|
129
133
|
<div class="mt-3">
|
|
130
134
|
<small class="text-muted">
|
|
131
135
|
<i class="bi bi-info-circle"></i>
|
|
132
|
-
|
|
136
|
+
<%= red_t("red.errors.cascades.footnote", probability: strong_cascade_probability, frequency: strong_cascade_frequency) %>
|
|
133
137
|
</small>
|
|
134
138
|
</div>
|
|
135
139
|
</div>
|