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
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<% deprecation_crumbs = breadcrumbs.select { |c| c["c"] == "deprecation" } %>
|
|
6
6
|
<% if deprecation_crumbs.any? %>
|
|
7
7
|
<div class="card mb-4" id="section-deprecation-warnings" style="border-color: var(--bs-danger);">
|
|
8
|
-
<div class="card-header
|
|
8
|
+
<div class="card-header">
|
|
9
9
|
<h5 class="mb-0">
|
|
10
10
|
<i class="bi bi-exclamation-triangle text-danger"></i> Deprecation Warnings
|
|
11
11
|
<span class="badge bg-danger"><%= deprecation_crumbs.size %></span>
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
<% n_plus_one_patterns = RailsErrorDashboard::Services::NplusOneDetector.call(breadcrumbs) %>
|
|
50
50
|
<% if n_plus_one_patterns.any? %>
|
|
51
51
|
<div class="card mb-4" id="section-n-plus-one" style="border-color: var(--bs-warning);">
|
|
52
|
-
<div class="card-header
|
|
52
|
+
<div class="card-header">
|
|
53
53
|
<h5 class="mb-0">
|
|
54
54
|
<i class="bi bi-arrow-repeat text-warning"></i> N+1 Query Detection
|
|
55
55
|
<span class="badge bg-warning text-dark"><%= n_plus_one_patterns.size %> pattern<%= n_plus_one_patterns.size == 1 ? "" : "s" %></span>
|
|
@@ -108,7 +108,7 @@
|
|
|
108
108
|
<% cache_analysis = RailsErrorDashboard::Services::CacheAnalyzer.call(breadcrumbs) %>
|
|
109
109
|
<% if cache_analysis %>
|
|
110
110
|
<div class="card mb-4" id="section-cache-health" style="border-color: var(--bs-info);">
|
|
111
|
-
<div class="card-header
|
|
111
|
+
<div class="card-header">
|
|
112
112
|
<h5 class="mb-0">
|
|
113
113
|
<i class="bi bi-lightning-charge text-info"></i> Cache Health
|
|
114
114
|
<span class="badge bg-info text-dark"><%= cache_analysis[:reads] + cache_analysis[:writes] %> operations</span>
|
|
@@ -189,7 +189,7 @@
|
|
|
189
189
|
<% end %>
|
|
190
190
|
|
|
191
191
|
<div class="card mb-4" id="section-breadcrumbs">
|
|
192
|
-
<div class="card-header
|
|
192
|
+
<div class="card-header">
|
|
193
193
|
<h5 class="mb-0">
|
|
194
194
|
<i class="bi bi-signpost-2"></i> Breadcrumbs
|
|
195
195
|
<span class="badge bg-secondary"><%= breadcrumbs.size %> events</span>
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<% co_occurring = error.co_occurring_errors(window_minutes: 5, min_frequency: 2, limit: 5) %>
|
|
4
4
|
<% if co_occurring.any? %>
|
|
5
5
|
<div class="card mb-4" id="section-co-occurring">
|
|
6
|
-
<div class="card-header
|
|
6
|
+
<div class="card-header">
|
|
7
7
|
<h5 class="mb-0">
|
|
8
8
|
<i class="bi bi-clock-history"></i> Co-occurring Errors
|
|
9
9
|
<span class="badge bg-info text-dark">Time Patterns</span>
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
<% if has_linked_issue && (platform_comments.any? || has_audit_comments) %>
|
|
7
7
|
<div class="card mb-4" id="section-discussion">
|
|
8
|
-
<div class="card-header
|
|
8
|
+
<div class="card-header d-flex justify-content-between align-items-center">
|
|
9
9
|
<h5 class="mb-0">
|
|
10
10
|
<i class="bi bi-chat-dots"></i> Discussion
|
|
11
11
|
<% if platform_comments.any? %>
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
<% elsif has_linked_issue %>
|
|
63
63
|
<!-- Issue linked but no comments yet -->
|
|
64
64
|
<div class="card mb-4" id="section-discussion">
|
|
65
|
-
<div class="card-header
|
|
65
|
+
<div class="card-header d-flex justify-content-between align-items-center">
|
|
66
66
|
<h5 class="mb-0">
|
|
67
67
|
<i class="bi bi-chat-dots"></i> Discussion
|
|
68
68
|
</h5>
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
<% elsif has_audit_comments %>
|
|
78
78
|
<!-- No issue linked, but has audit trail -->
|
|
79
79
|
<div class="card mb-4" id="section-discussion">
|
|
80
|
-
<div class="card-header
|
|
80
|
+
<div class="card-header">
|
|
81
81
|
<h5 class="mb-0">
|
|
82
82
|
<i class="bi bi-clock-history"></i> Activity Log
|
|
83
83
|
</h5>
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<% cascades = error.error_cascades %>
|
|
4
4
|
<% if cascades[:parents].any? || cascades[:children].any? %>
|
|
5
5
|
<div class="card" id="section-error-cascades">
|
|
6
|
-
<div class="card-header
|
|
6
|
+
<div class="card-header">
|
|
7
7
|
<h5 class="mb-0">
|
|
8
8
|
<i class="bi bi-diagram-3"></i> Error Cascades
|
|
9
9
|
<small class="text-muted ms-2">Causal relationships between errors</small>
|
|
@@ -1,54 +1,83 @@
|
|
|
1
|
-
<tr id="error_<%= error.id %>"
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
<tr id="error_<%= error.id %>"
|
|
2
|
+
onclick="window.location='<%= error_path(error, **app_context) %>'"
|
|
3
|
+
style="border-bottom: 1px solid var(--border-primary); cursor: pointer; transition: background 0.1s ease;"
|
|
4
|
+
onmouseenter="this.style.background='var(--surface-hover)'; this.querySelector('.sev-bar').style.opacity='1';"
|
|
5
|
+
onmouseleave="this.style.background='transparent'; this.querySelector('.sev-bar').style.opacity='0';">
|
|
6
|
+
<td style="padding: var(--space-3) var(--space-4); text-align: center; position: relative;" onclick="event.stopPropagation(); var cb = this.querySelector('input'); cb.checked = !cb.checked; cb.dispatchEvent(new Event('change'));">
|
|
7
|
+
<span class="sev-bar" style="position: absolute; left: 0; top: 0; bottom: 0; width: 3px; background: <%= severity_color_var(error.severity) %>; opacity: 0; transition: opacity 0.1s ease; border-radius: 0 2px 2px 0;"></span>
|
|
8
|
+
<input type="checkbox" class="error-checkbox form-check-input" value="<%= error.id %>" data-error-id="<%= error.id %>" onclick="event.stopPropagation();" style="accent-color: var(--accent);">
|
|
4
9
|
</td>
|
|
5
|
-
<td onclick="window.location='<%= error_path(error) %>'
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
10
|
+
<td style="padding: var(--space-3) var(--space-4);" onclick="window.location='<%= error_path(error, **app_context) %>'">
|
|
11
|
+
<div style="display: flex; align-items: center; gap: 8px;">
|
|
12
|
+
<span class="badge bg-<%= severity_color(error.severity) %>" style="display: inline-flex; align-items: center; gap: 4px;">
|
|
13
|
+
<span style="width: 6px; height: 6px; border-radius: 50%; background: currentColor;"></span>
|
|
14
|
+
<%= error.severity %>
|
|
15
|
+
</span>
|
|
16
|
+
<div style="min-width: 0;">
|
|
17
|
+
<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;">
|
|
18
|
+
<%= error.error_type %>
|
|
19
|
+
<% if error.recent? %>
|
|
20
|
+
<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;">NEW</span>
|
|
21
|
+
<% end %>
|
|
22
|
+
</div>
|
|
23
|
+
<div style="color: var(--text-tertiary); font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 400px;" title="<%= error.message %>"><%= error.message %></div>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
19
26
|
</td>
|
|
20
|
-
<td onclick="window.location='<%= error_path(error) %>'
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
<td style="padding: var(--space-3) var(--space-4);" onclick="window.location='<%= error_path(error, **app_context) %>'">
|
|
28
|
+
<%
|
|
29
|
+
# Use the model's status field if it has a meaningful workflow status
|
|
30
|
+
raw_status = error.respond_to?(:status) ? error.status : nil
|
|
31
|
+
status_text = if error.respond_to?(:muted?) && error.muted?
|
|
32
|
+
'muted'
|
|
33
|
+
elsif error.respond_to?(:snoozed?) && error.snoozed?
|
|
34
|
+
'snoozed'
|
|
35
|
+
elsif raw_status.present? && raw_status != 'new'
|
|
36
|
+
raw_status.tr('_', ' ')
|
|
37
|
+
elsif error.resolved?
|
|
38
|
+
'resolved'
|
|
39
|
+
else
|
|
40
|
+
'unresolved'
|
|
41
|
+
end
|
|
42
|
+
status_colors = {
|
|
43
|
+
'unresolved' => { bg: 'var(--status-critical-bg)', color: 'var(--status-critical)' },
|
|
44
|
+
'resolved' => { bg: 'var(--status-success-bg)', color: 'var(--status-success)' },
|
|
45
|
+
'in progress' => { bg: 'var(--status-info-bg)', color: 'var(--status-info)' },
|
|
46
|
+
'investigating' => { bg: 'var(--status-caution-bg)', color: 'var(--status-caution)' },
|
|
47
|
+
'wont fix' => { bg: 'var(--surface-tertiary)', color: 'var(--text-tertiary)' },
|
|
48
|
+
'assigned' => { bg: 'var(--accent-subtle)', color: 'var(--accent)' },
|
|
49
|
+
'snoozed' => { bg: 'var(--status-caution-bg)', color: 'var(--status-caution)' },
|
|
50
|
+
'muted' => { bg: 'var(--surface-tertiary)', color: 'var(--text-tertiary)' },
|
|
51
|
+
}
|
|
52
|
+
sc = status_colors[status_text] || status_colors['unresolved']
|
|
53
|
+
%>
|
|
54
|
+
<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;">
|
|
55
|
+
<%= status_text %>
|
|
56
|
+
</span>
|
|
57
|
+
<% if error.reopened? %>
|
|
58
|
+
<i class="bi bi-arrow-counterclockwise" style="color: var(--status-warning); margin-left: 4px; font-size: 12px;" title="Reopened"></i>
|
|
27
59
|
<% end %>
|
|
28
60
|
</td>
|
|
29
|
-
<td onclick="window.location='<%= error_path(error) %>'
|
|
30
|
-
|
|
31
|
-
<%= error.message %>
|
|
32
|
-
</div>
|
|
61
|
+
<td style="padding: var(--space-3) var(--space-4); text-align: right; font-weight: 600; font-variant-numeric: tabular-nums; color: var(--text-primary);" onclick="window.location='<%= error_path(error, **app_context) %>'">
|
|
62
|
+
<%= error.occurrence_count.to_s(:delimited) rescue error.occurrence_count %>
|
|
33
63
|
</td>
|
|
34
|
-
<td onclick="window.location='<%= error_path(error) %>'
|
|
35
|
-
|
|
64
|
+
<td style="padding: var(--space-3) var(--space-4); text-align: right; font-variant-numeric: tabular-nums; color: var(--text-secondary);" onclick="window.location='<%= error_path(error, **app_context) %>'">
|
|
65
|
+
<% if error.user_id %>
|
|
66
|
+
<%= error.user_id %>
|
|
67
|
+
<% else %>
|
|
68
|
+
<span style="color: var(--text-tertiary);">—</span>
|
|
69
|
+
<% end %>
|
|
36
70
|
</td>
|
|
37
|
-
<td onclick="window.location='<%= error_path(error) %>'
|
|
38
|
-
|
|
39
|
-
<strong>First:</strong> <%= local_time(error.first_seen_at, format: :short) %><br>
|
|
40
|
-
<strong>Last:</strong> <%= local_time(error.last_seen_at, format: :short) %>
|
|
41
|
-
</small>
|
|
71
|
+
<td style="padding: var(--space-3) var(--space-4); text-align: right; color: var(--text-tertiary); font-size: 12px;" onclick="window.location='<%= error_path(error, **app_context) %>'">
|
|
72
|
+
<%= local_time_ago(error.last_seen_at) %>
|
|
42
73
|
</td>
|
|
43
74
|
<% if local_assigns[:show_application] %>
|
|
44
|
-
<td onclick="window.location='<%= error_path(error) %>'
|
|
45
|
-
<span class="badge bg-info">
|
|
46
|
-
<%= error.application&.name || 'Unknown' %>
|
|
47
|
-
</span>
|
|
75
|
+
<td style="padding: var(--space-3) var(--space-4); font-size: 12px;" onclick="window.location='<%= error_path(error, **app_context) %>'">
|
|
76
|
+
<span class="badge bg-info"><%= error.application&.name || 'Unknown' %></span>
|
|
48
77
|
</td>
|
|
49
78
|
<% end %>
|
|
50
79
|
<% if local_assigns[:show_platform] %>
|
|
51
|
-
<td onclick="window.location='<%= error_path(error) %>'
|
|
80
|
+
<td style="padding: var(--space-3) var(--space-4); font-size: 12px;" onclick="window.location='<%= error_path(error, **app_context) %>'">
|
|
52
81
|
<% if error.platform == 'iOS' %>
|
|
53
82
|
<span class="badge badge-ios"><i class="bi bi-apple"></i> iOS</span>
|
|
54
83
|
<% elsif error.platform == 'Android' %>
|
|
@@ -60,43 +89,4 @@
|
|
|
60
89
|
<% end %>
|
|
61
90
|
</td>
|
|
62
91
|
<% end %>
|
|
63
|
-
<td onclick="window.location='<%= error_path(error) %>';">
|
|
64
|
-
<% if error.user_id %>
|
|
65
|
-
<small data-bs-toggle="tooltip" title="User affected by this error">User #<%= error.user_id %></small>
|
|
66
|
-
<% if error.respond_to?(:user_impact_percentage) %>
|
|
67
|
-
<% impact = error.user_impact_percentage %>
|
|
68
|
-
<% if impact > 0 %>
|
|
69
|
-
<br><small class="badge bg-warning text-dark" data-bs-toggle="tooltip" title="Percentage of users affected by this error type"><%= impact %>% impact</small>
|
|
70
|
-
<% end %>
|
|
71
|
-
<% end %>
|
|
72
|
-
<% else %>
|
|
73
|
-
<small class="text-muted" data-bs-toggle="tooltip" title="No specific user affected">-</small>
|
|
74
|
-
<% end %>
|
|
75
|
-
</td>
|
|
76
|
-
<td onclick="window.location='<%= error_path(error) %>';">
|
|
77
|
-
<% if error.resolved? %>
|
|
78
|
-
<i class="bi bi-check-circle-fill text-success"
|
|
79
|
-
data-bs-toggle="tooltip"
|
|
80
|
-
data-bs-placement="left"
|
|
81
|
-
data-bs-html="true"
|
|
82
|
-
title="<%= if error.resolution_comment.present?
|
|
83
|
-
"<strong>Resolved</strong><br><em>#{error.resolution_comment.truncate(100).gsub('"', '"').gsub("'", ''')}</em>"
|
|
84
|
-
else
|
|
85
|
-
'Resolved'
|
|
86
|
-
end %>"></i>
|
|
87
|
-
<% else %>
|
|
88
|
-
<i class="bi bi-exclamation-circle-fill text-danger" data-bs-toggle="tooltip" title="Unresolved"></i>
|
|
89
|
-
<% end %>
|
|
90
|
-
<% if error.reopened? %>
|
|
91
|
-
<i class="bi bi-arrow-counterclockwise text-warning ms-1" data-bs-toggle="tooltip" title="Reopened"></i>
|
|
92
|
-
<% end %>
|
|
93
|
-
<% if error.respond_to?(:muted?) && error.muted? %>
|
|
94
|
-
<i class="bi bi-bell-slash text-secondary ms-1" data-bs-toggle="tooltip" title="Muted - notifications silenced"></i>
|
|
95
|
-
<% end %>
|
|
96
|
-
</td>
|
|
97
|
-
<td onclick="event.stopPropagation();">
|
|
98
|
-
<%= link_to error_path(error), class: "btn btn-sm btn-outline-primary" do %>
|
|
99
|
-
<i class="bi bi-eye"></i>
|
|
100
|
-
<% end %>
|
|
101
|
-
</td>
|
|
102
92
|
</tr>
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<% self_class = instance_vars.delete("_self_class") %>
|
|
5
5
|
<% if instance_vars.any? %>
|
|
6
6
|
<div class="card mb-4" id="section-instance-variables">
|
|
7
|
-
<div class="card-header
|
|
7
|
+
<div class="card-header">
|
|
8
8
|
<h5 class="mb-0">
|
|
9
9
|
<i class="bi bi-box"></i> Instance Variables
|
|
10
10
|
<span class="badge bg-secondary"><%= instance_vars.size %> captured</span>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<% if RailsErrorDashboard.configuration.enable_issue_tracking %>
|
|
2
2
|
<div class="card mb-4" id="issue-tracking">
|
|
3
|
-
<div class="card-header
|
|
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
|
Issue Tracker
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<% local_vars = JSON.parse(error.local_variables) rescue {} %>
|
|
4
4
|
<% if local_vars.any? %>
|
|
5
5
|
<div class="card mb-4" id="section-local-variables">
|
|
6
|
-
<div class="card-header
|
|
6
|
+
<div class="card-header">
|
|
7
7
|
<h5 class="mb-0">
|
|
8
8
|
<i class="bi bi-braces"></i> Local Variables
|
|
9
9
|
<span class="badge bg-secondary"><%= local_vars.size %> captured</span>
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
<% if pattern_data.present? && pattern_data[:total_errors] > 0 %>
|
|
9
9
|
<div class="card mb-4" id="section-occurrence-patterns">
|
|
10
|
-
<div class="card-header
|
|
10
|
+
<div class="card-header">
|
|
11
11
|
<h5 class="mb-0">
|
|
12
12
|
<i class="bi bi-graph-up"></i> Occurrence Patterns
|
|
13
13
|
<small class="text-muted">(Last 30 days)</small>
|
|
@@ -128,7 +128,7 @@
|
|
|
128
128
|
<!-- Error Bursts -->
|
|
129
129
|
<% if bursts.present? && bursts.any? %>
|
|
130
130
|
<div class="card mb-4">
|
|
131
|
-
<div class="card-header
|
|
131
|
+
<div class="card-header">
|
|
132
132
|
<h5 class="mb-0">
|
|
133
133
|
<i class="bi bi-lightning-charge-fill text-warning"></i> Error Bursts
|
|
134
134
|
<span class="badge bg-warning text-dark"><%= bursts.count %></span>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<!-- Request Context -->
|
|
2
2
|
<% cache [error, 'request_context_v3'] do %>
|
|
3
3
|
<div class="card mb-4" id="section-request-context">
|
|
4
|
-
<div class="card-header
|
|
4
|
+
<div class="card-header">
|
|
5
5
|
<h5 class="mb-0"><i class="bi bi-globe"></i> Request Context</h5>
|
|
6
6
|
</div>
|
|
7
7
|
<div class="card-body">
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<% if similar.any? %>
|
|
5
5
|
<% cache [error, 'similar_errors_v1', similar.map { |s| s[:error]&.updated_at }.compact.max] do %>
|
|
6
6
|
<div class="card mb-4" id="section-similar-errors">
|
|
7
|
-
<div class="card-header
|
|
7
|
+
<div class="card-header">
|
|
8
8
|
<h5 class="mb-0">
|
|
9
9
|
<i class="bi bi-diagram-3"></i> Similar Errors
|
|
10
10
|
<span class="badge bg-info text-dark">Fuzzy Matching</span>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<%# Timeline view for related errors leading up to this error %>
|
|
2
2
|
<% if @related_errors.any? %>
|
|
3
3
|
<div class="card" id="section-timeline">
|
|
4
|
-
<div class="card-header
|
|
4
|
+
<div class="card-header">
|
|
5
5
|
<h5 class="mb-0">
|
|
6
6
|
<i class="bi bi-clock-history"></i> Timeline
|
|
7
7
|
<small class="text-muted ms-2">Recent related errors</small>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<% content_for :page_title, "ActionCable Health" %>
|
|
2
2
|
|
|
3
|
-
<div
|
|
3
|
+
<div>
|
|
4
4
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
5
|
-
<h1
|
|
5
|
+
<h1 style="font-size: 20px; font-weight: 700; margin: 0;">
|
|
6
6
|
<i class="bi bi-broadcast me-2"></i>
|
|
7
7
|
ActionCable Health
|
|
8
8
|
</h1>
|
|
@@ -21,9 +21,9 @@
|
|
|
21
21
|
</div>
|
|
22
22
|
|
|
23
23
|
<% if @unique_channels == 0 %>
|
|
24
|
-
<div class="
|
|
24
|
+
<div class="red-empty-state">
|
|
25
25
|
<i class="bi bi-broadcast display-1 text-success mb-3"></i>
|
|
26
|
-
<
|
|
26
|
+
<div class="red-empty-state-title">No ActionCable Events Found</div>
|
|
27
27
|
<p class="text-muted">
|
|
28
28
|
No ActionCable channel actions, transmissions, or subscription events were detected in error breadcrumbs over the last <%= @days %> days.
|
|
29
29
|
</p>
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
</div>
|
|
69
69
|
|
|
70
70
|
<div class="card mb-4">
|
|
71
|
-
<div class="card-header
|
|
71
|
+
<div class="card-header d-flex justify-content-between align-items-center">
|
|
72
72
|
<h5 class="mb-0">
|
|
73
73
|
<i class="bi bi-broadcast text-primary me-2"></i>
|
|
74
74
|
ActionCable Events by Channel
|
|
@@ -112,7 +112,7 @@
|
|
|
112
112
|
</table>
|
|
113
113
|
</div>
|
|
114
114
|
</div>
|
|
115
|
-
<div class="card-footer
|
|
115
|
+
<div class="card-footer border-top d-flex justify-content-between align-items-center">
|
|
116
116
|
<div>
|
|
117
117
|
<small class="text-muted">
|
|
118
118
|
<i class="bi bi-lightbulb text-warning"></i> ActionCable events are captured when they coincide with errors. High rejection counts may indicate authentication or authorization issues.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<% content_for :page_title, "ActiveStorage Health" %>
|
|
2
2
|
|
|
3
|
-
<div
|
|
3
|
+
<div>
|
|
4
4
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
5
|
-
<h1
|
|
5
|
+
<h1 style="font-size: 20px; font-weight: 700; margin: 0;">
|
|
6
6
|
<i class="bi bi-cloud-arrow-up me-2"></i>
|
|
7
7
|
ActiveStorage Health
|
|
8
8
|
</h1>
|
|
@@ -21,9 +21,9 @@
|
|
|
21
21
|
</div>
|
|
22
22
|
|
|
23
23
|
<% if @unique_services == 0 %>
|
|
24
|
-
<div class="
|
|
24
|
+
<div class="red-empty-state">
|
|
25
25
|
<i class="bi bi-cloud-arrow-up display-1 text-success mb-3"></i>
|
|
26
|
-
<
|
|
26
|
+
<div class="red-empty-state-title">No ActiveStorage Events Found</div>
|
|
27
27
|
<p class="text-muted">
|
|
28
28
|
No ActiveStorage service operations were detected in error breadcrumbs over the last <%= @days %> days.
|
|
29
29
|
</p>
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
</div>
|
|
69
69
|
|
|
70
70
|
<div class="card mb-4">
|
|
71
|
-
<div class="card-header
|
|
71
|
+
<div class="card-header d-flex justify-content-between align-items-center">
|
|
72
72
|
<h5 class="mb-0">
|
|
73
73
|
<i class="bi bi-cloud-arrow-up text-primary me-2"></i>
|
|
74
74
|
Storage Operations by Service
|
|
@@ -128,7 +128,7 @@
|
|
|
128
128
|
</table>
|
|
129
129
|
</div>
|
|
130
130
|
</div>
|
|
131
|
-
<div class="card-footer
|
|
131
|
+
<div class="card-footer border-top d-flex justify-content-between align-items-center">
|
|
132
132
|
<div>
|
|
133
133
|
<small class="text-muted">
|
|
134
134
|
<i class="bi bi-lightbulb text-warning"></i> Slow storage operations may indicate network issues, large file sizes, or service provider throttling.
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<script>
|
|
4
4
|
// Dynamic chart colors based on theme
|
|
5
5
|
window.getChartColors = function() {
|
|
6
|
-
const isDark = document.
|
|
6
|
+
const isDark = document.documentElement.getAttribute('data-theme') === 'dark';
|
|
7
7
|
return {
|
|
8
8
|
textColor: isDark ? '#cdd6f4' : '#1f2937',
|
|
9
9
|
gridColor: isDark ? 'rgba(88, 91, 112, 0.2)' : 'rgba(0, 0, 0, 0.1)'
|
|
@@ -32,53 +32,37 @@
|
|
|
32
32
|
};
|
|
33
33
|
</script>
|
|
34
34
|
|
|
35
|
-
<div
|
|
36
|
-
<div
|
|
37
|
-
<
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
['Last 90 Days', 90]
|
|
46
|
-
], @days), class: "form-select", onchange: "this.form.submit()" %>
|
|
47
|
-
<% end %>
|
|
48
|
-
</div>
|
|
35
|
+
<div>
|
|
36
|
+
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: var(--space-4);">
|
|
37
|
+
<h1 style="font-size: 20px; font-weight: 700; margin: 0;">Analytics</h1>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<!-- Time range pills -->
|
|
41
|
+
<div style="display: flex; gap: 6px; margin-bottom: var(--space-6);">
|
|
42
|
+
<% [['7d', 7], ['14d', 14], ['30d', 30], ['60d', 60], ['90d', 90]].each do |label, days| %>
|
|
43
|
+
<%= link_to analytics_errors_path(days: days), class: "filter-pill #{@days == days ? 'active' : ''}", style: "text-decoration: none;" do %><%= label %><% end %>
|
|
44
|
+
<% end %>
|
|
49
45
|
</div>
|
|
50
46
|
|
|
51
47
|
<!-- Summary Stats -->
|
|
52
|
-
<div
|
|
53
|
-
<div class="
|
|
54
|
-
<div class="
|
|
55
|
-
|
|
56
|
-
<div class="stat-label mb-2 text-primary">Total Errors</div>
|
|
57
|
-
<div class="stat-value text-primary"><%= @error_stats[:total] %></div>
|
|
58
|
-
</div>
|
|
59
|
-
</div>
|
|
48
|
+
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-4); margin-bottom: var(--space-6);">
|
|
49
|
+
<div class="card stat-card" style="border-left: 3px solid var(--accent); padding: var(--space-5) var(--space-6);">
|
|
50
|
+
<div class="stat-label" style="margin-bottom: 4px;">Total Errors</div>
|
|
51
|
+
<span class="stat-value"><%= @error_stats[:total] %></span>
|
|
60
52
|
</div>
|
|
61
|
-
<div class="
|
|
62
|
-
<div class="
|
|
63
|
-
|
|
64
|
-
<div class="stat-label mb-2 text-danger">Unresolved</div>
|
|
65
|
-
<div class="stat-value text-danger"><%= @error_stats[:unresolved] %></div>
|
|
66
|
-
</div>
|
|
67
|
-
</div>
|
|
53
|
+
<div class="card stat-card" style="border-left: 3px solid var(--status-critical); padding: var(--space-5) var(--space-6);">
|
|
54
|
+
<div class="stat-label" style="margin-bottom: 4px;">Unresolved</div>
|
|
55
|
+
<span class="stat-value" style="color: var(--status-critical);"><%= @error_stats[:unresolved] %></span>
|
|
68
56
|
</div>
|
|
69
|
-
<div class="
|
|
70
|
-
<div class="
|
|
71
|
-
|
|
72
|
-
<div class="stat-label mb-2 text-success">Resolution Rate</div>
|
|
73
|
-
<div class="stat-value text-success"><%= @resolution_rate %>%</div>
|
|
74
|
-
</div>
|
|
75
|
-
</div>
|
|
57
|
+
<div class="card stat-card" style="border-left: 3px solid var(--status-success); padding: var(--space-5) var(--space-6);">
|
|
58
|
+
<div class="stat-label" style="margin-bottom: 4px;">Resolution Rate</div>
|
|
59
|
+
<span class="stat-value" style="color: var(--status-success);"><%= @resolution_rate %>%</span>
|
|
76
60
|
</div>
|
|
77
61
|
</div>
|
|
78
62
|
|
|
79
63
|
<!-- Error Trend Chart -->
|
|
80
64
|
<div class="card mb-4">
|
|
81
|
-
<div class="card-header
|
|
65
|
+
<div class="card-header">
|
|
82
66
|
<h5 class="mb-0"><i class="bi bi-graph-up"></i> Error Trend (Last <%= @days %> Days)</h5>
|
|
83
67
|
</div>
|
|
84
68
|
<div class="card-body">
|
|
@@ -87,7 +71,7 @@
|
|
|
87
71
|
document.addEventListener('DOMContentLoaded', function() {
|
|
88
72
|
const colors = window.getChartColors();
|
|
89
73
|
new Chartkick.LineChart("errors-over-time-chart", <%= raw @errors_over_time.to_json %>, {
|
|
90
|
-
color: "#
|
|
74
|
+
color: "#DC2626",
|
|
91
75
|
curve: false,
|
|
92
76
|
points: true,
|
|
93
77
|
height: "300px",
|
|
@@ -123,7 +107,7 @@
|
|
|
123
107
|
<div class="row g-4 mb-4">
|
|
124
108
|
<div class="col-md-12">
|
|
125
109
|
<div class="card">
|
|
126
|
-
<div class="card-header
|
|
110
|
+
<div class="card-header">
|
|
127
111
|
<h5 class="mb-0"><i class="bi bi-phone"></i> Errors by Platform</h5>
|
|
128
112
|
</div>
|
|
129
113
|
<div class="card-body">
|
|
@@ -160,7 +144,7 @@
|
|
|
160
144
|
});
|
|
161
145
|
</script>
|
|
162
146
|
</div>
|
|
163
|
-
<div class="card-footer
|
|
147
|
+
<div class="card-footer border-top-0">
|
|
164
148
|
<div class="d-flex gap-2 flex-wrap">
|
|
165
149
|
<small class="text-muted me-2">Quick Links:</small>
|
|
166
150
|
<% @errors_by_platform.keys.each do |platform| %>
|
|
@@ -177,7 +161,7 @@
|
|
|
177
161
|
<div class="row g-4 mb-4">
|
|
178
162
|
<div class="col-md-12">
|
|
179
163
|
<div class="card">
|
|
180
|
-
<div class="card-header
|
|
164
|
+
<div class="card-header">
|
|
181
165
|
<h5 class="mb-0"><i class="bi bi-bug"></i> Top 10 Error Types</h5>
|
|
182
166
|
</div>
|
|
183
167
|
<div class="card-body">
|
|
@@ -219,7 +203,7 @@
|
|
|
219
203
|
<div class="row g-4 mb-4">
|
|
220
204
|
<div class="col-md-12">
|
|
221
205
|
<div class="card">
|
|
222
|
-
<div class="card-header
|
|
206
|
+
<div class="card-header">
|
|
223
207
|
<h5 class="mb-0"><i class="bi bi-clock"></i> Errors by Hour of Day</h5>
|
|
224
208
|
</div>
|
|
225
209
|
<div class="card-body">
|
|
@@ -228,7 +212,7 @@
|
|
|
228
212
|
document.addEventListener('DOMContentLoaded', function() {
|
|
229
213
|
const colors = window.getChartColors();
|
|
230
214
|
new Chartkick.ColumnChart("errors-by-hour-chart", <%= raw @errors_by_hour.to_json %>, {
|
|
231
|
-
color: "#
|
|
215
|
+
color: "#DC2626",
|
|
232
216
|
height: "300px",
|
|
233
217
|
xtitle: "Hour",
|
|
234
218
|
ytitle: "Number of Errors",
|
|
@@ -262,7 +246,7 @@
|
|
|
262
246
|
<div class="row g-4 mb-4">
|
|
263
247
|
<div class="col-md-12">
|
|
264
248
|
<div class="card">
|
|
265
|
-
<div class="card-header
|
|
249
|
+
<div class="card-header">
|
|
266
250
|
<h5 class="mb-0"><i class="bi bi-people"></i> Top 10 Affected Users</h5>
|
|
267
251
|
</div>
|
|
268
252
|
<div class="card-body p-0">
|
|
@@ -285,7 +269,7 @@
|
|
|
285
269
|
<div class="row g-4">
|
|
286
270
|
<div class="col-md-12">
|
|
287
271
|
<div class="card">
|
|
288
|
-
<div class="card-header
|
|
272
|
+
<div class="card-header d-flex justify-content-between align-items-center">
|
|
289
273
|
<h5 class="mb-0"><i class="bi bi-list-check"></i> Error Type Breakdown</h5>
|
|
290
274
|
<small class="text-muted">Last <%= @days %> days</small>
|
|
291
275
|
</div>
|
|
@@ -334,7 +318,7 @@
|
|
|
334
318
|
|
|
335
319
|
<!-- Recurring Issues Analysis -->
|
|
336
320
|
<div class="card mb-4">
|
|
337
|
-
<div class="card-header
|
|
321
|
+
<div class="card-header">
|
|
338
322
|
<h5 class="mb-0"><i class="bi bi-arrow-repeat"></i> Recurring Issues</h5>
|
|
339
323
|
</div>
|
|
340
324
|
<div class="card-body">
|
|
@@ -421,7 +405,7 @@
|
|
|
421
405
|
<!-- Errors by Version Chart -->
|
|
422
406
|
<div class="col-md-8">
|
|
423
407
|
<div class="card">
|
|
424
|
-
<div class="card-header
|
|
408
|
+
<div class="card-header">
|
|
425
409
|
<h5 class="mb-0"><i class="bi bi-git"></i> Errors by Release Version</h5>
|
|
426
410
|
</div>
|
|
427
411
|
<div class="card-body">
|
|
@@ -447,7 +431,7 @@
|
|
|
447
431
|
<!-- Release Comparison Card -->
|
|
448
432
|
<div class="col-md-4">
|
|
449
433
|
<div class="card">
|
|
450
|
-
<div class="card-header
|
|
434
|
+
<div class="card-header">
|
|
451
435
|
<h5 class="mb-0">Latest vs Previous</h5>
|
|
452
436
|
</div>
|
|
453
437
|
<div class="card-body">
|
|
@@ -510,7 +494,7 @@
|
|
|
510
494
|
<!-- MTTR (Mean Time to Resolution) -->
|
|
511
495
|
<% if @mttr_stats[:total_resolved] > 0 %>
|
|
512
496
|
<div class="card mb-4">
|
|
513
|
-
<div class="card-header
|
|
497
|
+
<div class="card-header">
|
|
514
498
|
<h5 class="mb-0"><i class="bi bi-clock-history"></i> Resolution Performance (MTTR)</h5>
|
|
515
499
|
</div>
|
|
516
500
|
<div class="card-body">
|