rails_error_dashboard 0.5.15 → 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 +1217 -1935
- 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 +292 -620
- 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/configuration.rb +6 -0
- data/lib/rails_error_dashboard/version.rb +1 -1
- metadata +2 -2
|
@@ -2,245 +2,221 @@
|
|
|
2
2
|
|
|
3
3
|
<%= render "show_scripts", error: @error %>
|
|
4
4
|
|
|
5
|
-
<div
|
|
6
|
-
<!--
|
|
7
|
-
<
|
|
8
|
-
|
|
9
|
-
<
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
</
|
|
5
|
+
<div data-controller="loading">
|
|
6
|
+
<!-- Back + breadcrumb -->
|
|
7
|
+
<div style="display: flex; align-items: center; gap: 8px; margin-bottom: var(--space-4); font-size: 13px; color: var(--text-tertiary);">
|
|
8
|
+
<%= link_to errors_path(app_context), style: "background: none; border: none; color: var(--text-tertiary); display: flex; align-items: center; gap: 4px; text-decoration: none; font-size: 13px;" do %>
|
|
9
|
+
<i class="bi bi-arrow-left"></i> Errors
|
|
10
|
+
<% end %>
|
|
11
|
+
<span>/</span>
|
|
12
|
+
<span style="color: var(--text-secondary);"><%= @error.error_type %></span>
|
|
13
|
+
</div>
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
15
|
+
<!-- Hero card -->
|
|
16
|
+
<div style="display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-4); padding: var(--space-6); background: var(--surface-primary); border-radius: var(--radius-md); border: 1px solid var(--border-primary); margin-bottom: var(--space-4);">
|
|
17
|
+
<div style="flex: 1; min-width: 0;">
|
|
18
|
+
<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 6px;">
|
|
19
|
+
<span class="badge bg-<%= severity_color(@error.severity) %>" style="display: inline-flex; align-items: center; gap: 4px;">
|
|
20
|
+
<span style="width: 6px; height: 6px; border-radius: 50%; background: currentColor;"></span>
|
|
21
|
+
<%= @error.severity %>
|
|
22
|
+
</span>
|
|
23
|
+
<%
|
|
24
|
+
raw_status = @error.respond_to?(:status) ? @error.status : nil
|
|
25
|
+
status_text = if @error.respond_to?(:muted?) && @error.muted?
|
|
26
|
+
'muted'
|
|
27
|
+
elsif @error.respond_to?(:snoozed?) && @error.snoozed?
|
|
28
|
+
'snoozed'
|
|
29
|
+
elsif raw_status.present? && raw_status != 'new'
|
|
30
|
+
raw_status.tr('_', ' ')
|
|
31
|
+
elsif @error.resolved?
|
|
32
|
+
'resolved'
|
|
33
|
+
else
|
|
34
|
+
'unresolved'
|
|
35
|
+
end
|
|
36
|
+
status_colors = {
|
|
37
|
+
'unresolved' => { bg: 'var(--status-critical-bg)', color: 'var(--status-critical)' },
|
|
38
|
+
'resolved' => { bg: 'var(--status-success-bg)', color: 'var(--status-success)' },
|
|
39
|
+
'in progress' => { bg: 'var(--status-info-bg)', color: 'var(--status-info)' },
|
|
40
|
+
'investigating' => { bg: 'var(--status-caution-bg)', color: 'var(--status-caution)' },
|
|
41
|
+
'wont fix' => { bg: 'var(--surface-tertiary)', color: 'var(--text-tertiary)' },
|
|
42
|
+
'assigned' => { bg: 'var(--accent-subtle)', color: 'var(--accent)' },
|
|
43
|
+
'snoozed' => { bg: 'var(--status-caution-bg)', color: 'var(--status-caution)' },
|
|
44
|
+
'muted' => { bg: 'var(--surface-tertiary)', color: 'var(--text-tertiary)' },
|
|
45
|
+
}
|
|
46
|
+
sc = status_colors[status_text] || status_colors['unresolved']
|
|
47
|
+
%>
|
|
48
|
+
<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;">
|
|
49
|
+
<%= status_text %>
|
|
50
|
+
</span>
|
|
51
|
+
<% if @error.reopened? %>
|
|
52
|
+
<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: var(--status-warning-bg); color: var(--status-warning);">
|
|
53
|
+
<i class="bi bi-arrow-counterclockwise" style="font-size: 10px;"></i> Reopened
|
|
54
|
+
</span>
|
|
55
|
+
<% end %>
|
|
56
|
+
</div>
|
|
57
|
+
<h2 style="font-size: 20px; font-weight: 700; color: var(--text-primary); font-family: var(--font-mono); margin: 0 0 4px; word-break: break-word;"><%= @error.error_type %></h2>
|
|
58
|
+
<p style="font-size: 14px; color: var(--text-secondary); margin: 0; font-family: var(--font-mono);"><%= @error.message&.truncate(200) %></p>
|
|
59
|
+
<div style="display: flex; gap: var(--space-6); margin-top: var(--space-4); font-size: 12px; color: var(--text-tertiary); flex-wrap: wrap;">
|
|
60
|
+
<span><strong style="color: var(--text-primary);"><%= @error.occurrence_count %></strong> occurrences</span>
|
|
61
|
+
<% if @error.application&.name.present? %>
|
|
62
|
+
<span><i class="bi bi-layers" style="font-size: 10px;"></i> <strong style="color: var(--text-primary);"><%= @error.application.name %></strong></span>
|
|
28
63
|
<% end %>
|
|
29
|
-
|
|
64
|
+
<% if @error.user_id %>
|
|
65
|
+
<span>User <strong style="color: var(--text-primary);">#<%= @error.user_id %></strong></span>
|
|
66
|
+
<% end %>
|
|
67
|
+
<span>First seen <%= local_time_ago(@error.first_seen_at) %></span>
|
|
68
|
+
<span>Last seen <%= local_time_ago(@error.last_seen_at) %></span>
|
|
69
|
+
</div>
|
|
30
70
|
</div>
|
|
31
|
-
<div
|
|
32
|
-
|
|
33
|
-
<
|
|
34
|
-
|
|
35
|
-
<button type="button" class="btn btn-outline-secondary" onclick="copyToClipboard(this.dataset.markdown.replace(/\\(.)/g, function(m,c){return c==='n'?'\n':c}), this)" data-markdown="<%= j @error_markdown %>" title="Copy error details as Markdown for LLM debugging">
|
|
36
|
-
<i class="bi bi-clipboard"></i> Copy for LLM
|
|
37
|
-
</button>
|
|
38
|
-
<% if @error.respond_to?(:muted?) && @error.muted? %>
|
|
39
|
-
<button type="button" class="btn btn-secondary" disabled>
|
|
40
|
-
<i class="bi bi-bell-slash"></i> Muted
|
|
71
|
+
<div style="display: flex; gap: 6px; flex-shrink: 0;">
|
|
72
|
+
<% unless @error.resolved? %>
|
|
73
|
+
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#resolveModal" style="font-weight: 600;">
|
|
74
|
+
Resolve
|
|
41
75
|
</button>
|
|
42
76
|
<% end %>
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
<% end %>
|
|
53
|
-
<% end %>
|
|
77
|
+
<button type="button" class="btn" data-bs-toggle="modal" data-bs-target="#assignModal" id="hero-assign-btn">
|
|
78
|
+
Assign
|
|
79
|
+
</button>
|
|
80
|
+
<button type="button" class="btn" onclick="copyToClipboard(this.dataset.markdown.replace(/\\(.)/g, function(m,c){return c==='n'?'\n':c}), this)" data-markdown="<%= j @error_markdown %>">
|
|
81
|
+
<i class="bi bi-clipboard" style="margin-right: 4px;"></i>Copy for LLM
|
|
82
|
+
</button>
|
|
83
|
+
<button type="button" class="btn" onclick="downloadErrorJSON(event)" title="Export JSON">
|
|
84
|
+
<i class="bi bi-download"></i>
|
|
85
|
+
</button>
|
|
54
86
|
</div>
|
|
55
87
|
</div>
|
|
56
88
|
|
|
57
|
-
<!--
|
|
58
|
-
<div
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
89
|
+
<!-- Main content: tabs + sidebar -->
|
|
90
|
+
<div style="display: grid; grid-template-columns: 1fr 280px; gap: var(--space-4);">
|
|
91
|
+
<!-- Left: tabbed content -->
|
|
92
|
+
<div>
|
|
93
|
+
<!-- Tab bar -->
|
|
94
|
+
<div style="display: flex; gap: 0; border-bottom: 1px solid var(--border-primary); margin-bottom: var(--space-4);">
|
|
95
|
+
<button onclick="switchTab('details')" class="red-tab active" id="tab-details" style="padding: 10px 16px; font-size: 13px; font-weight: 600; color: var(--accent); background: none; border: none; border-bottom: 2px solid var(--accent); margin-bottom: -1px; cursor: pointer;">
|
|
96
|
+
<i class="bi bi-code-slash" style="margin-right: 6px;"></i>Details
|
|
97
|
+
</button>
|
|
98
|
+
<button onclick="switchTab('context')" class="red-tab" id="tab-context" style="padding: 10px 16px; font-size: 13px; font-weight: 400; color: var(--text-secondary); background: none; border: none; border-bottom: 2px solid transparent; margin-bottom: -1px; cursor: pointer;">
|
|
99
|
+
<i class="bi bi-layers" style="margin-right: 6px;"></i>Context
|
|
100
|
+
</button>
|
|
101
|
+
<button onclick="switchTab('history')" class="red-tab" id="tab-history" style="padding: 10px 16px; font-size: 13px; font-weight: 400; color: var(--text-secondary); background: none; border: none; border-bottom: 2px solid transparent; margin-bottom: -1px; cursor: pointer;">
|
|
102
|
+
<i class="bi bi-clock-history" style="margin-right: 6px;"></i>History
|
|
103
|
+
</button>
|
|
104
|
+
<% if RailsErrorDashboard.configuration.enable_issue_tracking %>
|
|
105
|
+
<button onclick="switchTab('issues')" class="red-tab" id="tab-issues" style="padding: 10px 16px; font-size: 13px; font-weight: 400; color: var(--text-secondary); background: none; border: none; border-bottom: 2px solid transparent; margin-bottom: -1px; cursor: pointer;">
|
|
106
|
+
<i class="bi bi-github" style="margin-right: 6px;"></i>Issues
|
|
107
|
+
</button>
|
|
108
|
+
<% end %>
|
|
63
109
|
</div>
|
|
64
|
-
</div>
|
|
65
|
-
</div>
|
|
66
110
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
<%= button_to "Enable Coverage", enable_coverage_errors_path, method: :post, class: "btn btn-sm btn-info" %>
|
|
86
|
-
<% end %>
|
|
111
|
+
<!-- Tab: Details -->
|
|
112
|
+
<div id="panel-details">
|
|
113
|
+
<% if RailsErrorDashboard.configuration.enable_coverage_tracking && RailsErrorDashboard.configuration.enable_source_code_integration %>
|
|
114
|
+
<div class="alert alert-info mb-3" style="display: flex; justify-content: space-between; align-items: center; padding: var(--space-3) var(--space-4);">
|
|
115
|
+
<div style="font-size: 12px;">
|
|
116
|
+
<% if RailsErrorDashboard::Services::CoverageTracker.active? %>
|
|
117
|
+
<i class="bi bi-broadcast"></i> <strong>Coverage Active</strong> — expand source code to see executed lines
|
|
118
|
+
<% else %>
|
|
119
|
+
<i class="bi bi-code-square"></i> <strong>Code Path Coverage</strong> — enable to see executed lines
|
|
120
|
+
<% end %>
|
|
121
|
+
</div>
|
|
122
|
+
<div>
|
|
123
|
+
<% if RailsErrorDashboard::Services::CoverageTracker.active? %>
|
|
124
|
+
<%= button_to "Disable", disable_coverage_errors_path, method: :post, class: "btn btn-sm" %>
|
|
125
|
+
<% else %>
|
|
126
|
+
<%= button_to "Enable", enable_coverage_errors_path, method: :post, class: "btn btn-sm btn-primary" %>
|
|
127
|
+
<% end %>
|
|
128
|
+
</div>
|
|
87
129
|
</div>
|
|
88
|
-
|
|
89
|
-
<% end %>
|
|
90
|
-
|
|
91
|
-
<%= render "error_info", error: @error %>
|
|
92
|
-
|
|
93
|
-
<%= render "local_variables", error: @error %>
|
|
94
|
-
|
|
95
|
-
<%= render "instance_variables", error: @error %>
|
|
96
|
-
|
|
97
|
-
<%= render "request_context", error: @error %>
|
|
98
|
-
|
|
99
|
-
<%= render "breadcrumbs_group", error: @error %>
|
|
100
|
-
|
|
101
|
-
<%= render "similar_errors", error: @error %>
|
|
102
|
-
|
|
103
|
-
<%= render "co_occurring_errors", error: @error %>
|
|
104
|
-
|
|
105
|
-
<!-- Timeline: Related Errors -->
|
|
106
|
-
<%= render "timeline" %>
|
|
130
|
+
<% end %>
|
|
107
131
|
|
|
108
|
-
|
|
132
|
+
<%= render "error_info", error: @error %>
|
|
133
|
+
<%= render "local_variables", error: @error %>
|
|
134
|
+
<%= render "instance_variables", error: @error %>
|
|
135
|
+
</div>
|
|
109
136
|
|
|
110
|
-
|
|
137
|
+
<!-- Tab: Context -->
|
|
138
|
+
<div id="panel-context">
|
|
139
|
+
<%= render "request_context", error: @error %>
|
|
140
|
+
<%= render "breadcrumbs_group", error: @error %>
|
|
141
|
+
</div>
|
|
111
142
|
|
|
112
|
-
|
|
143
|
+
<!-- Tab: History -->
|
|
144
|
+
<div id="panel-history">
|
|
145
|
+
<%= render "timeline" %>
|
|
146
|
+
<% unless RailsErrorDashboard.configuration.enable_issue_tracking %>
|
|
147
|
+
<%= render "discussion", error: @error %>
|
|
148
|
+
<% end %>
|
|
149
|
+
<%= render "similar_errors", error: @error %>
|
|
150
|
+
<%= render "co_occurring_errors", error: @error %>
|
|
151
|
+
<%= render "error_cascades", error: @error %>
|
|
152
|
+
<%= render "pattern_insights" %>
|
|
153
|
+
</div>
|
|
113
154
|
|
|
114
|
-
<!--
|
|
115
|
-
|
|
155
|
+
<!-- Tab: Issues -->
|
|
156
|
+
<% if RailsErrorDashboard.configuration.enable_issue_tracking %>
|
|
157
|
+
<div id="panel-issues">
|
|
158
|
+
<%= render "issue_section", error: @error %>
|
|
159
|
+
<%= render "discussion", error: @error %>
|
|
160
|
+
</div>
|
|
161
|
+
<% end %>
|
|
116
162
|
</div>
|
|
117
163
|
|
|
118
|
-
<!--
|
|
119
|
-
<div class="
|
|
164
|
+
<!-- Right: sidebar -->
|
|
165
|
+
<div class="error-sidebar" style="display: flex; flex-direction: column; gap: var(--space-4); min-width: 0;">
|
|
120
166
|
<%= render "sidebar_metadata", error: @error, related_errors: @related_errors %>
|
|
121
167
|
|
|
122
168
|
<!-- Baseline Statistics -->
|
|
123
169
|
<% if RailsErrorDashboard.configuration.enable_baseline_alerts %>
|
|
124
170
|
<% baselines = @error.baselines %>
|
|
125
171
|
<% if baselines[:hourly].present? || baselines[:daily].present? || baselines[:weekly].present? %>
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
<% if anomaly[:anomaly] %>
|
|
136
|
-
<div class="alert alert-<%= anomaly[:level] == :critical ? 'danger' : anomaly[:level] == :high ? 'warning' : 'info' %> mb-3">
|
|
137
|
-
<i class="bi bi-exclamation-triangle-fill"></i>
|
|
138
|
-
<strong>Anomaly Detected!</strong><br>
|
|
139
|
-
<small>
|
|
140
|
-
This error is <%= anomaly[:std_devs_above]&.round(1) %>σ above baseline
|
|
141
|
-
(<%= anomaly[:level].to_s.upcase %>)
|
|
142
|
-
</small>
|
|
143
|
-
</div>
|
|
144
|
-
<% end %>
|
|
145
|
-
|
|
146
|
-
<% [:hourly, :daily, :weekly].each do |type| %>
|
|
147
|
-
<% baseline = baselines[type] %>
|
|
148
|
-
<% next unless baseline %>
|
|
149
|
-
|
|
150
|
-
<div class="mb-3">
|
|
151
|
-
<small class="text-muted d-block mb-1 text-capitalize">
|
|
152
|
-
<i class="bi bi-<%= type == :hourly ? 'clock' : type == :daily ? 'calendar-day' : 'calendar-week' %>"></i>
|
|
153
|
-
<%= type.to_s.capitalize %> Baseline
|
|
154
|
-
</small>
|
|
155
|
-
|
|
156
|
-
<div class="row g-2">
|
|
157
|
-
<div class="col-6">
|
|
158
|
-
<div class="text-center p-2 bg-light rounded">
|
|
159
|
-
<small class="text-muted d-block">Mean</small>
|
|
160
|
-
<strong><%= baseline.mean&.round(1) || 'N/A' %></strong>
|
|
161
|
-
</div>
|
|
162
|
-
</div>
|
|
163
|
-
<div class="col-6">
|
|
164
|
-
<div class="text-center p-2 bg-light rounded">
|
|
165
|
-
<small class="text-muted d-block">Std Dev</small>
|
|
166
|
-
<strong><%= baseline.std_dev&.round(1) || 'N/A' %></strong>
|
|
167
|
-
</div>
|
|
168
|
-
</div>
|
|
169
|
-
</div>
|
|
170
|
-
|
|
171
|
-
<div class="row g-2 mt-2">
|
|
172
|
-
<div class="col-6">
|
|
173
|
-
<div class="text-center p-2 bg-light rounded">
|
|
174
|
-
<small class="text-muted d-block">95th %</small>
|
|
175
|
-
<strong><%= baseline.percentile_95&.round(1) || 'N/A' %></strong>
|
|
176
|
-
</div>
|
|
177
|
-
</div>
|
|
178
|
-
<div class="col-6">
|
|
179
|
-
<div class="text-center p-2 bg-light rounded">
|
|
180
|
-
<small class="text-muted d-block">99th %</small>
|
|
181
|
-
<strong><%= baseline.percentile_99&.round(1) || 'N/A' %></strong>
|
|
182
|
-
</div>
|
|
183
|
-
</div>
|
|
172
|
+
<div class="card">
|
|
173
|
+
<div style="display: flex; justify-content: space-between; align-items: center; padding: var(--space-4) var(--space-5); border-bottom: 1px solid var(--border-primary);">
|
|
174
|
+
<span style="font-size: 13px; font-weight: 600;">Baseline Statistics</span>
|
|
175
|
+
</div>
|
|
176
|
+
<div style="padding: var(--space-3) var(--space-5);">
|
|
177
|
+
<% anomaly = @error.baseline_anomaly %>
|
|
178
|
+
<% if anomaly[:anomaly] %>
|
|
179
|
+
<div class="alert alert-<%= anomaly[:level] == :critical ? 'danger' : anomaly[:level] == :high ? 'warning' : 'info' %>" style="padding: var(--space-2) var(--space-3); font-size: 12px; margin-bottom: var(--space-3);">
|
|
180
|
+
<strong>Anomaly:</strong> <%= anomaly[:std_devs_above]&.round(1) %>σ above baseline (<%= anomaly[:level].to_s.upcase %>)
|
|
184
181
|
</div>
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
182
|
+
<% end %>
|
|
183
|
+
<% [:hourly, :daily, :weekly].each do |type| %>
|
|
184
|
+
<% baseline = baselines[type] %>
|
|
185
|
+
<% next unless baseline %>
|
|
186
|
+
<div style="padding: var(--space-2) 0; border-bottom: 1px solid var(--border-primary); font-size: 12px;">
|
|
187
|
+
<div style="color: var(--text-tertiary); font-weight: 500; text-transform: capitalize; margin-bottom: 4px;"><%= type %></div>
|
|
188
|
+
<div style="display: flex; gap: var(--space-4);">
|
|
189
|
+
<span>Mean: <strong><%= baseline.mean&.round(1) || 'N/A' %></strong></span>
|
|
190
|
+
<span>σ: <strong><%= baseline.std_dev&.round(1) || 'N/A' %></strong></span>
|
|
191
|
+
<span>P95: <strong><%= baseline.percentile_95&.round(1) || 'N/A' %></strong></span>
|
|
191
192
|
</div>
|
|
192
|
-
<% end %>
|
|
193
|
-
|
|
194
|
-
<div class="mt-2">
|
|
195
|
-
<small class="text-muted">
|
|
196
|
-
Sample: <%= baseline.respond_to?(:sample_size) ? baseline.sample_size : 'N/A' %> periods
|
|
197
|
-
<span class="text-muted">|</span>
|
|
198
|
-
Updated: <%= baseline.respond_to?(:updated_at) ? local_time(baseline.updated_at, format: :short) : 'N/A' %>
|
|
199
|
-
</small>
|
|
200
193
|
</div>
|
|
201
|
-
</div>
|
|
202
|
-
|
|
203
|
-
<% unless baseline == baselines.values.compact.last %>
|
|
204
|
-
<hr>
|
|
205
194
|
<% end %>
|
|
206
|
-
<% end %>
|
|
207
|
-
|
|
208
|
-
<div class="mt-3">
|
|
209
|
-
<small class="text-muted">
|
|
210
|
-
<i class="bi bi-info-circle"></i>
|
|
211
|
-
Baselines establish "normal" error rates. Anomalies are detected when current rates exceed baseline + 2σ.
|
|
212
|
-
</small>
|
|
213
195
|
</div>
|
|
214
196
|
</div>
|
|
215
|
-
</div>
|
|
216
197
|
<% end %>
|
|
217
198
|
<% end %>
|
|
218
199
|
|
|
219
200
|
<!-- Quick Actions -->
|
|
220
201
|
<div class="card">
|
|
221
|
-
<div
|
|
222
|
-
<
|
|
202
|
+
<div style="display: flex; justify-content: space-between; align-items: center; padding: var(--space-4) var(--space-5); border-bottom: 1px solid var(--border-primary);">
|
|
203
|
+
<span style="font-size: 13px; font-weight: 600;">Quick Actions</span>
|
|
223
204
|
</div>
|
|
224
|
-
<div
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
205
|
+
<div style="padding: var(--space-3) var(--space-5); display: flex; flex-direction: column; gap: var(--space-2);">
|
|
206
|
+
<%= link_to errors_path(app_context.merge(error_type: @error.error_type)), class: "btn btn-sm" do %>
|
|
207
|
+
<i class="bi bi-filter"></i> View Similar Errors
|
|
208
|
+
<% end %>
|
|
209
|
+
<% if @error.user_id %>
|
|
210
|
+
<%= link_to errors_path(app_context.merge(user_id: @error.user_id)), class: "btn btn-sm" do %>
|
|
211
|
+
<i class="bi bi-person"></i> View User's Errors
|
|
228
212
|
<% end %>
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
<%= link_to errors_path(platform: @error.platform), class: "btn btn-outline-primary btn-sm" do %>
|
|
237
|
-
<i class="bi bi-phone"></i> View <%= @error.platform %> Errors
|
|
238
|
-
<% end %>
|
|
239
|
-
|
|
240
|
-
<%= link_to analytics_errors_path, class: "btn btn-outline-secondary btn-sm" do %>
|
|
241
|
-
<i class="bi bi-graph-up"></i> View Analytics
|
|
242
|
-
<% end %>
|
|
243
|
-
</div>
|
|
213
|
+
<% end %>
|
|
214
|
+
<%= link_to errors_path(app_context.merge(platform: @error.platform)), class: "btn btn-sm" do %>
|
|
215
|
+
<i class="bi bi-phone"></i> View <%= @error.platform %> Errors
|
|
216
|
+
<% end %>
|
|
217
|
+
<%= link_to analytics_errors_path(app_context), class: "btn btn-sm" do %>
|
|
218
|
+
<i class="bi bi-bar-chart-line"></i> Analytics
|
|
219
|
+
<% end %>
|
|
244
220
|
</div>
|
|
245
221
|
</div>
|
|
246
222
|
</div>
|
|
@@ -248,3 +224,24 @@
|
|
|
248
224
|
</div>
|
|
249
225
|
|
|
250
226
|
<%= render "modals", error: @error %>
|
|
227
|
+
|
|
228
|
+
<script>
|
|
229
|
+
function switchTab(tabId) {
|
|
230
|
+
// Deactivate all tabs
|
|
231
|
+
document.querySelectorAll('.red-tab').forEach(function(el) {
|
|
232
|
+
el.style.color = 'var(--text-secondary)';
|
|
233
|
+
el.style.fontWeight = '400';
|
|
234
|
+
el.style.borderBottomColor = 'transparent';
|
|
235
|
+
});
|
|
236
|
+
// Activate selected tab
|
|
237
|
+
var tab = document.getElementById('tab-' + tabId);
|
|
238
|
+
if (tab) {
|
|
239
|
+
tab.style.color = 'var(--accent)';
|
|
240
|
+
tab.style.fontWeight = '600';
|
|
241
|
+
tab.style.borderBottomColor = 'var(--accent)';
|
|
242
|
+
}
|
|
243
|
+
// Scroll to selected panel
|
|
244
|
+
var panel = document.getElementById('panel-' + tabId);
|
|
245
|
+
if (panel) { panel.scrollIntoView({ behavior: 'smooth', block: 'start' }); }
|
|
246
|
+
}
|
|
247
|
+
</script>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<% content_for :page_title, "Swallowed Exceptions" %>
|
|
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-eye-slash me-2"></i>
|
|
7
7
|
Swallowed Exceptions
|
|
8
8
|
</h1>
|
|
@@ -21,9 +21,9 @@
|
|
|
21
21
|
</div>
|
|
22
22
|
|
|
23
23
|
<% if @unique_count == 0 %>
|
|
24
|
-
<div class="
|
|
25
|
-
<
|
|
26
|
-
<
|
|
24
|
+
<div class="red-empty-state">
|
|
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">No Swallowed Exceptions Detected</div>
|
|
27
27
|
<p class="text-muted">
|
|
28
28
|
No exceptions with a rescue ratio above <%= (RailsErrorDashboard.configuration.swallowed_exception_threshold * 100).round %>% were detected in 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-eye-slash text-danger me-2"></i>
|
|
74
74
|
Swallowed Exception Patterns
|
|
@@ -111,7 +111,7 @@
|
|
|
111
111
|
</table>
|
|
112
112
|
</div>
|
|
113
113
|
</div>
|
|
114
|
-
<div class="card-footer
|
|
114
|
+
<div class="card-footer border-top d-flex justify-content-between align-items-center">
|
|
115
115
|
<div>
|
|
116
116
|
<small class="text-muted">
|
|
117
117
|
<i class="bi bi-lightbulb text-warning"></i> Swallowed exceptions are raised then silently rescued. They may hide real bugs.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<% content_for :page_title, "User Impact" %>
|
|
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-people me-2"></i>
|
|
7
7
|
User Impact
|
|
8
8
|
</h1>
|
|
@@ -21,9 +21,9 @@
|
|
|
21
21
|
</div>
|
|
22
22
|
|
|
23
23
|
<% if @entries.empty? %>
|
|
24
|
-
<div class="
|
|
24
|
+
<div class="red-empty-state">
|
|
25
25
|
<i class="bi bi-people display-1 text-muted mb-3"></i>
|
|
26
|
-
<
|
|
26
|
+
<div class="red-empty-state-title">No User Impact Data</div>
|
|
27
27
|
<p class="text-muted">
|
|
28
28
|
No errors with user attribution were found in the last <%= @days %> days.
|
|
29
29
|
</p>
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
|
|
78
78
|
<!-- User Impact Table -->
|
|
79
79
|
<div class="card mb-4">
|
|
80
|
-
<div class="card-header
|
|
80
|
+
<div class="card-header d-flex justify-content-between align-items-center">
|
|
81
81
|
<h5 class="mb-0">
|
|
82
82
|
<i class="bi bi-bar-chart text-primary me-2"></i>
|
|
83
83
|
Errors Ranked by User Impact
|
|
@@ -157,7 +157,7 @@
|
|
|
157
157
|
</table>
|
|
158
158
|
</div>
|
|
159
159
|
</div>
|
|
160
|
-
<div class="card-footer
|
|
160
|
+
<div class="card-footer border-top d-flex justify-content-between align-items-center">
|
|
161
161
|
<small class="text-muted">
|
|
162
162
|
<i class="bi bi-info-circle"></i>
|
|
163
163
|
Ranked by unique users affected, not occurrence count.
|
|
@@ -184,6 +184,9 @@ module RailsErrorDashboard
|
|
|
184
184
|
# ActiveStorage event tracking (requires enable_breadcrumbs = true)
|
|
185
185
|
attr_accessor :enable_activestorage_tracking # Master switch (default: false)
|
|
186
186
|
|
|
187
|
+
# Dashboard UI appearance
|
|
188
|
+
attr_accessor :accent_color # :crimson (default), :ruby, :ember, :violet
|
|
189
|
+
|
|
187
190
|
# Notification callbacks (managed via helper methods, not set directly)
|
|
188
191
|
attr_reader :notification_callbacks
|
|
189
192
|
|
|
@@ -353,6 +356,9 @@ module RailsErrorDashboard
|
|
|
353
356
|
@enable_internal_logging = false # Opt-in for debugging
|
|
354
357
|
@log_level = :silent # Silent by default, use :debug, :info, :warn, :error, or :silent
|
|
355
358
|
|
|
359
|
+
# Dashboard UI
|
|
360
|
+
@accent_color = :crimson # :crimson, :ruby, :ember, :violet
|
|
361
|
+
|
|
356
362
|
@notification_callbacks = {
|
|
357
363
|
error_logged: [],
|
|
358
364
|
critical_error: [],
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rails_error_dashboard
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Anjan Jagirdar
|
|
@@ -496,7 +496,7 @@ metadata:
|
|
|
496
496
|
funding_uri: https://github.com/sponsors/AnjanJ
|
|
497
497
|
post_install_message: |
|
|
498
498
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
499
|
-
RED (Rails Error Dashboard) v0.
|
|
499
|
+
RED (Rails Error Dashboard) v0.6.0
|
|
500
500
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
501
501
|
|
|
502
502
|
First install:
|