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,22 +1,22 @@
|
|
|
1
|
-
<% content_for :page_title, "
|
|
1
|
+
<% content_for :page_title, red_t("red.health.llm.title") %>
|
|
2
2
|
|
|
3
3
|
<div>
|
|
4
4
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
5
5
|
<h1 style="font-size: 20px; font-weight: 700; margin: 0;">
|
|
6
6
|
<i class="bi bi-cpu-fill me-2"></i>
|
|
7
|
-
|
|
7
|
+
<%= red_t("red.health.llm.title") %>
|
|
8
8
|
</h1>
|
|
9
9
|
|
|
10
10
|
<% unless @feature_disabled %>
|
|
11
11
|
<div class="btn-group" role="group">
|
|
12
12
|
<%= link_to llm_health_summary_errors_path(days: 7), class: "btn btn-sm #{@days == 7 ? 'btn-primary' : 'btn-outline-primary'}" do %>
|
|
13
|
-
7
|
|
13
|
+
<%= red_t("red.common.days_filter.7") %>
|
|
14
14
|
<% end %>
|
|
15
15
|
<%= link_to llm_health_summary_errors_path(days: 30), class: "btn btn-sm #{@days == 30 ? 'btn-primary' : 'btn-outline-primary'}" do %>
|
|
16
|
-
30
|
|
16
|
+
<%= red_t("red.common.days_filter.30") %>
|
|
17
17
|
<% end %>
|
|
18
18
|
<%= link_to llm_health_summary_errors_path(days: 90), class: "btn btn-sm #{@days == 90 ? 'btn-primary' : 'btn-outline-primary'}" do %>
|
|
19
|
-
90
|
|
19
|
+
<%= red_t("red.common.days_filter.90") %>
|
|
20
20
|
<% end %>
|
|
21
21
|
</div>
|
|
22
22
|
<% end %>
|
|
@@ -25,13 +25,13 @@
|
|
|
25
25
|
<% if @feature_disabled %>
|
|
26
26
|
<div class="red-empty-state">
|
|
27
27
|
<i class="bi bi-cpu-fill display-1 text-muted mb-3"></i>
|
|
28
|
-
<div class="red-empty-state-title"
|
|
28
|
+
<div class="red-empty-state-title"><%= red_t("red.health.llm.disabled.title") %></div>
|
|
29
29
|
<p class="text-muted">
|
|
30
|
-
|
|
30
|
+
<%= red_t("red.health.llm.disabled.message") %>
|
|
31
31
|
</p>
|
|
32
32
|
<div class="card mx-auto" style="max-width: 600px;">
|
|
33
33
|
<div class="card-body text-start">
|
|
34
|
-
<h6
|
|
34
|
+
<h6><%= red_t("red.health.llm.disabled.enable_in_html") %></h6>
|
|
35
35
|
<pre class="mb-0" style="background: var(--bg-subtle); padding: var(--space-3); border-radius: var(--radius-md); font-size: 12px;"><code>RailsErrorDashboard.configure do |config|
|
|
36
36
|
config.enable_breadcrumbs = true
|
|
37
37
|
config.enable_llm_observability = true
|
|
@@ -43,10 +43,9 @@ end</code></pre>
|
|
|
43
43
|
<% elsif @totals[:total_calls] == 0 && @totals[:total_tool_calls] == 0 %>
|
|
44
44
|
<div class="red-empty-state">
|
|
45
45
|
<i class="bi bi-cpu-fill display-1 text-success mb-3"></i>
|
|
46
|
-
<div class="red-empty-state-title"
|
|
46
|
+
<div class="red-empty-state-title"><%= red_t("red.health.llm.empty.title") %></div>
|
|
47
47
|
<p class="text-muted">
|
|
48
|
-
|
|
49
|
-
Instrument LLM calls in one of three ways:
|
|
48
|
+
<%= red_t("red.health.llm.empty.message", days: @days) %>
|
|
50
49
|
</p>
|
|
51
50
|
|
|
52
51
|
<div class="card mx-auto" style="max-width: 720px;">
|
|
@@ -54,41 +53,41 @@ end</code></pre>
|
|
|
54
53
|
<ul class="nav nav-tabs" role="tablist" id="llmPathTabs">
|
|
55
54
|
<li class="nav-item" role="presentation">
|
|
56
55
|
<button class="nav-link active" data-bs-toggle="tab" data-bs-target="#llmPathOtel" type="button" role="tab">
|
|
57
|
-
<i class="bi bi-diagram-3 me-1"></i>
|
|
56
|
+
<i class="bi bi-diagram-3 me-1"></i> <%= red_t("red.health.llm.empty.tab_otel") %>
|
|
58
57
|
</button>
|
|
59
58
|
</li>
|
|
60
59
|
<li class="nav-item" role="presentation">
|
|
61
60
|
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#llmPathFaraday" type="button" role="tab">
|
|
62
|
-
<i class="bi bi-arrow-left-right me-1"></i>
|
|
61
|
+
<i class="bi bi-arrow-left-right me-1"></i> <%= red_t("red.health.llm.empty.tab_faraday") %>
|
|
63
62
|
</button>
|
|
64
63
|
</li>
|
|
65
64
|
<li class="nav-item" role="presentation">
|
|
66
65
|
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#llmPathManual" type="button" role="tab">
|
|
67
|
-
<i class="bi bi-pencil-square me-1"></i>
|
|
66
|
+
<i class="bi bi-pencil-square me-1"></i> <%= red_t("red.health.llm.empty.tab_manual") %>
|
|
68
67
|
</button>
|
|
69
68
|
</li>
|
|
70
69
|
</ul>
|
|
71
70
|
|
|
72
71
|
<div class="tab-content p-3 text-start">
|
|
73
72
|
<div class="tab-pane fade show active" id="llmPathOtel" role="tabpanel">
|
|
74
|
-
<p class="mb-2"><small
|
|
73
|
+
<p class="mb-2"><small><%= red_t("red.health.llm.empty.otel_hint") %></small></p>
|
|
75
74
|
<pre class="mb-2" style="background: var(--bg-subtle); padding: var(--space-3); border-radius: var(--radius-md); font-size: 12px;"><code># Gemfile
|
|
76
75
|
gem "opentelemetry-instrumentation-ruby_llm"
|
|
77
76
|
|
|
78
77
|
# Then nothing else — OTel spans flow into RED breadcrumbs.</code></pre>
|
|
79
|
-
<small class="text-muted"
|
|
78
|
+
<small class="text-muted"><%= red_t("red.health.llm.empty.otel_requires_html") %></small>
|
|
80
79
|
</div>
|
|
81
80
|
|
|
82
81
|
<div class="tab-pane fade" id="llmPathFaraday" role="tabpanel">
|
|
83
|
-
<p class="mb-2"><small
|
|
82
|
+
<p class="mb-2"><small><%= red_t("red.health.llm.empty.faraday_hint") %></small></p>
|
|
84
83
|
<pre class="mb-2" style="background: var(--bg-subtle); padding: var(--space-3); border-radius: var(--radius-md); font-size: 12px;"><code>Faraday.default_connection_options.builder.tap do |b|
|
|
85
84
|
b.use RailsErrorDashboard::Integrations::LlmMiddleware
|
|
86
85
|
end</code></pre>
|
|
87
|
-
<small class="text-muted"
|
|
86
|
+
<small class="text-muted"><%= red_t("red.health.llm.empty.faraday_note") %></small>
|
|
88
87
|
</div>
|
|
89
88
|
|
|
90
89
|
<div class="tab-pane fade" id="llmPathManual" role="tabpanel">
|
|
91
|
-
<p class="mb-2"><small
|
|
90
|
+
<p class="mb-2"><small><%= red_t("red.health.llm.empty.manual_hint") %></small></p>
|
|
92
91
|
<pre class="mb-2" style="background: var(--bg-subtle); padding: var(--space-3); border-radius: var(--radius-md); font-size: 12px;"><code>ActiveSupport::Notifications.instrument(
|
|
93
92
|
"red.llm_call",
|
|
94
93
|
provider: "anthropic",
|
|
@@ -98,7 +97,7 @@ end</code></pre>
|
|
|
98
97
|
) do
|
|
99
98
|
call_anthropic(...)
|
|
100
99
|
end</code></pre>
|
|
101
|
-
<small class="text-muted"
|
|
100
|
+
<small class="text-muted"><%= red_t("red.health.llm.empty.manual_note_html") %></small>
|
|
102
101
|
</div>
|
|
103
102
|
</div>
|
|
104
103
|
</div>
|
|
@@ -111,7 +110,7 @@ end</code></pre>
|
|
|
111
110
|
<div class="card text-center">
|
|
112
111
|
<div class="card-body">
|
|
113
112
|
<div class="display-6 text-primary"><%= @totals[:total_calls] %></div>
|
|
114
|
-
<small class="text-muted"
|
|
113
|
+
<small class="text-muted"><%= red_t("red.health.llm.stats.calls") %></small>
|
|
115
114
|
</div>
|
|
116
115
|
</div>
|
|
117
116
|
</div>
|
|
@@ -119,7 +118,7 @@ end</code></pre>
|
|
|
119
118
|
<div class="card text-center">
|
|
120
119
|
<div class="card-body">
|
|
121
120
|
<div class="display-6 text-secondary"><%= @totals[:model_count] %></div>
|
|
122
|
-
<small class="text-muted"
|
|
121
|
+
<small class="text-muted"><%= red_t("red.health.llm.stats.models") %></small>
|
|
123
122
|
</div>
|
|
124
123
|
</div>
|
|
125
124
|
</div>
|
|
@@ -128,7 +127,7 @@ end</code></pre>
|
|
|
128
127
|
<div class="card-body">
|
|
129
128
|
<% err_color = @totals[:error_rate] >= 10 ? "danger" : (@totals[:error_rate] >= 5 ? "warning" : "success") %>
|
|
130
129
|
<div class="display-6 text-<%= err_color %>"><%= @totals[:unique_error_count] %></div>
|
|
131
|
-
<small class="text-muted"
|
|
130
|
+
<small class="text-muted"><%= red_t("red.health.llm.stats.errors_with_llm", rate: @totals[:error_rate]) %></small>
|
|
132
131
|
</div>
|
|
133
132
|
</div>
|
|
134
133
|
</div>
|
|
@@ -136,7 +135,7 @@ end</code></pre>
|
|
|
136
135
|
<div class="card text-center">
|
|
137
136
|
<div class="card-body">
|
|
138
137
|
<div class="display-6 text-info">$<%= format("%.2f", @totals[:total_cost_usd]) %></div>
|
|
139
|
-
<small class="text-muted"
|
|
138
|
+
<small class="text-muted"><%= red_t("red.health.llm.stats.total_cost") %></small>
|
|
140
139
|
</div>
|
|
141
140
|
</div>
|
|
142
141
|
</div>
|
|
@@ -146,7 +145,7 @@ end</code></pre>
|
|
|
146
145
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
147
146
|
<h5 class="mb-0">
|
|
148
147
|
<i class="bi bi-cpu-fill text-primary me-2"></i>
|
|
149
|
-
|
|
148
|
+
<%= red_t("red.health.llm.by_model.title") %>
|
|
150
149
|
<span class="badge bg-primary"><%= @totals[:model_count] %></span>
|
|
151
150
|
</h5>
|
|
152
151
|
<small class="text-muted"><%== @pagy.info_tag %></small>
|
|
@@ -156,15 +155,15 @@ end</code></pre>
|
|
|
156
155
|
<table class="table table-hover mb-0">
|
|
157
156
|
<thead class="table-light">
|
|
158
157
|
<tr>
|
|
159
|
-
<th
|
|
160
|
-
<th width="90"
|
|
161
|
-
<th width="80"
|
|
162
|
-
<th width="130"
|
|
163
|
-
<th width="110"
|
|
164
|
-
<th width="100"
|
|
165
|
-
<th width="100"
|
|
166
|
-
<th
|
|
167
|
-
<th width="140"
|
|
158
|
+
<th><%= red_t("red.health.llm.by_model.column_provider_model") %></th>
|
|
159
|
+
<th width="90"><%= red_t("red.health.llm.by_model.column_calls") %></th>
|
|
160
|
+
<th width="80"><%= red_t("red.health.llm.by_model.column_tools") %></th>
|
|
161
|
+
<th width="130"><%= red_t("red.health.llm.by_model.column_avg_tokens") %></th>
|
|
162
|
+
<th width="110"><%= red_t("red.health.llm.by_model.column_avg_latency") %></th>
|
|
163
|
+
<th width="100"><%= red_t("red.health.llm.by_model.column_error_rate") %></th>
|
|
164
|
+
<th width="100"><%= red_t("red.health.llm.by_model.column_cost") %></th>
|
|
165
|
+
<th><%= red_t("red.health.llm.by_model.column_top_error") %></th>
|
|
166
|
+
<th width="140"><%= red_t("red.health.llm.by_model.column_last_seen") %></th>
|
|
168
167
|
</tr>
|
|
169
168
|
</thead>
|
|
170
169
|
<tbody>
|
|
@@ -185,8 +184,8 @@ end</code></pre>
|
|
|
185
184
|
<td>
|
|
186
185
|
<% if entry[:avg_input_tokens] || entry[:avg_output_tokens] %>
|
|
187
186
|
<small>
|
|
188
|
-
|
|
189
|
-
|
|
187
|
+
<%= red_t("red.health.llm.by_model.avg_tokens_in") %> <strong><%= number_with_delimiter(entry[:avg_input_tokens] || 0) %></strong><br>
|
|
188
|
+
<%= red_t("red.health.llm.by_model.avg_tokens_out") %> <strong><%= number_with_delimiter(entry[:avg_output_tokens] || 0) %></strong>
|
|
190
189
|
</small>
|
|
191
190
|
<% else %>
|
|
192
191
|
<span class="text-muted">—</span>
|
|
@@ -204,7 +203,7 @@ end</code></pre>
|
|
|
204
203
|
<% sev_color = entry[:severity] == :danger ? "danger" : (entry[:severity] == :warning ? "warning" : "success") %>
|
|
205
204
|
<span class="badge bg-<%= sev_color %>"><%= entry[:error_rate] %>%</span>
|
|
206
205
|
<% if entry[:error_count].positive? %>
|
|
207
|
-
<br><small class="text-muted"><%= entry[:error_count]
|
|
206
|
+
<br><small class="text-muted"><%= red_t("red.health.llm.by_model.failed_count", count: entry[:error_count]) %></small>
|
|
208
207
|
<% end %>
|
|
209
208
|
</td>
|
|
210
209
|
<td>
|
|
@@ -232,11 +231,11 @@ end</code></pre>
|
|
|
232
231
|
<div class="card-footer border-top d-flex justify-content-between align-items-center">
|
|
233
232
|
<div>
|
|
234
233
|
<small class="text-muted">
|
|
235
|
-
<i class="bi bi-lightbulb text-warning"></i>
|
|
234
|
+
<i class="bi bi-lightbulb text-warning"></i> <%= red_t("red.health.llm.by_model.tip_html") %>
|
|
236
235
|
</small>
|
|
237
236
|
<small class="ms-3">
|
|
238
237
|
<a href="https://github.com/thoughtbot/opentelemetry-instrumentation-ruby_llm" target="_blank" rel="noopener" class="text-decoration-none">
|
|
239
|
-
<i class="bi bi-book"></i>
|
|
238
|
+
<i class="bi bi-book"></i> <%= red_t("red.health.llm.by_model.guide_link") %> <i class="bi bi-box-arrow-up-right" style="font-size: 0.7em;"></i>
|
|
240
239
|
</a>
|
|
241
240
|
</small>
|
|
242
241
|
</div>
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
<% content_for :page_title, "
|
|
1
|
+
<% content_for :page_title, red_t("red.errors.n_plus_one_page.title") %>
|
|
2
2
|
|
|
3
3
|
<div>
|
|
4
4
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
5
5
|
<h1 style="font-size: 20px; font-weight: 700; margin: 0;">
|
|
6
6
|
<i class="bi bi-arrow-repeat me-2"></i>
|
|
7
|
-
|
|
7
|
+
<%= red_t("red.errors.n_plus_one_page.title") %>
|
|
8
8
|
</h1>
|
|
9
9
|
|
|
10
10
|
<div class="btn-group" role="group">
|
|
11
11
|
<%= link_to n_plus_one_summary_errors_path(days: 7), class: "btn btn-sm #{@days == 7 ? 'btn-primary' : 'btn-outline-primary'}" do %>
|
|
12
|
-
7
|
|
12
|
+
<%= red_t("red.common.days_filter.7") %>
|
|
13
13
|
<% end %>
|
|
14
14
|
<%= link_to n_plus_one_summary_errors_path(days: 30), class: "btn btn-sm #{@days == 30 ? 'btn-primary' : 'btn-outline-primary'}" do %>
|
|
15
|
-
30
|
|
15
|
+
<%= red_t("red.common.days_filter.30") %>
|
|
16
16
|
<% end %>
|
|
17
17
|
<%= link_to n_plus_one_summary_errors_path(days: 90), class: "btn btn-sm #{@days == 90 ? 'btn-primary' : 'btn-outline-primary'}" do %>
|
|
18
|
-
90
|
|
18
|
+
<%= red_t("red.common.days_filter.90") %>
|
|
19
19
|
<% end %>
|
|
20
20
|
</div>
|
|
21
21
|
</div>
|
|
@@ -23,18 +23,21 @@
|
|
|
23
23
|
<% if @unique_count == 0 %>
|
|
24
24
|
<div class="red-empty-state">
|
|
25
25
|
<div class="red-empty-state-icon" style="background: var(--status-success-bg); color: var(--status-success);"><i class="bi bi-check-lg"></i></div>
|
|
26
|
-
<div class="red-empty-state-title"
|
|
26
|
+
<div class="red-empty-state-title"><%= red_t("red.errors.n_plus_one_page.empty_title") %></div>
|
|
27
27
|
<p class="text-muted">
|
|
28
|
-
|
|
28
|
+
<%= red_t("red.errors.n_plus_one_page.empty_body", days: @days) %>
|
|
29
29
|
</p>
|
|
30
30
|
<div class="card mx-auto" style="max-width: 500px;">
|
|
31
31
|
<div class="card-body text-start">
|
|
32
|
-
<h6
|
|
32
|
+
<h6><%= red_t("red.errors.n_plus_one_page.how_title") %></h6>
|
|
33
33
|
<ul class="mb-0">
|
|
34
|
-
<li
|
|
35
|
-
<li
|
|
36
|
-
<li
|
|
37
|
-
|
|
34
|
+
<li><%= red_t("red.errors.pages.how_breadcrumbs_html", option: content_tag(:code, "enable_breadcrumbs = true")) %></li>
|
|
35
|
+
<li><%= red_t("red.errors.n_plus_one_page.how_captured") %></li>
|
|
36
|
+
<li><%= red_t("red.errors.n_plus_one_page.how_threshold_html", threshold: content_tag(:code, "n_plus_one_threshold")) %></li>
|
|
37
|
+
<%# The default is read from the configuration rather than written
|
|
38
|
+
here too: a prose copy of a config value drifts the moment the
|
|
39
|
+
default changes, and the copy the user reads is the stale one. %>
|
|
40
|
+
<li><%= red_tp("red.errors.n_plus_one_page.how_default", count: RailsErrorDashboard.configuration.n_plus_one_threshold) %></li>
|
|
38
41
|
</ul>
|
|
39
42
|
</div>
|
|
40
43
|
</div>
|
|
@@ -45,7 +48,7 @@
|
|
|
45
48
|
<div class="card text-center">
|
|
46
49
|
<div class="card-body">
|
|
47
50
|
<div class="display-6 text-danger"><%= @unique_count %></div>
|
|
48
|
-
<small class="text-muted"
|
|
51
|
+
<small class="text-muted"><%= red_t("red.errors.n_plus_one_page.unique_patterns") %></small>
|
|
49
52
|
</div>
|
|
50
53
|
</div>
|
|
51
54
|
</div>
|
|
@@ -53,7 +56,7 @@
|
|
|
53
56
|
<div class="card text-center">
|
|
54
57
|
<div class="card-body">
|
|
55
58
|
<div class="display-6 text-warning"><%= @total_count %></div>
|
|
56
|
-
<small class="text-muted"
|
|
59
|
+
<small class="text-muted"><%= red_t("red.errors.pages.total_occurrences") %></small>
|
|
57
60
|
</div>
|
|
58
61
|
</div>
|
|
59
62
|
</div>
|
|
@@ -61,7 +64,7 @@
|
|
|
61
64
|
<div class="card text-center">
|
|
62
65
|
<div class="card-body">
|
|
63
66
|
<div class="display-6 text-info"><%= @affected_count %></div>
|
|
64
|
-
<small class="text-muted"
|
|
67
|
+
<small class="text-muted"><%= red_t("red.errors.pages.affected_errors") %></small>
|
|
65
68
|
</div>
|
|
66
69
|
</div>
|
|
67
70
|
</div>
|
|
@@ -71,7 +74,7 @@
|
|
|
71
74
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
72
75
|
<h5 class="mb-0">
|
|
73
76
|
<i class="bi bi-arrow-repeat text-danger me-2"></i>
|
|
74
|
-
|
|
77
|
+
<%= red_t("red.errors.n_plus_one_page.all_title") %>
|
|
75
78
|
<span class="badge bg-danger"><%= @unique_count %></span>
|
|
76
79
|
</h5>
|
|
77
80
|
<small class="text-muted"><%== @pagy.info_tag %></small>
|
|
@@ -81,11 +84,11 @@
|
|
|
81
84
|
<table class="table table-hover mb-0">
|
|
82
85
|
<thead class="table-light">
|
|
83
86
|
<tr>
|
|
84
|
-
<th
|
|
85
|
-
<th width="120"
|
|
86
|
-
<th width="250"
|
|
87
|
-
<th width="120"
|
|
88
|
-
<th width="140"
|
|
87
|
+
<th><%= red_t("red.errors.n_plus_one.column_query") %></th>
|
|
88
|
+
<th width="120"><%= red_t("red.errors.pages.column_occurrences") %></th>
|
|
89
|
+
<th width="250"><%= red_t("red.errors.pages.column_errors") %></th>
|
|
90
|
+
<th width="120"><%= red_t("red.errors.n_plus_one.column_total_time") %></th>
|
|
91
|
+
<th width="140"><%= red_t("red.errors.pages.column_last_seen") %></th>
|
|
89
92
|
</tr>
|
|
90
93
|
</thead>
|
|
91
94
|
<tbody>
|
|
@@ -99,12 +102,12 @@
|
|
|
99
102
|
<%= link_to "##{eid}", error_path(eid), class: "text-decoration-none" %><%= "," unless i == shown_ids.size - 1 %>
|
|
100
103
|
<% end %>
|
|
101
104
|
<% if pattern[:error_ids].size > 5 %>
|
|
102
|
-
<small class="text-muted"
|
|
105
|
+
<small class="text-muted"><%= red_tp("red.errors.pages.more_errors", count: pattern[:error_ids].size - 5) %></small>
|
|
103
106
|
<% end %>
|
|
104
107
|
</td>
|
|
105
108
|
<td>
|
|
106
109
|
<span class="<%= pattern[:total_duration_ms] > 100 ? 'text-danger fw-bold' : '' %>">
|
|
107
|
-
<%= pattern[:total_duration_ms] %>
|
|
110
|
+
<%= red_t("red.errors.n_plus_one_page.duration_ms", value: pattern[:total_duration_ms]) %>
|
|
108
111
|
</span>
|
|
109
112
|
</td>
|
|
110
113
|
<td><%= local_time_ago(pattern[:last_seen]) %></td>
|
|
@@ -117,11 +120,11 @@
|
|
|
117
120
|
<div class="card-footer border-top d-flex justify-content-between align-items-center">
|
|
118
121
|
<div>
|
|
119
122
|
<small class="text-muted">
|
|
120
|
-
<i class="bi bi-lightbulb text-warning"></i>
|
|
123
|
+
<i class="bi bi-lightbulb text-warning"></i> <%= red_t("red.errors.n_plus_one_page.footer_tip_html", includes: content_tag(:code, "includes"), preload: content_tag(:code, "preload"), eager_load: content_tag(:code, "eager_load")) %>
|
|
121
124
|
</small>
|
|
122
125
|
<small class="ms-3">
|
|
123
126
|
<a href="https://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations" target="_blank" rel="noopener" class="text-decoration-none">
|
|
124
|
-
<i class="bi bi-book"></i>
|
|
127
|
+
<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>
|
|
125
128
|
</a>
|
|
126
129
|
</small>
|
|
127
130
|
</div>
|