rails_error_dashboard 0.5.14 → 0.6.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/app/controllers/rails_error_dashboard/errors_controller.rb +31 -26
- data/app/helpers/rails_error_dashboard/application_helper.rb +12 -5
- data/app/views/layouts/rails_error_dashboard.html.erb +1218 -1936
- data/app/views/rails_error_dashboard/errors/_breadcrumbs_group.html.erb +4 -4
- data/app/views/rails_error_dashboard/errors/_co_occurring_errors.html.erb +1 -1
- data/app/views/rails_error_dashboard/errors/_discussion.html.erb +3 -3
- data/app/views/rails_error_dashboard/errors/_error_cascades.html.erb +1 -1
- data/app/views/rails_error_dashboard/errors/_error_row.html.erb +69 -79
- data/app/views/rails_error_dashboard/errors/_instance_variables.html.erb +1 -1
- data/app/views/rails_error_dashboard/errors/_issue_section.html.erb +1 -1
- data/app/views/rails_error_dashboard/errors/_local_variables.html.erb +1 -1
- data/app/views/rails_error_dashboard/errors/_pattern_insights.html.erb +2 -2
- data/app/views/rails_error_dashboard/errors/_request_context.html.erb +1 -1
- data/app/views/rails_error_dashboard/errors/_sidebar_metadata.html.erb +1 -1
- data/app/views/rails_error_dashboard/errors/_similar_errors.html.erb +1 -1
- data/app/views/rails_error_dashboard/errors/_timeline.html.erb +1 -1
- data/app/views/rails_error_dashboard/errors/actioncable_health_summary.html.erb +6 -6
- data/app/views/rails_error_dashboard/errors/activestorage_health_summary.html.erb +6 -6
- data/app/views/rails_error_dashboard/errors/analytics.html.erb +34 -50
- data/app/views/rails_error_dashboard/errors/cache_health_summary.html.erb +7 -7
- data/app/views/rails_error_dashboard/errors/correlation.html.erb +11 -11
- data/app/views/rails_error_dashboard/errors/database_health_summary.html.erb +114 -172
- data/app/views/rails_error_dashboard/errors/deprecations.html.erb +7 -7
- data/app/views/rails_error_dashboard/errors/diagnostic_dumps.html.erb +6 -6
- data/app/views/rails_error_dashboard/errors/index.html.erb +294 -622
- data/app/views/rails_error_dashboard/errors/job_health_summary.html.erb +7 -7
- data/app/views/rails_error_dashboard/errors/n_plus_one_summary.html.erb +7 -7
- data/app/views/rails_error_dashboard/errors/overview.html.erb +192 -363
- data/app/views/rails_error_dashboard/errors/platform_comparison.html.erb +11 -11
- data/app/views/rails_error_dashboard/errors/rack_attack_summary.html.erb +6 -6
- data/app/views/rails_error_dashboard/errors/releases.html.erb +6 -6
- data/app/views/rails_error_dashboard/errors/settings.html.erb +32 -52
- data/app/views/rails_error_dashboard/errors/show.html.erb +200 -203
- data/app/views/rails_error_dashboard/errors/swallowed_exceptions.html.erb +7 -7
- data/app/views/rails_error_dashboard/errors/user_impact.html.erb +6 -6
- data/lib/rails_error_dashboard/commands/log_error.rb +14 -3
- data/lib/rails_error_dashboard/configuration.rb +6 -0
- data/lib/rails_error_dashboard/error_reporter.rb +11 -1
- data/lib/rails_error_dashboard/services/backtrace_parser.rb +10 -4
- data/lib/rails_error_dashboard/services/backtrace_processor.rb +44 -2
- data/lib/rails_error_dashboard/services/error_broadcaster.rb +19 -4
- data/lib/rails_error_dashboard/services/notification_helpers.rb +9 -2
- data/lib/rails_error_dashboard/subscribers/issue_tracker_subscriber.rb +21 -0
- data/lib/rails_error_dashboard/version.rb +1 -1
- metadata +2 -2
|
@@ -1,229 +1,188 @@
|
|
|
1
1
|
<% content_for :page_title, "Dashboard" %>
|
|
2
|
-
<div
|
|
3
|
-
<!-- Page Header -->
|
|
2
|
+
<div style="max-width: 100%;">
|
|
4
3
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
5
|
-
<h1
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
</
|
|
9
|
-
<div class="text-muted">
|
|
10
|
-
<small>
|
|
11
|
-
Last updated: <%= local_time(Time.current, format: :full) %>
|
|
12
|
-
</small>
|
|
13
|
-
</div>
|
|
4
|
+
<h1 style="font-size: 20px; font-weight: 700; margin: 0;">Overview</h1>
|
|
5
|
+
<span style="font-size: 12px; color: var(--text-tertiary);">
|
|
6
|
+
Last updated: <%= local_time(Time.current, format: :full) %>
|
|
7
|
+
</span>
|
|
14
8
|
</div>
|
|
15
9
|
|
|
16
|
-
<!--
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
</div>
|
|
10
|
+
<!-- Spike / Critical Alerts Banner -->
|
|
11
|
+
<% if @critical_alerts.any? %>
|
|
12
|
+
<%
|
|
13
|
+
top_alert = @critical_alerts.first
|
|
14
|
+
alert_count = @critical_alerts.count
|
|
15
|
+
%>
|
|
16
|
+
<div class="alert-danger" style="display: flex; align-items: center; gap: 10px; padding: 10px var(--space-5); background: var(--status-critical-bg); border-radius: var(--radius-md); border: 1px solid var(--status-critical); margin-bottom: var(--space-6); font-size: 13px; color: var(--status-critical);">
|
|
17
|
+
<span style="width: 8px; height: 8px; border-radius: 50%; background: var(--status-critical); animation: pulse 2s infinite; flex-shrink: 0;"></span>
|
|
18
|
+
<strong><%= alert_count %> Critical/High Error<%= alert_count != 1 ? 's' : '' %> in Last Hour</strong>
|
|
19
|
+
<span style="color: var(--text-secondary);">—</span>
|
|
20
|
+
<span style="color: var(--text-secondary);"><%= link_to error_path(top_alert, **app_context), style: "background: transparent; padding: 0; color: var(--status-critical); text-decoration: none; font-family: var(--font-mono); font-size: 13px;" do %><%= top_alert.error_type %><% end %> · <%= top_alert.occurrence_count %> occurrences</span>
|
|
21
|
+
<%= link_to errors_path(app_context.merge(severity: 'critical')), style: "margin-left: auto; padding: 4px 12px; font-size: 12px; font-weight: 600; border-radius: var(--radius-full); border: 1px solid var(--status-critical); background: transparent; color: var(--status-critical); text-decoration: none; white-space: nowrap;" do %>View<% end %>
|
|
29
22
|
</div>
|
|
23
|
+
<% end %>
|
|
30
24
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
25
|
+
<!-- Hero Stats (3 cards) -->
|
|
26
|
+
<%
|
|
27
|
+
total_errors = @stats[:resolved] + @stats[:unresolved]
|
|
28
|
+
resolution_rate = total_errors > 0 ? ((@stats[:resolved].to_f / total_errors) * 100).round(1) : 0.0
|
|
29
|
+
%>
|
|
30
|
+
<%
|
|
31
|
+
# Build sparkline data from 7-day trend
|
|
32
|
+
trend_values = @stats[:errors_trend_7d].values rescue []
|
|
33
|
+
trend_max = [ trend_values.max || 1, 1 ].max
|
|
34
|
+
%>
|
|
35
|
+
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-4); margin-bottom: var(--space-6);">
|
|
36
|
+
<!-- Error Rate -->
|
|
37
|
+
<div class="card stat-card" style="border-left: 3px solid var(--status-critical); padding: var(--space-5) var(--space-6);">
|
|
38
|
+
<div class="stat-label" style="margin-bottom: 4px;">Error Rate</div>
|
|
39
|
+
<div style="display: flex; align-items: baseline; gap: 8px;">
|
|
40
|
+
<span class="stat-value" style="color: var(--status-critical);"><%= @stats[:error_rate] %>%</span>
|
|
41
|
+
<% if @stats[:trend_percentage].present? && @stats[:trend_percentage] != 0 %>
|
|
42
|
+
<span style="display: inline-flex; align-items: center; gap: 2px; font-size: 12px; font-weight: 600; color: <%= @stats[:trend_percentage] > 0 ? 'var(--status-critical)' : 'var(--status-success)' %>;">
|
|
43
|
+
<i class="bi <%= @stats[:trend_percentage] > 0 ? 'bi-arrow-up-right' : 'bi-arrow-down-right' %>" style="font-size: 10px;"></i>
|
|
44
|
+
<%= @stats[:trend_percentage].abs %>%
|
|
45
|
+
</span>
|
|
46
|
+
<% end %>
|
|
45
47
|
</div>
|
|
48
|
+
<% if trend_values.any? %>
|
|
49
|
+
<div style="display: flex; align-items: flex-end; gap: 2px; height: 24px; margin-top: var(--space-2);">
|
|
50
|
+
<% trend_values.each do |v| %>
|
|
51
|
+
<div style="flex: 1; background: var(--status-critical); border-radius: 1px; min-height: 2px; height: <%= (v.to_f / trend_max * 100).round %>%; opacity: <%= v > 0 ? 0.7 : 0.15 %>;"></div>
|
|
52
|
+
<% end %>
|
|
53
|
+
</div>
|
|
54
|
+
<% end %>
|
|
55
|
+
<small style="color: var(--text-tertiary); margin-top: var(--space-1);">Errors per hour today</small>
|
|
46
56
|
</div>
|
|
47
57
|
|
|
48
|
-
<!-- Unresolved
|
|
49
|
-
<div class="
|
|
50
|
-
<div class="
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
<div class="stat-value text-danger">
|
|
54
|
-
<%= @stats[:unresolved] %>
|
|
55
|
-
</div>
|
|
56
|
-
<small class="text-muted">
|
|
57
|
-
Pending resolution
|
|
58
|
-
</small>
|
|
59
|
-
</div>
|
|
58
|
+
<!-- Unresolved -->
|
|
59
|
+
<div class="card stat-card" style="border-left: 3px solid var(--accent); padding: var(--space-5) var(--space-6);">
|
|
60
|
+
<div class="stat-label" style="margin-bottom: 4px;">Unresolved</div>
|
|
61
|
+
<div style="display: flex; align-items: baseline; gap: 8px;">
|
|
62
|
+
<span class="stat-value"><%= @stats[:unresolved] %></span>
|
|
60
63
|
</div>
|
|
64
|
+
<% if trend_values.any? %>
|
|
65
|
+
<div style="display: flex; align-items: flex-end; gap: 2px; height: 24px; margin-top: var(--space-2);">
|
|
66
|
+
<% trend_values.each do |v| %>
|
|
67
|
+
<div style="flex: 1; background: var(--accent); border-radius: 1px; min-height: 2px; height: <%= (v.to_f / trend_max * 100).round %>%; opacity: <%= v > 0 ? 0.7 : 0.15 %>;"></div>
|
|
68
|
+
<% end %>
|
|
69
|
+
</div>
|
|
70
|
+
<% end %>
|
|
71
|
+
<small style="color: var(--text-tertiary); margin-top: var(--space-1);">Pending resolution</small>
|
|
61
72
|
</div>
|
|
62
73
|
|
|
63
|
-
<!--
|
|
64
|
-
<div class="
|
|
65
|
-
<div class="
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
</small>
|
|
74
|
+
<!-- Resolution Rate -->
|
|
75
|
+
<div class="card stat-card" style="border-left: 3px solid var(--status-success); padding: var(--space-5) var(--space-6);">
|
|
76
|
+
<div class="stat-label" style="margin-bottom: 4px;">Resolution Rate</div>
|
|
77
|
+
<div style="display: flex; align-items: baseline; gap: 8px;">
|
|
78
|
+
<span class="stat-value" style="color: var(--status-success);"><%= resolution_rate %>%</span>
|
|
79
|
+
</div>
|
|
80
|
+
<% if trend_values.any? %>
|
|
81
|
+
<div style="display: flex; align-items: flex-end; gap: 2px; height: 24px; margin-top: var(--space-2);">
|
|
82
|
+
<% trend_values.each do |v| %>
|
|
83
|
+
<div style="flex: 1; background: var(--status-success); border-radius: 1px; min-height: 2px; height: <%= (v.to_f / trend_max * 100).round %>%; opacity: <%= v > 0 ? 0.7 : 0.15 %>;"></div>
|
|
84
|
+
<% end %>
|
|
75
85
|
</div>
|
|
86
|
+
<% end %>
|
|
87
|
+
<small style="color: var(--text-tertiary); margin-top: var(--space-1);"><%= @stats[:resolved] %> of <%= total_errors %> resolved</small>
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
<!-- Secondary Stats (3 cards) -->
|
|
92
|
+
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-4); margin-bottom: var(--space-6);">
|
|
93
|
+
<!-- Affected Users -->
|
|
94
|
+
<div class="card stat-card" style="padding: var(--space-5) var(--space-6);">
|
|
95
|
+
<div class="stat-label" style="margin-bottom: 4px;">Affected Users</div>
|
|
96
|
+
<div style="display: flex; align-items: baseline; gap: 8px;">
|
|
97
|
+
<span class="stat-value"><%= @stats[:affected_users_today] %></span>
|
|
98
|
+
<% if @stats[:affected_users_change] != 0 %>
|
|
99
|
+
<span style="display: inline-flex; align-items: center; gap: 2px; font-size: 12px; font-weight: 600; color: <%= @stats[:affected_users_change] > 0 ? 'var(--status-critical)' : 'var(--status-success)' %>;">
|
|
100
|
+
<i class="bi <%= @stats[:affected_users_change] > 0 ? 'bi-arrow-up-right' : 'bi-arrow-down-right' %>" style="font-size: 10px;"></i>
|
|
101
|
+
<%= @stats[:affected_users_change].abs %> from yesterday
|
|
102
|
+
</span>
|
|
103
|
+
<% end %>
|
|
76
104
|
</div>
|
|
77
105
|
</div>
|
|
78
106
|
|
|
79
|
-
<!-- Resolution
|
|
80
|
-
<div class="
|
|
81
|
-
<div class="
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
<div class="stat-value <%= resolution_rate >= 80 ? 'text-success' : resolution_rate >= 50 ? 'text-warning' : 'text-danger' %>">
|
|
89
|
-
<%= resolution_rate %>%
|
|
90
|
-
</div>
|
|
91
|
-
<small class="text-muted">
|
|
92
|
-
<%= @stats[:resolved] %> of <%= total_errors %> resolved
|
|
93
|
-
</small>
|
|
94
|
-
</div>
|
|
107
|
+
<!-- Avg Resolution Time -->
|
|
108
|
+
<div class="card stat-card" style="padding: var(--space-5) var(--space-6);">
|
|
109
|
+
<div class="stat-label" style="margin-bottom: 4px;">Avg Resolution</div>
|
|
110
|
+
<div style="display: flex; align-items: baseline; gap: 8px;">
|
|
111
|
+
<% if @stats[:average_resolution_time].present? %>
|
|
112
|
+
<span class="stat-value"><%= @stats[:average_resolution_time] %>h</span>
|
|
113
|
+
<% else %>
|
|
114
|
+
<span class="stat-value" style="color: var(--text-tertiary);">—</span>
|
|
115
|
+
<% end %>
|
|
95
116
|
</div>
|
|
117
|
+
<small style="color: var(--text-tertiary);">Mean time to resolution</small>
|
|
96
118
|
</div>
|
|
97
119
|
|
|
98
|
-
<!--
|
|
99
|
-
<div class="
|
|
100
|
-
<div class="
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
<% if @stats[:average_resolution_time].present? %>
|
|
104
|
-
<div class="stat-value text-warning">
|
|
105
|
-
<%= @stats[:average_resolution_time] %>h
|
|
106
|
-
</div>
|
|
107
|
-
<small class="text-muted">
|
|
108
|
-
Mean time to resolution
|
|
109
|
-
</small>
|
|
110
|
-
<% else %>
|
|
111
|
-
<div class="stat-value text-muted">
|
|
112
|
-
--
|
|
113
|
-
</div>
|
|
114
|
-
<small class="text-muted">
|
|
115
|
-
No resolved errors yet
|
|
116
|
-
</small>
|
|
117
|
-
<% end %>
|
|
118
|
-
</div>
|
|
120
|
+
<!-- Error Trend -->
|
|
121
|
+
<div class="card stat-card" style="padding: var(--space-5) var(--space-6);">
|
|
122
|
+
<div class="stat-label" style="margin-bottom: 4px;">Error Trend</div>
|
|
123
|
+
<div style="display: flex; align-items: baseline; gap: 8px;">
|
|
124
|
+
<span class="stat-value"><%= trend_arrow(@stats[:trend_percentage]) %> <%= @stats[:trend_percentage].abs %>%</span>
|
|
119
125
|
</div>
|
|
126
|
+
<small style="color: var(--text-tertiary);"><%= trend_text(@stats[:trend_direction]) %></small>
|
|
120
127
|
</div>
|
|
121
128
|
</div>
|
|
122
129
|
|
|
123
|
-
<!-- Top
|
|
130
|
+
<!-- Top Errors by Impact -->
|
|
124
131
|
<% if @stats[:top_errors_by_impact].any? %>
|
|
125
|
-
<div class="card
|
|
126
|
-
<div
|
|
127
|
-
<
|
|
128
|
-
|
|
129
|
-
Top 6 Errors by Impact
|
|
130
|
-
</h5>
|
|
131
|
-
<%= link_to "View All Errors →", errors_path, class: "btn btn-sm btn-outline-primary" %>
|
|
132
|
+
<div class="card" style="margin-bottom: var(--space-6);">
|
|
133
|
+
<div style="display: flex; justify-content: space-between; align-items: center; padding: var(--space-4) var(--space-6); border-bottom: 1px solid var(--border-primary);">
|
|
134
|
+
<span style="font-size: 13px; font-weight: 600; color: var(--text-primary);">Top Errors by Impact</span>
|
|
135
|
+
<%= link_to errors_path(app_context), style: "font-size: 12px; color: var(--accent); text-decoration: none; font-weight: 500;" do %>View all →<% end %>
|
|
132
136
|
</div>
|
|
133
|
-
<div
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
<
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
</div>
|
|
151
|
-
<div class="d-flex justify-content-between align-items-center small">
|
|
152
|
-
<span class="text-muted">
|
|
153
|
-
<i class="bi bi-people-fill me-1"></i>
|
|
154
|
-
<%= error[:affected_users] %> user<%= error[:affected_users] != 1 ? 's' : '' %>
|
|
155
|
-
</span>
|
|
156
|
-
<span class="text-muted">
|
|
157
|
-
<i class="bi bi-arrow-repeat me-1"></i>
|
|
158
|
-
<%= error[:occurrence_count] %> occurrence<%= error[:occurrence_count] != 1 ? 's' : '' %>
|
|
159
|
-
</span>
|
|
160
|
-
<span class="fw-bold text-danger">
|
|
161
|
-
Impact: <%= error[:impact_score] %>
|
|
162
|
-
</span>
|
|
163
|
-
</div>
|
|
164
|
-
</div>
|
|
165
|
-
</div>
|
|
166
|
-
<% end %>
|
|
137
|
+
<div>
|
|
138
|
+
<% @stats[:top_errors_by_impact].each_with_index do |error, i| %>
|
|
139
|
+
<%= link_to error_path(error[:id], **app_context), style: "display: flex; align-items: center; gap: var(--space-4); padding: var(--space-4) var(--space-6); border-bottom: #{i < @stats[:top_errors_by_impact].length - 1 ? '1px solid var(--border-primary)' : 'none'}; cursor: pointer; transition: background 0.1s ease; text-decoration: none; color: inherit;", class: "top-error-row" do %>
|
|
140
|
+
<span style="width: 24px; text-align: center; font-size: 12px; font-weight: 600; color: var(--text-tertiary);"><%= i + 1 %></span>
|
|
141
|
+
<span class="badge bg-<%= severity_color(error[:severity]) %>" style="display: inline-flex; align-items: center; gap: 4px;">
|
|
142
|
+
<span style="width: 6px; height: 6px; border-radius: 50%; background: currentColor;"></span>
|
|
143
|
+
<%= error[:severity] %>
|
|
144
|
+
</span>
|
|
145
|
+
<div style="flex: 1; min-width: 0;">
|
|
146
|
+
<div style="font-size: 13px; font-weight: 600; color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-family: var(--font-mono); font-size: 12px;">
|
|
147
|
+
<%= error[:error_type] %>
|
|
148
|
+
</div>
|
|
149
|
+
<div style="font-size: 12px; color: var(--text-tertiary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;"><%= error[:message]&.truncate(80) %></div>
|
|
150
|
+
</div>
|
|
151
|
+
<div style="text-align: right; flex-shrink: 0;">
|
|
152
|
+
<div style="font-size: 14px; font-weight: 600; color: var(--text-primary); font-variant-numeric: tabular-nums;"><%= error[:occurrence_count]&.to_s(:delimited) rescue error[:occurrence_count] %></div>
|
|
153
|
+
<div style="font-size: 11px; color: var(--text-tertiary);"><%= error[:affected_users] %> users</div>
|
|
167
154
|
</div>
|
|
168
155
|
<% end %>
|
|
169
|
-
|
|
156
|
+
<% end %>
|
|
170
157
|
</div>
|
|
171
158
|
</div>
|
|
172
159
|
<% end %>
|
|
173
160
|
|
|
174
|
-
<!-- Platform Health Summary
|
|
161
|
+
<!-- Platform Health Summary -->
|
|
175
162
|
<% if @platform_health.any? %>
|
|
176
|
-
<div class="card
|
|
177
|
-
<div
|
|
178
|
-
<
|
|
179
|
-
<i class="bi bi-phone me-2"></i>
|
|
180
|
-
Platform Health
|
|
181
|
-
</h5>
|
|
163
|
+
<div class="card" style="margin-bottom: var(--space-6);">
|
|
164
|
+
<div style="display: flex; justify-content: space-between; align-items: center; padding: var(--space-4) var(--space-6); border-bottom: 1px solid var(--border-primary);">
|
|
165
|
+
<span style="font-size: 13px; font-weight: 600; color: var(--text-primary);">Platform Health</span>
|
|
182
166
|
<% if RailsErrorDashboard.configuration.enable_platform_comparison %>
|
|
183
|
-
<%= link_to
|
|
167
|
+
<%= link_to platform_comparison_errors_path(app_context), style: "font-size: 12px; color: var(--accent); text-decoration: none; font-weight: 500;" do %>Full comparison →<% end %>
|
|
184
168
|
<% end %>
|
|
185
169
|
</div>
|
|
186
|
-
<div
|
|
187
|
-
<div
|
|
170
|
+
<div style="padding: var(--space-5) var(--space-6);">
|
|
171
|
+
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: var(--space-4);">
|
|
188
172
|
<% @platform_health.each do |platform, health| %>
|
|
189
|
-
<div
|
|
190
|
-
<div
|
|
191
|
-
<
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
<h4 class="mb-0 me-2"><%= health[:stability_score] %>/100</h4>
|
|
203
|
-
<div class="progress flex-grow-1" style="height: 8px;">
|
|
204
|
-
<div class="progress-bar bg-<%= health_status_color(health[:health_status]) %>"
|
|
205
|
-
role="progressbar"
|
|
206
|
-
style="width: <%= health[:stability_score] %>%">
|
|
207
|
-
</div>
|
|
208
|
-
</div>
|
|
209
|
-
</div>
|
|
210
|
-
</div>
|
|
211
|
-
|
|
212
|
-
<div class="small">
|
|
213
|
-
<div class="d-flex justify-content-between mb-1">
|
|
214
|
-
<span class="text-muted">Total Errors:</span>
|
|
215
|
-
<strong><%= health[:total_errors] %></strong>
|
|
216
|
-
</div>
|
|
217
|
-
<div class="d-flex justify-content-between mb-1">
|
|
218
|
-
<span class="text-muted">Critical:</span>
|
|
219
|
-
<strong class="text-danger"><%= health[:critical_errors] %></strong>
|
|
220
|
-
</div>
|
|
221
|
-
<div class="d-flex justify-content-between">
|
|
222
|
-
<span class="text-muted">Unresolved:</span>
|
|
223
|
-
<strong><%= health[:unresolved_errors] %></strong>
|
|
224
|
-
</div>
|
|
225
|
-
</div>
|
|
226
|
-
</div>
|
|
173
|
+
<div style="padding: var(--space-4); border: 1px solid var(--border-primary); border-radius: var(--radius-md);">
|
|
174
|
+
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: var(--space-3);">
|
|
175
|
+
<span style="font-size: 13px; font-weight: 600; text-transform: capitalize;"><%= platform %></span>
|
|
176
|
+
<span class="badge bg-<%= health_status_color(health[:health_status]) %>"><%= health_status_text(health[:health_status]) %></span>
|
|
177
|
+
</div>
|
|
178
|
+
<div style="margin-bottom: var(--space-2);">
|
|
179
|
+
<div style="font-size: 20px; font-weight: 700; font-variant-numeric: tabular-nums;"><%= health[:stability_score] %><span style="font-size: 12px; color: var(--text-tertiary); font-weight: 400;">/100</span></div>
|
|
180
|
+
</div>
|
|
181
|
+
<div class="progress" style="height: 4px; margin-bottom: var(--space-3);">
|
|
182
|
+
<div class="progress-bar" style="width: <%= health[:stability_score] %>%; background: var(--status-<%= health[:health_status] == :healthy ? 'success' : health[:health_status] == :warning ? 'warning' : 'critical' %>);"></div>
|
|
183
|
+
</div>
|
|
184
|
+
<div style="font-size: 12px; color: var(--text-tertiary);">
|
|
185
|
+
<%= health[:total_errors] %> errors · <%= health[:critical_errors] %> critical · <%= health[:unresolved_errors] %> open
|
|
227
186
|
</div>
|
|
228
187
|
</div>
|
|
229
188
|
<% end %>
|
|
@@ -232,198 +191,68 @@
|
|
|
232
191
|
</div>
|
|
233
192
|
<% end %>
|
|
234
193
|
|
|
235
|
-
<!-- Correlation
|
|
194
|
+
<!-- Correlation Insights -->
|
|
236
195
|
<% if RailsErrorDashboard.configuration.enable_error_correlation && (@problematic_releases.any? || @time_correlated_errors.any? || @multi_error_users.any?) %>
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
when 1 then "col-12"
|
|
242
|
-
when 2 then "col-12 col-lg-6"
|
|
243
|
-
else "col-12 col-lg-4"
|
|
244
|
-
end
|
|
245
|
-
%>
|
|
246
|
-
<div class="card mb-4">
|
|
247
|
-
<div class="card-header bg-white d-flex justify-content-between align-items-center">
|
|
248
|
-
<h5 class="mb-0">
|
|
249
|
-
<i class="bi bi-diagram-3 me-2"></i>
|
|
250
|
-
Error Correlation Insights
|
|
251
|
-
</h5>
|
|
252
|
-
<%= link_to "Full Analysis →", correlation_errors_path, class: "btn btn-sm btn-outline-primary" %>
|
|
196
|
+
<div class="card" style="margin-bottom: var(--space-6);">
|
|
197
|
+
<div style="display: flex; justify-content: space-between; align-items: center; padding: var(--space-4) var(--space-6); border-bottom: 1px solid var(--border-primary);">
|
|
198
|
+
<span style="font-size: 13px; font-weight: 600; color: var(--text-primary);">Correlation Insights</span>
|
|
199
|
+
<%= link_to correlation_errors_path(app_context), style: "font-size: 12px; color: var(--accent); text-decoration: none; font-weight: 500;" do %>Full analysis →<% end %>
|
|
253
200
|
</div>
|
|
254
|
-
<div
|
|
255
|
-
<div
|
|
256
|
-
<!-- Problematic Releases -->
|
|
201
|
+
<div style="padding: var(--space-5) var(--space-6);">
|
|
202
|
+
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: var(--space-4);">
|
|
257
203
|
<% if @problematic_releases.any? %>
|
|
258
|
-
<div
|
|
259
|
-
<div
|
|
260
|
-
<
|
|
261
|
-
<h6 class="mb-3">
|
|
262
|
-
<i class="bi bi-tag me-2"></i>
|
|
263
|
-
Problematic Releases
|
|
264
|
-
</h6>
|
|
265
|
-
<div class="list-group list-group-flush">
|
|
266
|
-
<% @problematic_releases.each do |release| %>
|
|
267
|
-
<div class="list-group-item px-0 py-2">
|
|
268
|
-
<div class="d-flex justify-content-between align-items-start">
|
|
269
|
-
<div class="flex-grow-1">
|
|
270
|
-
<code class="small"><%= release[:version] || release[:git_sha]&.truncate(8, omission: '') %></code>
|
|
271
|
-
<div class="small text-muted">
|
|
272
|
-
<%= release[:error_count] %> error<%= release[:error_count] != 1 ? 's' : '' %> •
|
|
273
|
-
<%= release[:unique_types] %> type<%= release[:unique_types] != 1 ? 's' : '' %>
|
|
274
|
-
</div>
|
|
275
|
-
</div>
|
|
276
|
-
<span class="badge bg-danger"><%= release[:severity_score] %></span>
|
|
277
|
-
</div>
|
|
278
|
-
</div>
|
|
279
|
-
<% end %>
|
|
280
|
-
</div>
|
|
281
|
-
</div>
|
|
204
|
+
<div style="padding: var(--space-4); border: 1px solid var(--border-primary); border-radius: var(--radius-md); border-left: 3px solid var(--status-warning);">
|
|
205
|
+
<div style="font-size: 12px; font-weight: 600; color: var(--text-secondary); margin-bottom: var(--space-3);">
|
|
206
|
+
<i class="bi bi-tag me-1"></i> Problematic Releases
|
|
282
207
|
</div>
|
|
208
|
+
<% @problematic_releases.each do |release| %>
|
|
209
|
+
<div style="display: flex; justify-content: space-between; align-items: center; padding: var(--space-2) 0; border-bottom: 1px solid var(--border-primary); font-size: 12px;">
|
|
210
|
+
<code style="font-size: 11px;"><%= release[:version] || release[:git_sha]&.truncate(8, omission: '') %></code>
|
|
211
|
+
<span style="font-weight: 600;"><%= release[:error_count] %> errors</span>
|
|
212
|
+
</div>
|
|
213
|
+
<% end %>
|
|
283
214
|
</div>
|
|
284
215
|
<% end %>
|
|
285
216
|
|
|
286
|
-
<!-- Time-Correlated Errors -->
|
|
287
217
|
<% if @time_correlated_errors.any? %>
|
|
288
|
-
<div
|
|
289
|
-
<div
|
|
290
|
-
<
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
<i class="bi bi-arrow-left-right text-muted"></i>
|
|
302
|
-
<code class="text-truncate"><%= data[:error_type_b] %></code>
|
|
303
|
-
</div>
|
|
304
|
-
<div class="text-muted mt-1">
|
|
305
|
-
Correlation: <strong><%= (data[:correlation] * 100).round(0) %>%</strong>
|
|
306
|
-
(<%= data[:strength] %>)
|
|
307
|
-
</div>
|
|
308
|
-
</div>
|
|
309
|
-
</div>
|
|
310
|
-
<% end %>
|
|
218
|
+
<div style="padding: var(--space-4); border: 1px solid var(--border-primary); border-radius: var(--radius-md); border-left: 3px solid var(--status-info);">
|
|
219
|
+
<div style="font-size: 12px; font-weight: 600; color: var(--text-secondary); margin-bottom: var(--space-3);">
|
|
220
|
+
<i class="bi bi-clock-history me-1"></i> Time-Correlated
|
|
221
|
+
</div>
|
|
222
|
+
<% @time_correlated_errors.each do |_, data| %>
|
|
223
|
+
<div style="padding: var(--space-2) 0; border-bottom: 1px solid var(--border-primary); font-size: 12px;">
|
|
224
|
+
<div style="display: flex; align-items: center; gap: 4px;">
|
|
225
|
+
<code style="font-size: 10px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100px;"><%= data[:error_type_a] %></code>
|
|
226
|
+
<i class="bi bi-arrow-left-right" style="color: var(--text-tertiary); font-size: 10px;"></i>
|
|
227
|
+
<code style="font-size: 10px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100px;"><%= data[:error_type_b] %></code>
|
|
228
|
+
</div>
|
|
229
|
+
<div style="color: var(--text-tertiary); margin-top: 2px;">
|
|
230
|
+
<strong><%= (data[:correlation] * 100).round(0) %>%</strong> correlation (<%= data[:strength] %>)
|
|
311
231
|
</div>
|
|
312
232
|
</div>
|
|
313
|
-
|
|
233
|
+
<% end %>
|
|
314
234
|
</div>
|
|
315
235
|
<% end %>
|
|
316
236
|
|
|
317
|
-
<!-- Multi-Error Users -->
|
|
318
237
|
<% if @multi_error_users.any? %>
|
|
319
|
-
<div
|
|
320
|
-
<div
|
|
321
|
-
<
|
|
322
|
-
<h6 class="mb-3">
|
|
323
|
-
<i class="bi bi-people-fill me-2"></i>
|
|
324
|
-
Users with Multiple Errors
|
|
325
|
-
</h6>
|
|
326
|
-
<div class="list-group list-group-flush">
|
|
327
|
-
<% @multi_error_users.each do |user| %>
|
|
328
|
-
<div class="list-group-item px-0 py-2">
|
|
329
|
-
<div class="d-flex justify-content-between align-items-start">
|
|
330
|
-
<div class="flex-grow-1">
|
|
331
|
-
<div class="small">
|
|
332
|
-
User ID: <strong><%= user[:user_id] %></strong>
|
|
333
|
-
</div>
|
|
334
|
-
<div class="small text-muted">
|
|
335
|
-
<%= user[:error_types].size %> different error type<%= user[:error_types].size != 1 ? 's' : '' %>
|
|
336
|
-
</div>
|
|
337
|
-
</div>
|
|
338
|
-
<span class="badge bg-danger"><%= user[:total_errors] %></span>
|
|
339
|
-
</div>
|
|
340
|
-
</div>
|
|
341
|
-
<% end %>
|
|
342
|
-
</div>
|
|
343
|
-
</div>
|
|
238
|
+
<div style="padding: var(--space-4); border: 1px solid var(--border-primary); border-radius: var(--radius-md); border-left: 3px solid var(--status-critical);">
|
|
239
|
+
<div style="font-size: 12px; font-weight: 600; color: var(--text-secondary); margin-bottom: var(--space-3);">
|
|
240
|
+
<i class="bi bi-people-fill me-1"></i> Multi-Error Users
|
|
344
241
|
</div>
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
</div>
|
|
350
|
-
<% end %>
|
|
351
|
-
|
|
352
|
-
<!-- Critical Alerts (Last hour) -->
|
|
353
|
-
<% if @critical_alerts.any? %>
|
|
354
|
-
<div class="alert alert-danger border-danger mb-4" role="alert">
|
|
355
|
-
<div class="d-flex align-items-center mb-2">
|
|
356
|
-
<i class="bi bi-exclamation-triangle-fill fs-3 me-3"></i>
|
|
357
|
-
<div>
|
|
358
|
-
<h5 class="alert-heading mb-0">
|
|
359
|
-
🚨 <%= @critical_alerts.count %> Critical/High Error<%= @critical_alerts.count != 1 ? 's' : '' %> in Last Hour
|
|
360
|
-
</h5>
|
|
361
|
-
</div>
|
|
362
|
-
</div>
|
|
363
|
-
<div class="list-group list-group-flush">
|
|
364
|
-
<% @critical_alerts.first(5).each do |error| %>
|
|
365
|
-
<%= link_to error_path(error), class: "list-group-item list-group-item-action" do %>
|
|
366
|
-
<div class="d-flex justify-content-between align-items-start">
|
|
367
|
-
<div class="flex-grow-1">
|
|
368
|
-
<div class="d-flex align-items-center mb-1">
|
|
369
|
-
<span class="badge bg-<%= severity_color(error.severity) %> me-2">
|
|
370
|
-
<%= severity_icon(error.severity) %>
|
|
371
|
-
</span>
|
|
372
|
-
<code class="small"><%= error.error_type %></code>
|
|
242
|
+
<% @multi_error_users.each do |user| %>
|
|
243
|
+
<div style="display: flex; justify-content: space-between; align-items: center; padding: var(--space-2) 0; border-bottom: 1px solid var(--border-primary); font-size: 12px;">
|
|
244
|
+
<span>User <strong><%= user[:user_id] %></strong> · <%= user[:error_types].size %> types</span>
|
|
245
|
+
<span style="font-weight: 600; color: var(--status-critical);"><%= user[:total_errors] %></span>
|
|
373
246
|
</div>
|
|
374
|
-
|
|
375
|
-
<small class="text-muted">
|
|
376
|
-
<i class="bi bi-clock me-1"></i>
|
|
377
|
-
<%= local_time_ago(error.occurred_at) %>
|
|
378
|
-
•
|
|
379
|
-
<i class="bi bi-arrow-repeat me-1"></i>
|
|
380
|
-
<%= error.occurrence_count %> occurrence<%= error.occurrence_count != 1 ? 's' : '' %>
|
|
381
|
-
</small>
|
|
382
|
-
</div>
|
|
383
|
-
<i class="bi bi-chevron-right"></i>
|
|
247
|
+
<% end %>
|
|
384
248
|
</div>
|
|
385
249
|
<% end %>
|
|
386
|
-
<% end %>
|
|
387
|
-
</div>
|
|
388
|
-
<% if @critical_alerts.count > 5 %>
|
|
389
|
-
<div class="mt-2">
|
|
390
|
-
<%= link_to "View all #{@critical_alerts.count} critical errors →",
|
|
391
|
-
errors_path(severity: 'critical'),
|
|
392
|
-
class: "btn btn-sm btn-outline-danger" %>
|
|
393
250
|
</div>
|
|
394
|
-
|
|
251
|
+
</div>
|
|
395
252
|
</div>
|
|
396
253
|
<% end %>
|
|
397
|
-
|
|
398
|
-
<!-- Quick Actions -->
|
|
399
|
-
<div class="row g-3">
|
|
400
|
-
<div class="col-12 col-md-6 col-lg-3">
|
|
401
|
-
<%= link_to errors_path, class: "btn btn-lg btn-outline-primary w-100" do %>
|
|
402
|
-
<i class="bi bi-list-ul d-block fs-3 mb-2"></i>
|
|
403
|
-
View All Errors
|
|
404
|
-
<% end %>
|
|
405
|
-
</div>
|
|
406
|
-
<div class="col-12 col-md-6 col-lg-3">
|
|
407
|
-
<%= link_to analytics_errors_path, class: "btn btn-lg btn-outline-primary w-100" do %>
|
|
408
|
-
<i class="bi bi-graph-up d-block fs-3 mb-2"></i>
|
|
409
|
-
Analytics
|
|
410
|
-
<% end %>
|
|
411
|
-
</div>
|
|
412
|
-
<% if RailsErrorDashboard.configuration.enable_platform_comparison %>
|
|
413
|
-
<div class="col-12 col-md-6 col-lg-3">
|
|
414
|
-
<%= link_to platform_comparison_errors_path, class: "btn btn-lg btn-outline-primary w-100" do %>
|
|
415
|
-
<i class="bi bi-phone d-block fs-3 mb-2"></i>
|
|
416
|
-
Platform Health
|
|
417
|
-
<% end %>
|
|
418
|
-
</div>
|
|
419
|
-
<% end %>
|
|
420
|
-
<% if RailsErrorDashboard.configuration.enable_error_correlation %>
|
|
421
|
-
<div class="col-12 col-md-6 col-lg-3">
|
|
422
|
-
<%= link_to correlation_errors_path, class: "btn btn-lg btn-outline-primary w-100" do %>
|
|
423
|
-
<i class="bi bi-diagram-3 d-block fs-3 mb-2"></i>
|
|
424
|
-
Correlation
|
|
425
|
-
<% end %>
|
|
426
|
-
</div>
|
|
427
|
-
<% end %>
|
|
428
|
-
</div>
|
|
429
254
|
</div>
|
|
255
|
+
|
|
256
|
+
<style>
|
|
257
|
+
.top-error-row:hover { background: var(--surface-hover); }
|
|
258
|
+
</style>
|