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
|
@@ -1,28 +1,31 @@
|
|
|
1
|
-
|
|
1
|
+
<%# The locale is part of the cache key: this fragment now contains
|
|
2
|
+
translated text, and without it the first locale to render an error
|
|
3
|
+
would be served to every other one. %>
|
|
4
|
+
<% cache [error, 'error_details_v2', red_locale] do %>
|
|
2
5
|
<div class="card mb-4" id="section-error-info">
|
|
3
6
|
<div class="card-header bg-danger text-white">
|
|
4
7
|
<div class="d-flex justify-content-between align-items-center">
|
|
5
8
|
<h5 class="mb-0">
|
|
6
9
|
<i class="bi bi-bug-fill"></i> <%= error.error_type %>
|
|
7
10
|
<% if error.recent? %>
|
|
8
|
-
<span class="badge bg-success ms-2" data-bs-toggle="tooltip" title="
|
|
11
|
+
<span class="badge bg-success ms-2" data-bs-toggle="tooltip" title="<%= red_t("red.errors.info.new_tooltip") %>"><%= red_t("red.errors.info.new_badge") %></span>
|
|
9
12
|
<% end %>
|
|
10
13
|
<% if error.reopened? %>
|
|
11
|
-
<span class="badge bg-warning text-dark ms-2" data-bs-toggle="tooltip" title="
|
|
12
|
-
<i class="bi bi-arrow-counterclockwise"></i>
|
|
14
|
+
<span class="badge bg-warning text-dark ms-2" data-bs-toggle="tooltip" title="<%= red_t("red.errors.info.reopened_tooltip", time: error.reopened_at&.strftime(red_time_format(:datetime))) %>">
|
|
15
|
+
<i class="bi bi-arrow-counterclockwise"></i> <%= red_t("red.errors.info.reopened") %>
|
|
13
16
|
</span>
|
|
14
17
|
<% end %>
|
|
15
18
|
</h5>
|
|
16
|
-
<button type="button" class="btn btn-sm btn-outline-light" data-red-action="copy" data-red-copy-text="<%= j error.error_type %>" title="
|
|
19
|
+
<button type="button" class="btn btn-sm btn-outline-light" data-red-action="copy" data-red-copy-text="<%= j error.error_type %>" title="<%= red_t("red.errors.info.copy_error_type") %>">
|
|
17
20
|
<i class="bi bi-clipboard"></i>
|
|
18
21
|
</button>
|
|
19
22
|
</div>
|
|
20
23
|
</div>
|
|
21
24
|
<div class="card-body">
|
|
22
25
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
23
|
-
<h6 class="text-muted mb-0"
|
|
24
|
-
<button type="button" class="btn btn-sm btn-outline-secondary" data-red-action="copy" data-red-copy-text="<%= j error.message %>" title="
|
|
25
|
-
<i class="bi bi-clipboard"></i>
|
|
26
|
+
<h6 class="text-muted mb-0"><%= red_t("red.errors.info.message_label") %></h6>
|
|
27
|
+
<button type="button" class="btn btn-sm btn-outline-secondary" data-red-action="copy" data-red-copy-text="<%= j error.message %>" title="<%= red_t("red.errors.info.copy_message") %>">
|
|
28
|
+
<i class="bi bi-clipboard"></i> <%= red_t("red.errors.info.copy") %>
|
|
26
29
|
</button>
|
|
27
30
|
</div>
|
|
28
31
|
<div class="alert alert-danger">
|
|
@@ -35,7 +38,7 @@
|
|
|
35
38
|
<% if cause_chain.is_a?(Array) && cause_chain.any? %>
|
|
36
39
|
<div class="mt-4 mb-3">
|
|
37
40
|
<h6 class="text-muted mb-2">
|
|
38
|
-
<i class="bi bi-link-45deg"></i>
|
|
41
|
+
<i class="bi bi-link-45deg"></i> <%= red_t("red.errors.info.cause_chain.title") %>
|
|
39
42
|
<span class="badge bg-secondary ms-1"><%= cause_chain.length %></span>
|
|
40
43
|
</h6>
|
|
41
44
|
<div class="cause-chain">
|
|
@@ -46,7 +49,7 @@
|
|
|
46
49
|
<div class="card-header bg-warning bg-opacity-10 py-2">
|
|
47
50
|
<div class="d-flex justify-content-between align-items-center">
|
|
48
51
|
<div>
|
|
49
|
-
<small class="text-muted">
|
|
52
|
+
<small class="text-muted"><%= cause_chain.length > 1 ? red_t("red.errors.info.cause_chain.caused_by_indexed", index: index + 1) : red_t("red.errors.info.cause_chain.caused_by") %></small>
|
|
50
53
|
<strong class="ms-1"><code><%= cause["class_name"] %></code></strong>
|
|
51
54
|
</div>
|
|
52
55
|
<% if cause_bt.any? %>
|
|
@@ -54,7 +57,7 @@
|
|
|
54
57
|
data-bs-toggle="collapse"
|
|
55
58
|
data-bs-target="#cause-backtrace-<%= index %>"
|
|
56
59
|
aria-expanded="false">
|
|
57
|
-
<small><i class="bi bi-code-slash"></i>
|
|
60
|
+
<small><i class="bi bi-code-slash"></i> <%= red_t("red.errors.info.cause_chain.backtrace") %></small>
|
|
58
61
|
</button>
|
|
59
62
|
<% end %>
|
|
60
63
|
</div>
|
|
@@ -84,19 +87,19 @@
|
|
|
84
87
|
|
|
85
88
|
<div class="d-flex justify-content-between align-items-center mb-2 mt-4">
|
|
86
89
|
<h6 class="text-muted mb-0">
|
|
87
|
-
|
|
90
|
+
<%= red_t("red.errors.info.backtrace.label") %>
|
|
88
91
|
<% if error.backtrace.present? %>
|
|
89
92
|
<% frames = parse_backtrace(error.backtrace) %>
|
|
90
93
|
<% app_frames = filter_app_code(frames) %>
|
|
91
94
|
<% framework_frames = filter_framework_code(frames) %>
|
|
92
95
|
<small class="text-muted">
|
|
93
|
-
|
|
96
|
+
<%= red_t("red.errors.info.backtrace.frame_counts", app: app_frames.count, framework: framework_frames.count) %>
|
|
94
97
|
</small>
|
|
95
98
|
<% end %>
|
|
96
99
|
</h6>
|
|
97
100
|
<% if error.backtrace.present? %>
|
|
98
|
-
<button type="button" class="btn btn-sm btn-outline-secondary" data-red-action="copy" data-red-copy-text="<%= j error.backtrace %>" title="
|
|
99
|
-
<i class="bi bi-clipboard"></i>
|
|
101
|
+
<button type="button" class="btn btn-sm btn-outline-secondary" data-red-action="copy" data-red-copy-text="<%= j error.backtrace %>" title="<%= red_t("red.errors.info.backtrace.copy_full_title") %>">
|
|
102
|
+
<i class="bi bi-clipboard"></i> <%= red_t("red.errors.info.backtrace.copy_full") %>
|
|
100
103
|
</button>
|
|
101
104
|
<% end %>
|
|
102
105
|
</div>
|
|
@@ -111,9 +114,9 @@
|
|
|
111
114
|
<div class="card-header bg-success bg-opacity-10 border-success">
|
|
112
115
|
<div class="d-flex justify-content-between align-items-center">
|
|
113
116
|
<strong class="text-success">
|
|
114
|
-
<i class="bi bi-code-square"></i>
|
|
117
|
+
<i class="bi bi-code-square"></i> <%= red_t("red.errors.info.backtrace.your_code") %>
|
|
115
118
|
</strong>
|
|
116
|
-
<span class="badge bg-success"><%= app_frames.count
|
|
119
|
+
<span class="badge bg-success"><%= red_tp("red.errors.info.backtrace.frames", count: app_frames.count) %></span>
|
|
117
120
|
</div>
|
|
118
121
|
</div>
|
|
119
122
|
<div class="card-body p-0">
|
|
@@ -134,7 +137,7 @@
|
|
|
134
137
|
data-bs-target="#source-code-<%= index %>"
|
|
135
138
|
aria-expanded="false"
|
|
136
139
|
aria-controls="source-code-<%= index %>">
|
|
137
|
-
<i class="bi bi-code-slash"></i> <span class="d-none d-md-inline"
|
|
140
|
+
<i class="bi bi-code-slash"></i> <span class="d-none d-md-inline"><%= red_t("red.errors.info.backtrace.view_source") %></span>
|
|
138
141
|
</button>
|
|
139
142
|
<% end %>
|
|
140
143
|
</div>
|
|
@@ -159,9 +162,9 @@
|
|
|
159
162
|
<h2 class="accordion-header">
|
|
160
163
|
<button class="accordion-button collapsed bg-light" type="button" data-bs-toggle="collapse" data-bs-target="#frameworkBacktrace" aria-expanded="false" aria-controls="frameworkBacktrace">
|
|
161
164
|
<i class="bi bi-gear me-2 text-warning"></i>
|
|
162
|
-
<strong
|
|
163
|
-
<span class="badge bg-secondary ms-2"><%= framework_frames.count
|
|
164
|
-
<small class="text-muted ms-2"
|
|
165
|
+
<strong><%= red_t("red.errors.info.backtrace.framework_code") %></strong>
|
|
166
|
+
<span class="badge bg-secondary ms-2"><%= red_tp("red.errors.info.backtrace.frames", count: framework_frames.count) %></span>
|
|
167
|
+
<small class="text-muted ms-2"><%= red_t("red.errors.info.backtrace.click_to_expand") %></small>
|
|
165
168
|
</button>
|
|
166
169
|
</h2>
|
|
167
170
|
<div id="frameworkBacktrace" class="accordion-collapse collapse" data-bs-parent="#frameworkBacktraceAccordion">
|
|
@@ -183,7 +186,7 @@
|
|
|
183
186
|
</div>
|
|
184
187
|
<% end %>
|
|
185
188
|
<% else %>
|
|
186
|
-
<p class="text-muted"
|
|
189
|
+
<p class="text-muted"><%= red_t("red.errors.info.backtrace.none") %></p>
|
|
187
190
|
<% end %>
|
|
188
191
|
</div>
|
|
189
192
|
</div>
|
|
@@ -9,13 +9,13 @@
|
|
|
9
9
|
<div style="display: flex; align-items: center; gap: 8px;">
|
|
10
10
|
<span class="badge bg-<%= severity_color(error.severity) %>" style="display: inline-flex; align-items: center; gap: 4px;">
|
|
11
11
|
<span style="width: 6px; height: 6px; border-radius: 50%; background: currentColor;"></span>
|
|
12
|
-
<%= error.severity %>
|
|
12
|
+
<%= red_t("red.common.severity.#{error.severity}", default: error.severity.to_s) %>
|
|
13
13
|
</span>
|
|
14
14
|
<div style="min-width: 0;">
|
|
15
15
|
<div style="font-weight: 600; color: var(--text-primary); font-family: var(--font-mono); font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
|
|
16
16
|
<%= error.error_type %>
|
|
17
17
|
<% if error.recent? %>
|
|
18
|
-
<span style="display: inline-block; padding: 1px 6px; font-size: 9px; font-weight: 600; border-radius: var(--radius-full); background: var(--status-success-bg); color: var(--status-success); margin-left: 4px; text-transform: uppercase;"
|
|
18
|
+
<span style="display: inline-block; padding: 1px 6px; font-size: 9px; font-weight: 600; border-radius: var(--radius-full); background: var(--status-success-bg); color: var(--status-success); margin-left: 4px; text-transform: uppercase;"><%= red_t("red.errors.row.new_badge") %></span>
|
|
19
19
|
<% end %>
|
|
20
20
|
</div>
|
|
21
21
|
<div style="color: var(--text-tertiary); font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;" title="<%= error.message %>"><%= error.message %></div>
|
|
@@ -24,36 +24,43 @@
|
|
|
24
24
|
</td>
|
|
25
25
|
<td style="padding: var(--space-3) var(--space-4);">
|
|
26
26
|
<%
|
|
27
|
-
# Use the model's status field if it has a meaningful workflow status
|
|
27
|
+
# Use the model's status field if it has a meaningful workflow status.
|
|
28
|
+
#
|
|
29
|
+
# status_key stays the machine value ("in_progress"): it drives the colour
|
|
30
|
+
# lookup and the translation key, neither of which may vary by locale.
|
|
31
|
+
# Only the rendered label is translated. The old code displayed the value
|
|
32
|
+
# itself via tr('_', ' ') plus a CSS capitalize, which is English
|
|
33
|
+
# morphology applied to an identifier.
|
|
28
34
|
raw_status = error.respond_to?(:status) ? error.status : nil
|
|
29
|
-
|
|
35
|
+
status_key = if error.respond_to?(:muted?) && error.muted?
|
|
30
36
|
'muted'
|
|
31
37
|
elsif error.respond_to?(:snoozed?) && error.snoozed?
|
|
32
38
|
'snoozed'
|
|
33
39
|
elsif raw_status.present? && raw_status != 'new'
|
|
34
|
-
raw_status
|
|
40
|
+
raw_status
|
|
35
41
|
elsif error.resolved?
|
|
36
42
|
'resolved'
|
|
37
43
|
else
|
|
38
44
|
'unresolved'
|
|
39
45
|
end
|
|
46
|
+
status_text = red_t("red.errors.row.status.#{status_key}", default: status_key.tr('_', ' '))
|
|
40
47
|
status_colors = {
|
|
41
48
|
'unresolved' => { bg: 'var(--status-critical-bg)', color: 'var(--status-critical)' },
|
|
42
49
|
'resolved' => { bg: 'var(--status-success-bg)', color: 'var(--status-success)' },
|
|
43
|
-
'
|
|
50
|
+
'in_progress' => { bg: 'var(--status-info-bg)', color: 'var(--status-info)' },
|
|
44
51
|
'investigating' => { bg: 'var(--status-caution-bg)', color: 'var(--status-caution)' },
|
|
45
|
-
'
|
|
52
|
+
'wont_fix' => { bg: 'var(--surface-tertiary)', color: 'var(--text-tertiary)' },
|
|
46
53
|
'assigned' => { bg: 'var(--accent-subtle)', color: 'var(--accent)' },
|
|
47
54
|
'snoozed' => { bg: 'var(--status-caution-bg)', color: 'var(--status-caution)' },
|
|
48
55
|
'muted' => { bg: 'var(--surface-tertiary)', color: 'var(--text-tertiary)' },
|
|
49
56
|
}
|
|
50
|
-
sc = status_colors[
|
|
57
|
+
sc = status_colors[status_key] || status_colors['unresolved']
|
|
51
58
|
%>
|
|
52
59
|
<span style="display: inline-flex; align-items: center; gap: 4px; padding: 3px 10px; font-size: 11px; font-weight: 600; border-radius: var(--radius-full); background: <%= sc[:bg] %>; color: <%= sc[:color] %>; text-transform: capitalize;">
|
|
53
60
|
<%= status_text %>
|
|
54
61
|
</span>
|
|
55
62
|
<% if error.reopened? %>
|
|
56
|
-
<i class="bi bi-arrow-counterclockwise" style="color: var(--status-warning); margin-left: 4px; font-size: 12px;" title="
|
|
63
|
+
<i class="bi bi-arrow-counterclockwise" style="color: var(--status-warning); margin-left: 4px; font-size: 12px;" title="<%= red_t("red.errors.row.reopened") %>"></i>
|
|
57
64
|
<% end %>
|
|
58
65
|
</td>
|
|
59
66
|
<td style="padding: var(--space-3) var(--space-4); text-align: right; font-weight: 600; font-variant-numeric: tabular-nums; color: var(--text-primary);">
|
|
@@ -61,7 +68,7 @@
|
|
|
61
68
|
</td>
|
|
62
69
|
<td style="padding: var(--space-3) var(--space-4); text-align: right; font-variant-numeric: tabular-nums; color: var(--text-secondary);">
|
|
63
70
|
<% if error.user_id %>
|
|
64
|
-
<%= link_to error.user_id, errors_path(user_id: error.user_id, unresolved: '0'), style: "color: inherit; text-decoration: none;", title: "
|
|
71
|
+
<%= link_to error.user_id, errors_path(user_id: error.user_id, unresolved: '0'), style: "color: inherit; text-decoration: none;", title: red_t("red.errors.row.view_user_errors", user: error.user_id) %>
|
|
65
72
|
<% else %>
|
|
66
73
|
<span style="color: var(--text-tertiary);">—</span>
|
|
67
74
|
<% end %>
|
|
@@ -70,8 +77,13 @@
|
|
|
70
77
|
<%= local_time_ago(error.last_seen_at) %>
|
|
71
78
|
</td>
|
|
72
79
|
<% if local_assigns[:show_application] %>
|
|
73
|
-
|
|
74
|
-
|
|
80
|
+
<%
|
|
81
|
+
# An application name is user data and is never translated; only the
|
|
82
|
+
# placeholder for a missing one is.
|
|
83
|
+
application_name = error.application&.name.presence || red_t("red.common.unknown")
|
|
84
|
+
%>
|
|
85
|
+
<td style="padding: var(--space-3) var(--space-4); font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;" title="<%= application_name %>">
|
|
86
|
+
<span class="badge bg-info"><%= application_name %></span>
|
|
75
87
|
</td>
|
|
76
88
|
<% end %>
|
|
77
89
|
<% if local_assigns[:show_platform] %>
|
|
@@ -7,17 +7,18 @@
|
|
|
7
7
|
<div class="card mb-4" id="section-instance-variables">
|
|
8
8
|
<div class="card-header">
|
|
9
9
|
<h5 class="mb-0">
|
|
10
|
-
<i class="bi bi-box"></i>
|
|
11
|
-
<span class="badge bg-secondary"><%= instance_vars.size
|
|
10
|
+
<i class="bi bi-box"></i> <%= red_t("red.errors.variables.instance_title") %>
|
|
11
|
+
<span class="badge bg-secondary"><%= red_tp("red.errors.variables.captured", count: instance_vars.size) %></span>
|
|
12
12
|
</h5>
|
|
13
13
|
<small class="text-muted">
|
|
14
|
-
|
|
14
|
+
<%# Whole sentences per branch: the original spliced a <code>
|
|
15
|
+
element into the middle of one, which does not survive
|
|
16
|
+
reordering into every language. %>
|
|
15
17
|
<% if self_class && self_class["value"] %>
|
|
16
|
-
|
|
18
|
+
<%= red_t("red.errors.variables.instance_hint_html", klass: tag.code(self_class["value"])) %>
|
|
17
19
|
<% else %>
|
|
18
|
-
|
|
20
|
+
<%= red_t("red.errors.variables.instance_hint_generic") %>
|
|
19
21
|
<% end %>
|
|
20
|
-
where the exception was raised
|
|
21
22
|
</small>
|
|
22
23
|
</div>
|
|
23
24
|
<div class="card-body p-0">
|
|
@@ -25,9 +26,9 @@
|
|
|
25
26
|
<table class="table table-sm table-hover mb-0">
|
|
26
27
|
<thead class="table-light">
|
|
27
28
|
<tr>
|
|
28
|
-
<th width="200"
|
|
29
|
-
<th width="120"
|
|
30
|
-
<th
|
|
29
|
+
<th width="200"><%= red_t("red.errors.variables.column_variable") %></th>
|
|
30
|
+
<th width="120"><%= red_t("red.errors.variables.column_type") %></th>
|
|
31
|
+
<th><%= red_t("red.errors.variables.column_value") %></th>
|
|
31
32
|
</tr>
|
|
32
33
|
</thead>
|
|
33
34
|
<tbody>
|
|
@@ -37,11 +38,11 @@
|
|
|
37
38
|
<td><small class="text-muted"><%= info["type"] %></small></td>
|
|
38
39
|
<td>
|
|
39
40
|
<% if info["filtered"] %>
|
|
40
|
-
<span class="badge bg-warning text-dark"
|
|
41
|
+
<span class="badge bg-warning text-dark"><%= red_t("red.errors.variables.filtered") %></span>
|
|
41
42
|
<% else %>
|
|
42
43
|
<code style="word-break: break-all; font-size: 0.85em;"><%= truncate(info["value"].to_s, length: 500) %></code>
|
|
43
44
|
<% if info["truncated"] %>
|
|
44
|
-
<span class="badge bg-info text-dark ms-1" style="font-size: 0.7em;"
|
|
45
|
+
<span class="badge bg-info text-dark ms-1" style="font-size: 0.7em;"><%= red_t("red.errors.variables.truncated") %></span>
|
|
45
46
|
<% end %>
|
|
46
47
|
<% end %>
|
|
47
48
|
</td>
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
4
4
|
<h5 class="mb-0">
|
|
5
5
|
<i class="bi bi-link-45deg me-2"></i>
|
|
6
|
-
|
|
6
|
+
<%= red_t("red.errors.issue_section.title") %>
|
|
7
7
|
</h5>
|
|
8
8
|
<% if @error.external_issue_url.present? %>
|
|
9
9
|
<a href="<%= @error.external_issue_url %>" target="_blank" rel="noopener" class="btn btn-sm btn-outline-primary">
|
|
10
|
-
<i class="bi bi-box-arrow-up-right me-1"></i>
|
|
10
|
+
<i class="bi bi-box-arrow-up-right me-1"></i> <%= red_t("red.errors.issue_section.view_issue") %>
|
|
11
11
|
</a>
|
|
12
12
|
<% end %>
|
|
13
13
|
</div>
|
|
@@ -27,14 +27,16 @@
|
|
|
27
27
|
<div class="d-flex align-items-center gap-2 mb-3">
|
|
28
28
|
<span class="badge bg-success">
|
|
29
29
|
<i class="bi <%= icon %> me-1"></i>
|
|
30
|
-
|
|
30
|
+
<%# Provider names are brands: github -> "GitHub", not "Github". %>
|
|
31
|
+
<% issue_provider = provider.present? ? red_t("red.errors.providers.#{provider}", default: provider.to_s) : red_t("red.errors.issue_section.linked") %>
|
|
32
|
+
<%= red_t("red.errors.issue_section.issue_reference", provider: issue_provider, number: @error.external_issue_number || "?") %>
|
|
31
33
|
</span>
|
|
32
34
|
<% if issue %>
|
|
33
35
|
<% state_color = issue[:state] == "open" ? "success" : "secondary" %>
|
|
34
36
|
<% state_icon = issue[:state] == "open" ? "bi-circle-fill" : "bi-check-circle-fill" %>
|
|
35
37
|
<span class="badge bg-<%= state_color %>">
|
|
36
38
|
<i class="bi <%= state_icon %> me-1" style="font-size: 0.6em;"></i>
|
|
37
|
-
<%= issue[:state]
|
|
39
|
+
<%= red_t("red.errors.issue_section.states.#{issue[:state]}", default: issue[:state].to_s) %>
|
|
38
40
|
</span>
|
|
39
41
|
<% end %>
|
|
40
42
|
</div>
|
|
@@ -43,7 +45,7 @@
|
|
|
43
45
|
<div class="row">
|
|
44
46
|
<!-- Assignees -->
|
|
45
47
|
<div class="col-md-6 mb-2">
|
|
46
|
-
<small class="text-muted d-block mb-1"
|
|
48
|
+
<small class="text-muted d-block mb-1"><%= red_t("red.errors.issue_section.assignees") %></small>
|
|
47
49
|
<% if issue[:assignees].present? && issue[:assignees].any? %>
|
|
48
50
|
<div class="d-flex align-items-center gap-2 flex-wrap">
|
|
49
51
|
<% issue[:assignees].each do |assignee| %>
|
|
@@ -56,13 +58,13 @@
|
|
|
56
58
|
<% end %>
|
|
57
59
|
</div>
|
|
58
60
|
<% else %>
|
|
59
|
-
<span class="text-muted" style="font-size: 0.85em;"
|
|
61
|
+
<span class="text-muted" style="font-size: 0.85em;"><%= red_t("red.errors.issue_section.unassigned") %></span>
|
|
60
62
|
<% end %>
|
|
61
63
|
</div>
|
|
62
64
|
|
|
63
65
|
<!-- Labels -->
|
|
64
66
|
<div class="col-md-6 mb-2">
|
|
65
|
-
<small class="text-muted d-block mb-1"
|
|
67
|
+
<small class="text-muted d-block mb-1"><%= red_t("red.errors.issue_section.labels") %></small>
|
|
66
68
|
<% if issue[:labels].present? && issue[:labels].any? %>
|
|
67
69
|
<div class="d-flex align-items-center gap-1 flex-wrap">
|
|
68
70
|
<% issue[:labels].each do |label| %>
|
|
@@ -74,7 +76,7 @@
|
|
|
74
76
|
<% end %>
|
|
75
77
|
</div>
|
|
76
78
|
<% else %>
|
|
77
|
-
<span class="text-muted" style="font-size: 0.85em;"
|
|
79
|
+
<span class="text-muted" style="font-size: 0.85em;"><%= red_t("red.errors.issue_section.no_labels") %></span>
|
|
78
80
|
<% end %>
|
|
79
81
|
</div>
|
|
80
82
|
</div>
|
|
@@ -84,26 +86,30 @@
|
|
|
84
86
|
<!-- No linked issue — show create/link options -->
|
|
85
87
|
<div class="row">
|
|
86
88
|
<div class="col-md-6">
|
|
87
|
-
<h6 class="text-muted mb-2"
|
|
89
|
+
<h6 class="text-muted mb-2"><%= red_t("red.errors.issue_section.create_new") %></h6>
|
|
88
90
|
<% provider = RailsErrorDashboard.configuration.effective_issue_tracker_provider %>
|
|
89
91
|
<% if provider && RailsErrorDashboard.configuration.effective_issue_tracker_token %>
|
|
90
92
|
<%= button_to create_issue_error_path(@error), method: :post, class: "btn btn-sm btn-primary" do %>
|
|
91
93
|
<i class="bi bi-plus-circle me-1"></i>
|
|
92
|
-
|
|
94
|
+
<%= red_t("red.errors.issue_section.create_issue", provider: red_t("red.errors.providers.#{provider}", default: provider.to_s)) %>
|
|
93
95
|
<% end %>
|
|
94
96
|
<% else %>
|
|
95
97
|
<p class="text-muted small mb-0">
|
|
96
98
|
<i class="bi bi-info-circle"></i>
|
|
97
|
-
|
|
99
|
+
<%# The config option names are identifiers and stay English;
|
|
100
|
+
only the sentence around them is translated. %>
|
|
101
|
+
<%= red_t("red.errors.issue_section.configure_html",
|
|
102
|
+
token: tag.code("issue_tracker_token"),
|
|
103
|
+
url: tag.code("git_repository_url")) %>
|
|
98
104
|
</p>
|
|
99
105
|
<% end %>
|
|
100
106
|
</div>
|
|
101
107
|
<div class="col-md-6">
|
|
102
|
-
<h6 class="text-muted mb-2"
|
|
108
|
+
<h6 class="text-muted mb-2"><%= red_t("red.errors.issue_section.link_existing") %></h6>
|
|
103
109
|
<%= form_tag link_issue_error_path(@error), method: :post, class: "d-flex gap-2" do %>
|
|
104
110
|
<%= text_field_tag :issue_url, nil, placeholder: "https://github.com/user/repo/issues/42", class: "form-control form-control-sm", required: true %>
|
|
105
111
|
<button type="submit" class="btn btn-sm btn-outline-secondary text-nowrap">
|
|
106
|
-
<i class="bi bi-link"></i>
|
|
112
|
+
<i class="bi bi-link"></i> <%= red_t("red.errors.issue_section.link") %>
|
|
107
113
|
</button>
|
|
108
114
|
<% end %>
|
|
109
115
|
</div>
|
|
@@ -8,20 +8,20 @@
|
|
|
8
8
|
<div class="card mb-4" id="section-llm-summary">
|
|
9
9
|
<div class="card-header">
|
|
10
10
|
<h5 class="mb-0">
|
|
11
|
-
<i class="bi bi-robot text-info" aria-hidden="true"></i>
|
|
11
|
+
<i class="bi bi-robot text-info" aria-hidden="true"></i> <%= red_t("red.errors.llm_summary.title") %>
|
|
12
12
|
<span class="badge bg-info text-dark"><%= llm_summary[:total_calls] %></span>
|
|
13
13
|
</h5>
|
|
14
|
-
<small class="text-muted"
|
|
14
|
+
<small class="text-muted"><%= red_t("red.errors.llm_summary.hint") %></small>
|
|
15
15
|
</div>
|
|
16
16
|
<div class="card-body">
|
|
17
17
|
<div class="row text-center mb-3">
|
|
18
18
|
<div class="col-4">
|
|
19
19
|
<div class="fs-4 fw-bold"><%= llm_summary[:total_calls] %></div>
|
|
20
|
-
<small class="text-muted"
|
|
20
|
+
<small class="text-muted"><%= red_t("red.errors.llm_summary.calls") %></small>
|
|
21
21
|
</div>
|
|
22
22
|
<div class="col-4">
|
|
23
23
|
<div class="fs-4 fw-bold"><%= number_with_delimiter(llm_summary[:total_tokens]) %></div>
|
|
24
|
-
<small class="text-muted"
|
|
24
|
+
<small class="text-muted"><%= red_t("red.errors.llm_summary.tokens") %></small>
|
|
25
25
|
</div>
|
|
26
26
|
<div class="col-4">
|
|
27
27
|
<div class="fs-4 fw-bold">
|
|
@@ -31,32 +31,32 @@
|
|
|
31
31
|
<span class="text-muted">--</span>
|
|
32
32
|
<% end %>
|
|
33
33
|
</div>
|
|
34
|
-
<small class="text-muted"
|
|
34
|
+
<small class="text-muted"><%= red_t("red.errors.llm_summary.cost") %></small>
|
|
35
35
|
</div>
|
|
36
36
|
</div>
|
|
37
37
|
|
|
38
38
|
<div class="mb-3">
|
|
39
39
|
<div class="d-flex justify-content-between mb-1">
|
|
40
|
-
<small class="text-muted"
|
|
40
|
+
<small class="text-muted"><%= red_t("red.errors.llm_summary.input") %></small>
|
|
41
41
|
<small><strong><%= number_with_delimiter(llm_summary[:total_input_tokens]) %></strong></small>
|
|
42
42
|
</div>
|
|
43
43
|
<div class="d-flex justify-content-between mb-1">
|
|
44
|
-
<small class="text-muted"
|
|
44
|
+
<small class="text-muted"><%= red_t("red.errors.llm_summary.output") %></small>
|
|
45
45
|
<small><strong><%= number_with_delimiter(llm_summary[:total_output_tokens]) %></strong></small>
|
|
46
46
|
</div>
|
|
47
47
|
<% if llm_summary[:total_tool_calls] > 0 %>
|
|
48
48
|
<div class="d-flex justify-content-between mb-1">
|
|
49
|
-
<small class="text-muted"
|
|
49
|
+
<small class="text-muted"><%= red_t("red.errors.llm_summary.tool_calls") %></small>
|
|
50
50
|
<small><strong><%= llm_summary[:total_tool_calls] %></strong></small>
|
|
51
51
|
</div>
|
|
52
52
|
<% end %>
|
|
53
53
|
<div class="d-flex justify-content-between mb-1">
|
|
54
|
-
<small class="text-muted"
|
|
54
|
+
<small class="text-muted"><%= red_t("red.errors.llm_summary.total_time") %></small>
|
|
55
55
|
<small><strong><%= "%.1f" % llm_summary[:total_duration_ms] %>ms</strong></small>
|
|
56
56
|
</div>
|
|
57
57
|
<% if llm_summary[:error_count] > 0 %>
|
|
58
58
|
<div class="d-flex justify-content-between mb-1">
|
|
59
|
-
<small class="text-muted"
|
|
59
|
+
<small class="text-muted"><%= red_t("red.errors.llm_summary.errors") %></small>
|
|
60
60
|
<small><span class="badge bg-danger"><%= llm_summary[:error_count] %></span></small>
|
|
61
61
|
</div>
|
|
62
62
|
<% end %>
|
|
@@ -64,14 +64,14 @@
|
|
|
64
64
|
|
|
65
65
|
<% if llm_summary[:by_model].any? %>
|
|
66
66
|
<hr class="my-2">
|
|
67
|
-
<small class="text-muted d-block mb-2"
|
|
67
|
+
<small class="text-muted d-block mb-2"><%= red_t("red.errors.llm_summary.by_model") %></small>
|
|
68
68
|
<% llm_summary[:by_model].each do |row| %>
|
|
69
69
|
<div class="d-flex justify-content-between align-items-center mb-1" style="gap: 0.5rem;">
|
|
70
|
-
<small style="min-width: 0; overflow-wrap: anywhere;" title="<%= [ row[:provider], row[:model].presence || "unknown" ].compact.join(" · ") %>">
|
|
70
|
+
<small style="min-width: 0; overflow-wrap: anywhere;" title="<%= [ row[:provider], row[:model].presence || red_t("red.common.unknown") ].compact.join(" · ") %>">
|
|
71
71
|
<% if row[:provider].present? %>
|
|
72
72
|
<span class="text-muted"><%= row[:provider] %>·</span>
|
|
73
73
|
<% end %>
|
|
74
|
-
<strong><%= row[:model].presence || "unknown" %></strong>
|
|
74
|
+
<strong><%= row[:model].presence || red_t("red.common.unknown") %></strong>
|
|
75
75
|
</small>
|
|
76
76
|
<small class="text-muted text-nowrap">
|
|
77
77
|
<%= row[:calls] %> · <%= number_with_delimiter(row[:tokens]) %>tok
|
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
<div class="alert alert-danger py-1 px-2 mb-0 mt-2">
|
|
88
88
|
<small>
|
|
89
89
|
<i class="bi bi-exclamation-triangle" aria-hidden="true"></i>
|
|
90
|
-
<%= llm_summary
|
|
90
|
+
<%= red_tp("red.errors.llm_summary.failed_calls", count: llm_summary[:error_count]) %>
|
|
91
91
|
</small>
|
|
92
92
|
</div>
|
|
93
93
|
<% end %>
|
|
@@ -6,19 +6,19 @@
|
|
|
6
6
|
<div class="card mb-4" id="section-local-variables">
|
|
7
7
|
<div class="card-header">
|
|
8
8
|
<h5 class="mb-0">
|
|
9
|
-
<i class="bi bi-braces"></i>
|
|
10
|
-
<span class="badge bg-secondary"><%= local_vars.size
|
|
9
|
+
<i class="bi bi-braces"></i> <%= red_t("red.errors.variables.local_title") %>
|
|
10
|
+
<span class="badge bg-secondary"><%= red_tp("red.errors.variables.captured", count: local_vars.size) %></span>
|
|
11
11
|
</h5>
|
|
12
|
-
<small class="text-muted"
|
|
12
|
+
<small class="text-muted"><%= red_t("red.errors.variables.local_hint") %></small>
|
|
13
13
|
</div>
|
|
14
14
|
<div class="card-body p-0">
|
|
15
15
|
<div class="table-responsive">
|
|
16
16
|
<table class="table table-sm table-hover mb-0">
|
|
17
17
|
<thead class="table-light">
|
|
18
18
|
<tr>
|
|
19
|
-
<th width="200"
|
|
20
|
-
<th width="120"
|
|
21
|
-
<th
|
|
19
|
+
<th width="200"><%= red_t("red.errors.variables.column_variable") %></th>
|
|
20
|
+
<th width="120"><%= red_t("red.errors.variables.column_type") %></th>
|
|
21
|
+
<th><%= red_t("red.errors.variables.column_value") %></th>
|
|
22
22
|
</tr>
|
|
23
23
|
</thead>
|
|
24
24
|
<tbody>
|
|
@@ -28,11 +28,11 @@
|
|
|
28
28
|
<td><small class="text-muted"><%= info["type"] %></small></td>
|
|
29
29
|
<td>
|
|
30
30
|
<% if info["filtered"] %>
|
|
31
|
-
<span class="badge bg-warning text-dark"
|
|
31
|
+
<span class="badge bg-warning text-dark"><%= red_t("red.errors.variables.filtered") %></span>
|
|
32
32
|
<% else %>
|
|
33
33
|
<code style="word-break: break-all; font-size: 0.85em;"><%= truncate(info["value"].to_s, length: 500) %></code>
|
|
34
34
|
<% if info["truncated"] %>
|
|
35
|
-
<span class="badge bg-info text-dark ms-1" style="font-size: 0.7em;"
|
|
35
|
+
<span class="badge bg-info text-dark ms-1" style="font-size: 0.7em;"><%= red_t("red.errors.variables.truncated") %></span>
|
|
36
36
|
<% end %>
|
|
37
37
|
<% end %>
|
|
38
38
|
</td>
|