rails_error_dashboard 0.1.28 → 0.1.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +50 -6
- data/app/controllers/rails_error_dashboard/errors_controller.rb +22 -0
- data/app/helpers/rails_error_dashboard/application_helper.rb +79 -7
- data/app/helpers/rails_error_dashboard/backtrace_helper.rb +149 -0
- data/app/models/rails_error_dashboard/application.rb +1 -1
- data/app/models/rails_error_dashboard/error_log.rb +44 -16
- data/app/views/layouts/rails_error_dashboard.html.erb +71 -1237
- data/app/views/rails_error_dashboard/errors/_error_row.html.erb +10 -2
- data/app/views/rails_error_dashboard/errors/_source_code.html.erb +76 -0
- data/app/views/rails_error_dashboard/errors/_timeline.html.erb +18 -82
- data/app/views/rails_error_dashboard/errors/_user_errors_table.html.erb +70 -0
- data/app/views/rails_error_dashboard/errors/analytics.html.erb +9 -37
- data/app/views/rails_error_dashboard/errors/correlation.html.erb +11 -37
- data/app/views/rails_error_dashboard/errors/index.html.erb +64 -31
- data/app/views/rails_error_dashboard/errors/overview.html.erb +181 -3
- data/app/views/rails_error_dashboard/errors/platform_comparison.html.erb +2 -1
- data/app/views/rails_error_dashboard/errors/settings/_value_badge.html.erb +286 -0
- data/app/views/rails_error_dashboard/errors/settings.html.erb +146 -480
- data/app/views/rails_error_dashboard/errors/show.html.erb +102 -76
- data/db/migrate/20251223000000_create_rails_error_dashboard_complete_schema.rb +188 -0
- data/db/migrate/20251224000001_create_rails_error_dashboard_error_logs.rb +5 -0
- data/db/migrate/20251224081522_add_better_tracking_to_error_logs.rb +3 -0
- data/db/migrate/20251224101217_add_controller_action_to_error_logs.rb +3 -0
- data/db/migrate/20251225071314_add_optimized_indexes_to_error_logs.rb +4 -0
- data/db/migrate/20251225074653_remove_environment_from_error_logs.rb +3 -0
- data/db/migrate/20251225085859_add_enhanced_metrics_to_error_logs.rb +3 -0
- data/db/migrate/20251225093603_add_similarity_tracking_to_error_logs.rb +3 -0
- data/db/migrate/20251225100236_create_error_occurrences.rb +3 -0
- data/db/migrate/20251225101920_create_cascade_patterns.rb +3 -0
- data/db/migrate/20251225102500_create_error_baselines.rb +3 -0
- data/db/migrate/20251226020000_add_workflow_fields_to_error_logs.rb +3 -0
- data/db/migrate/20251226020100_create_error_comments.rb +3 -0
- data/db/migrate/20251229111223_add_additional_performance_indexes.rb +4 -0
- data/db/migrate/20260106094220_create_rails_error_dashboard_applications.rb +3 -0
- data/db/migrate/20260106094233_add_application_to_error_logs.rb +3 -0
- data/db/migrate/20260106094318_finalize_application_foreign_key.rb +5 -0
- data/lib/generators/rails_error_dashboard/install/install_generator.rb +32 -0
- data/lib/generators/rails_error_dashboard/install/templates/initializer.rb +37 -4
- data/lib/rails_error_dashboard/configuration.rb +160 -3
- data/lib/rails_error_dashboard/configuration_error.rb +24 -0
- data/lib/rails_error_dashboard/engine.rb +17 -0
- data/lib/rails_error_dashboard/helpers/user_model_detector.rb +138 -0
- data/lib/rails_error_dashboard/queries/analytics_stats.rb +1 -2
- data/lib/rails_error_dashboard/queries/dashboard_stats.rb +19 -4
- data/lib/rails_error_dashboard/queries/errors_list.rb +27 -8
- data/lib/rails_error_dashboard/services/error_normalizer.rb +143 -0
- data/lib/rails_error_dashboard/services/git_blame_reader.rb +195 -0
- data/lib/rails_error_dashboard/services/github_link_generator.rb +159 -0
- data/lib/rails_error_dashboard/services/source_code_reader.rb +214 -0
- data/lib/rails_error_dashboard/version.rb +1 -1
- data/lib/rails_error_dashboard.rb +6 -0
- metadata +14 -10
- data/app/assets/stylesheets/rails_error_dashboard/_catppuccin_mocha.scss +0 -107
- data/app/assets/stylesheets/rails_error_dashboard/_components.scss +0 -625
- data/app/assets/stylesheets/rails_error_dashboard/_layout.scss +0 -257
- data/app/assets/stylesheets/rails_error_dashboard/_theme_variables.scss +0 -203
- data/app/assets/stylesheets/rails_error_dashboard/application.css +0 -15
- data/app/assets/stylesheets/rails_error_dashboard/application.css.map +0 -7
- data/app/assets/stylesheets/rails_error_dashboard/application.scss +0 -61
- data/app/views/layouts/rails_error_dashboard/application.html.erb +0 -55
|
@@ -1,5 +1,59 @@
|
|
|
1
1
|
<% content_for :page_title, "Error ##{@error.id}" %>
|
|
2
2
|
|
|
3
|
+
<script>
|
|
4
|
+
function downloadErrorJSON(event) {
|
|
5
|
+
const errorData = {
|
|
6
|
+
id: <%= raw @error.id.to_json %>,
|
|
7
|
+
error_type: <%= raw @error.error_type.to_json %>,
|
|
8
|
+
message: <%= raw @error.message.to_json %>,
|
|
9
|
+
backtrace: <%= raw @error.backtrace.to_json %>,
|
|
10
|
+
occurred_at: <%= raw @error.occurred_at.to_json %>,
|
|
11
|
+
first_seen_at: <%= raw @error.first_seen_at.to_json %>,
|
|
12
|
+
last_seen_at: <%= raw @error.last_seen_at.to_json %>,
|
|
13
|
+
occurrence_count: <%= raw @error.occurrence_count.to_json %>,
|
|
14
|
+
resolved: <%= raw @error.resolved?.to_json %>,
|
|
15
|
+
resolved_at: <%= raw @error.resolved_at.to_json %>,
|
|
16
|
+
resolved_by_name: <%= raw @error.resolved_by_name.to_json %>,
|
|
17
|
+
platform: <%= raw @error.platform.to_json %>,
|
|
18
|
+
user_id: <%= raw @error.user_id.to_json %>,
|
|
19
|
+
severity: <%= raw @error.severity.to_json %>,
|
|
20
|
+
priority_level: <%= raw (@error.priority_level || 0).to_json %>,
|
|
21
|
+
<% if @error.respond_to?(:app_version) %>
|
|
22
|
+
app_version: <%= raw @error.app_version.to_json %>,
|
|
23
|
+
<% end %>
|
|
24
|
+
<% if @error.respond_to?(:git_commit) %>
|
|
25
|
+
git_commit: <%= raw @error.git_commit.to_json %>,
|
|
26
|
+
<% end %>
|
|
27
|
+
created_at: <%= raw @error.created_at.to_json %>,
|
|
28
|
+
updated_at: <%= raw @error.updated_at.to_json %>
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const jsonString = JSON.stringify(errorData, null, 2);
|
|
32
|
+
const blob = new Blob([jsonString], { type: 'application/json' });
|
|
33
|
+
const url = URL.createObjectURL(blob);
|
|
34
|
+
const link = document.createElement('a');
|
|
35
|
+
link.href = url;
|
|
36
|
+
link.download = `error_${errorData.id}_${errorData.error_type.replace(/[^a-zA-Z0-9]/g, '_')}.json`;
|
|
37
|
+
document.body.appendChild(link);
|
|
38
|
+
link.click();
|
|
39
|
+
document.body.removeChild(link);
|
|
40
|
+
URL.revokeObjectURL(url);
|
|
41
|
+
|
|
42
|
+
// Visual feedback
|
|
43
|
+
const button = event.currentTarget;
|
|
44
|
+
const originalHTML = button.innerHTML;
|
|
45
|
+
button.innerHTML = '<i class="bi bi-check"></i> Downloaded!';
|
|
46
|
+
button.classList.remove('btn-outline-secondary');
|
|
47
|
+
button.classList.add('btn-success');
|
|
48
|
+
|
|
49
|
+
setTimeout(() => {
|
|
50
|
+
button.innerHTML = originalHTML;
|
|
51
|
+
button.classList.remove('btn-success');
|
|
52
|
+
button.classList.add('btn-outline-secondary');
|
|
53
|
+
}, 2000);
|
|
54
|
+
}
|
|
55
|
+
</script>
|
|
56
|
+
|
|
3
57
|
<div class="py-4">
|
|
4
58
|
<!-- Breadcrumbs -->
|
|
5
59
|
<nav aria-label="breadcrumb" class="mb-3">
|
|
@@ -27,7 +81,7 @@
|
|
|
27
81
|
</h2>
|
|
28
82
|
</div>
|
|
29
83
|
<div class="d-flex gap-2">
|
|
30
|
-
<button type="button" class="btn btn-outline-secondary" onclick="downloadErrorJSON()" title="Download error details as JSON">
|
|
84
|
+
<button type="button" class="btn btn-outline-secondary" onclick="downloadErrorJSON(event)" title="Download error details as JSON">
|
|
31
85
|
<i class="bi bi-download"></i> Export JSON
|
|
32
86
|
</button>
|
|
33
87
|
<% if @error.resolved? %>
|
|
@@ -42,60 +96,6 @@
|
|
|
42
96
|
</div>
|
|
43
97
|
</div>
|
|
44
98
|
|
|
45
|
-
<script>
|
|
46
|
-
function downloadErrorJSON() {
|
|
47
|
-
const errorData = {
|
|
48
|
-
id: <%= @error.id %>,
|
|
49
|
-
error_type: <%= json_escape @error.error_type.to_json %>,
|
|
50
|
-
message: <%= json_escape @error.message.to_json %>,
|
|
51
|
-
backtrace: <%= json_escape @error.backtrace.to_json %>,
|
|
52
|
-
occurred_at: <%= json_escape @error.occurred_at.to_json %>,
|
|
53
|
-
first_seen_at: <%= json_escape @error.first_seen_at.to_json %>,
|
|
54
|
-
last_seen_at: <%= json_escape @error.last_seen_at.to_json %>,
|
|
55
|
-
occurrence_count: <%= @error.occurrence_count %>,
|
|
56
|
-
resolved: <%= @error.resolved? %>,
|
|
57
|
-
resolved_at: <%= json_escape @error.resolved_at.to_json %>,
|
|
58
|
-
resolved_by_name: <%= json_escape @error.resolved_by_name.to_json %>,
|
|
59
|
-
platform: <%= json_escape @error.platform.to_json %>,
|
|
60
|
-
user_id: <%= json_escape @error.user_id.to_json %>,
|
|
61
|
-
severity: <%= json_escape @error.severity.to_json %>,
|
|
62
|
-
priority_level: <%= @error.priority_level || 0 %>,
|
|
63
|
-
<% if @error.respond_to?(:app_version) %>
|
|
64
|
-
app_version: <%= json_escape @error.app_version.to_json %>,
|
|
65
|
-
<% end %>
|
|
66
|
-
<% if @error.respond_to?(:git_commit) %>
|
|
67
|
-
git_commit: <%= json_escape @error.git_commit.to_json %>,
|
|
68
|
-
<% end %>
|
|
69
|
-
created_at: <%= json_escape @error.created_at.to_json %>,
|
|
70
|
-
updated_at: <%= json_escape @error.updated_at.to_json %>
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
const jsonString = JSON.stringify(errorData, null, 2);
|
|
74
|
-
const blob = new Blob([jsonString], { type: 'application/json' });
|
|
75
|
-
const url = URL.createObjectURL(blob);
|
|
76
|
-
const link = document.createElement('a');
|
|
77
|
-
link.href = url;
|
|
78
|
-
link.download = `error_${errorData.id}_${errorData.error_type.replace(/[^a-zA-Z0-9]/g, '_')}.json`;
|
|
79
|
-
document.body.appendChild(link);
|
|
80
|
-
link.click();
|
|
81
|
-
document.body.removeChild(link);
|
|
82
|
-
URL.revokeObjectURL(url);
|
|
83
|
-
|
|
84
|
-
// Visual feedback
|
|
85
|
-
const button = event.currentTarget;
|
|
86
|
-
const originalHTML = button.innerHTML;
|
|
87
|
-
button.innerHTML = '<i class="bi bi-check"></i> Downloaded!';
|
|
88
|
-
button.classList.remove('btn-outline-secondary');
|
|
89
|
-
button.classList.add('btn-success');
|
|
90
|
-
|
|
91
|
-
setTimeout(() => {
|
|
92
|
-
button.innerHTML = originalHTML;
|
|
93
|
-
button.classList.remove('btn-success');
|
|
94
|
-
button.classList.add('btn-outline-secondary');
|
|
95
|
-
}, 2000);
|
|
96
|
-
}
|
|
97
|
-
</script>
|
|
98
|
-
|
|
99
99
|
<div class="row g-4">
|
|
100
100
|
<!-- Error Information -->
|
|
101
101
|
<div class="col-md-8">
|
|
@@ -160,9 +160,35 @@
|
|
|
160
160
|
</div>
|
|
161
161
|
</div>
|
|
162
162
|
<div class="card-body p-0">
|
|
163
|
-
<div class="code-block p-3" style="max-height:
|
|
164
|
-
|
|
165
|
-
|
|
163
|
+
<div class="code-block p-3" style="max-height: none; overflow-y: visible; overflow-x: auto;">
|
|
164
|
+
<% app_frames.each_with_index do |frame, index| %>
|
|
165
|
+
<div class="backtrace-frame-wrapper mb-2">
|
|
166
|
+
<!-- Frame header (always visible) -->
|
|
167
|
+
<div class="<%= frame_bg_class(frame[:category]) %> d-flex justify-content-between align-items-center px-2 py-1">
|
|
168
|
+
<div>
|
|
169
|
+
<span class="<%= frame_color_class(frame[:category]) %>">
|
|
170
|
+
<%= frame_icon(frame[:category]) %> <%= frame[:short_path] %>:<%= frame[:line_number] %>
|
|
171
|
+
</span> in <span class="backtrace-method-name">`<%= frame[:method_name] %>'</span>
|
|
172
|
+
</div>
|
|
173
|
+
<% if RailsErrorDashboard.configuration.enable_source_code_integration && frame[:category] == :app %>
|
|
174
|
+
<button class="btn btn-sm btn-outline-secondary" type="button"
|
|
175
|
+
data-bs-toggle="collapse"
|
|
176
|
+
data-bs-target="#source-code-<%= index %>"
|
|
177
|
+
aria-expanded="false"
|
|
178
|
+
aria-controls="source-code-<%= index %>">
|
|
179
|
+
<i class="bi bi-code-slash"></i> <span class="d-none d-md-inline">View Source</span>
|
|
180
|
+
</button>
|
|
181
|
+
<% end %>
|
|
182
|
+
</div>
|
|
183
|
+
|
|
184
|
+
<!-- Collapsible source code (only for app frames) -->
|
|
185
|
+
<% if RailsErrorDashboard.configuration.enable_source_code_integration && frame[:category] == :app %>
|
|
186
|
+
<div class="collapse" id="source-code-<%= index %>">
|
|
187
|
+
<%= render "source_code", frame: frame, error: @error, index: index %>
|
|
188
|
+
</div>
|
|
189
|
+
<% end %>
|
|
190
|
+
</div>
|
|
191
|
+
<% end %>
|
|
166
192
|
</div>
|
|
167
193
|
</div>
|
|
168
194
|
</div>
|
|
@@ -183,7 +209,7 @@
|
|
|
183
209
|
<div id="frameworkBacktrace" class="accordion-collapse collapse" data-bs-parent="#frameworkBacktraceAccordion">
|
|
184
210
|
<div class="accordion-body p-0">
|
|
185
211
|
<div class="code-block p-3" style="max-height: 400px; overflow-y: auto; overflow-x: auto;">
|
|
186
|
-
<pre class="mb-0"><code><% framework_frames.each do |frame| %><span class="<%= frame_bg_class(frame[:category]) %> d-block px-2 py-1"><span class="<%= frame_color_class(frame[:category]) %>"><%= frame_icon(frame[:category]) %> <%= frame[:short_path] %>:<%= frame[:line_number] %></span> in <span class="
|
|
212
|
+
<pre class="mb-0"><code><% framework_frames.each do |frame| %><span class="<%= frame_bg_class(frame[:category]) %> d-block px-2 py-1"><span class="<%= frame_color_class(frame[:category]) %>"><%= frame_icon(frame[:category]) %> <%= frame[:short_path] %>:<%= frame[:line_number] %></span> in <span class="backtrace-method-name">`<%= frame[:method_name] %>'</span></span>
|
|
187
213
|
<% end %></code></pre>
|
|
188
214
|
</div>
|
|
189
215
|
</div>
|
|
@@ -424,8 +450,8 @@
|
|
|
424
450
|
<!-- Existing Comments -->
|
|
425
451
|
<% if @error.comments.recent_first.any? %>
|
|
426
452
|
<div class="mb-4">
|
|
427
|
-
<% @error.comments.recent_first.
|
|
428
|
-
<div class="border-bottom pb-3 mb-3">
|
|
453
|
+
<% @error.comments.recent_first.each_with_index do |comment, index| %>
|
|
454
|
+
<div class="<%= index < @error.comments.count - 1 ? 'border-bottom' : '' %> pb-3 mb-3">
|
|
429
455
|
<div class="d-flex justify-content-between align-items-start mb-2">
|
|
430
456
|
<div>
|
|
431
457
|
<strong class="text-primary">
|
|
@@ -441,7 +467,7 @@
|
|
|
441
467
|
</small>
|
|
442
468
|
</div>
|
|
443
469
|
<div class="text-break">
|
|
444
|
-
<%=
|
|
470
|
+
<%= auto_link_urls(comment.body, error: @error).html_safe %>
|
|
445
471
|
</div>
|
|
446
472
|
</div>
|
|
447
473
|
<% end %>
|
|
@@ -453,7 +479,7 @@
|
|
|
453
479
|
<% end %>
|
|
454
480
|
|
|
455
481
|
<!-- Add Comment Form -->
|
|
456
|
-
<div class="border-top pt-3">
|
|
482
|
+
<div class="<%= @error.comments.any? ? '' : 'border-top' %> pt-3">
|
|
457
483
|
<h6 class="mb-3">
|
|
458
484
|
<i class="bi bi-plus-circle"></i> Add Comment
|
|
459
485
|
</h6>
|
|
@@ -741,7 +767,9 @@
|
|
|
741
767
|
<div class="mb-3">
|
|
742
768
|
<small class="text-muted d-block mb-1">Workflow Status</small>
|
|
743
769
|
<% if @error.respond_to?(:status) %>
|
|
744
|
-
|
|
770
|
+
<% badge_color = @error.status_badge_color %>
|
|
771
|
+
<% text_dark = %w[info warning light].include?(badge_color) ? 'text-dark' : '' %>
|
|
772
|
+
<span class="badge bg-<%= badge_color %> <%= text_dark %> fs-6">
|
|
745
773
|
<%= @error.status.titleize %>
|
|
746
774
|
</span>
|
|
747
775
|
<% elsif @error.resolved? %>
|
|
@@ -777,7 +805,7 @@
|
|
|
777
805
|
</small>
|
|
778
806
|
<% end %>
|
|
779
807
|
</div>
|
|
780
|
-
<%= button_to unassign_error_path(@error), method: :
|
|
808
|
+
<%= button_to unassign_error_path(@error), method: :post, class: "btn btn-sm btn-outline-secondary",
|
|
781
809
|
data: { turbo_confirm: "Remove assignment?" } do %>
|
|
782
810
|
<i class="bi bi-x"></i>
|
|
783
811
|
<% end %>
|
|
@@ -811,7 +839,7 @@
|
|
|
811
839
|
<strong>Snoozed</strong><br>
|
|
812
840
|
<small>Until <%= local_time(@error.snoozed_until, format: :datetime) %></small>
|
|
813
841
|
</div>
|
|
814
|
-
<%= button_to unsnooze_error_path(@error), method: :
|
|
842
|
+
<%= button_to unsnooze_error_path(@error), method: :post, class: "btn btn-sm btn-outline-warning" do %>
|
|
815
843
|
<i class="bi bi-alarm-fill"></i> Unsnooze
|
|
816
844
|
<% end %>
|
|
817
845
|
<% else %>
|
|
@@ -840,7 +868,7 @@
|
|
|
840
868
|
<div class="mb-3">
|
|
841
869
|
<small class="text-muted d-block mb-1">Resolution Notes</small>
|
|
842
870
|
<div class="alert alert-success mb-0">
|
|
843
|
-
<%=
|
|
871
|
+
<%= auto_link_urls(@error.resolution_comment, error: @error).html_safe %>
|
|
844
872
|
</div>
|
|
845
873
|
</div>
|
|
846
874
|
<% end %>
|
|
@@ -1066,7 +1094,7 @@
|
|
|
1066
1094
|
<div class="modal fade" id="assignModal" tabindex="-1" aria-labelledby="assignModalLabel" aria-hidden="true">
|
|
1067
1095
|
<div class="modal-dialog">
|
|
1068
1096
|
<div class="modal-content">
|
|
1069
|
-
<%= form_with url: assign_error_path(@error), method: :
|
|
1097
|
+
<%= form_with url: assign_error_path(@error), method: :post do |f| %>
|
|
1070
1098
|
<div class="modal-header">
|
|
1071
1099
|
<h5 class="modal-title" id="assignModalLabel">
|
|
1072
1100
|
<i class="bi bi-person-plus"></i> Assign Error
|
|
@@ -1093,7 +1121,7 @@
|
|
|
1093
1121
|
<div class="modal fade" id="priorityModal" tabindex="-1" aria-labelledby="priorityModalLabel" aria-hidden="true">
|
|
1094
1122
|
<div class="modal-dialog">
|
|
1095
1123
|
<div class="modal-content">
|
|
1096
|
-
<%= form_with url: update_priority_error_path(@error), method: :
|
|
1124
|
+
<%= form_with url: update_priority_error_path(@error), method: :post do |f| %>
|
|
1097
1125
|
<div class="modal-header">
|
|
1098
1126
|
<h5 class="modal-title" id="priorityModalLabel">
|
|
1099
1127
|
<i class="bi bi-flag"></i> Update Priority
|
|
@@ -1104,12 +1132,10 @@
|
|
|
1104
1132
|
<div class="mb-3">
|
|
1105
1133
|
<label for="priority_level" class="form-label">Priority Level <span class="text-danger">*</span></label>
|
|
1106
1134
|
<%= select_tag :priority_level,
|
|
1107
|
-
options_for_select(
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
['Low (P0)', 0]
|
|
1112
|
-
], @error.priority_level),
|
|
1135
|
+
options_for_select(
|
|
1136
|
+
RailsErrorDashboard::ErrorLog.priority_options,
|
|
1137
|
+
@error.priority_level
|
|
1138
|
+
),
|
|
1113
1139
|
class: "form-select", required: true %>
|
|
1114
1140
|
<small class="text-muted">Current: <%= @error.priority_label %></small>
|
|
1115
1141
|
</div>
|
|
@@ -1127,7 +1153,7 @@
|
|
|
1127
1153
|
<div class="modal fade" id="snoozeModal" tabindex="-1" aria-labelledby="snoozeModalLabel" aria-hidden="true">
|
|
1128
1154
|
<div class="modal-dialog">
|
|
1129
1155
|
<div class="modal-content">
|
|
1130
|
-
<%= form_with url: snooze_error_path(@error), method: :
|
|
1156
|
+
<%= form_with url: snooze_error_path(@error), method: :post do |f| %>
|
|
1131
1157
|
<div class="modal-header">
|
|
1132
1158
|
<h5 class="modal-title" id="snoozeModalLabel">
|
|
1133
1159
|
<i class="bi bi-alarm"></i> Snooze Error
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Squashed migration for new installations
|
|
4
|
+
# This migration creates the complete Rails Error Dashboard schema in one step.
|
|
5
|
+
#
|
|
6
|
+
# For existing installations, the incremental migrations (20251224-20260106) will run instead.
|
|
7
|
+
# Detection: If error_logs table already exists, this migration is skipped.
|
|
8
|
+
class CreateRailsErrorDashboardCompleteSchema < ActiveRecord::Migration[7.0]
|
|
9
|
+
def up
|
|
10
|
+
# Skip if this is an existing installation (error_logs table already exists)
|
|
11
|
+
# Existing installations will use incremental migrations instead
|
|
12
|
+
return if table_exists?(:rails_error_dashboard_error_logs)
|
|
13
|
+
|
|
14
|
+
# Create applications table
|
|
15
|
+
create_table :rails_error_dashboard_applications do |t|
|
|
16
|
+
t.string :name, limit: 255, null: false
|
|
17
|
+
t.text :description
|
|
18
|
+
t.datetime :created_at
|
|
19
|
+
t.datetime :updated_at
|
|
20
|
+
end
|
|
21
|
+
add_index :rails_error_dashboard_applications, :name, unique: true
|
|
22
|
+
|
|
23
|
+
# Create error_logs table with ALL columns from incremental migrations
|
|
24
|
+
create_table :rails_error_dashboard_error_logs do |t|
|
|
25
|
+
# Core error fields (from 20251224000001)
|
|
26
|
+
t.string :error_type, null: false
|
|
27
|
+
t.text :message, null: false
|
|
28
|
+
t.text :backtrace
|
|
29
|
+
t.integer :user_id
|
|
30
|
+
t.text :request_url
|
|
31
|
+
t.text :request_params
|
|
32
|
+
t.text :user_agent
|
|
33
|
+
t.string :ip_address
|
|
34
|
+
t.string :platform
|
|
35
|
+
t.boolean :resolved, null: false, default: false
|
|
36
|
+
t.text :resolution_comment
|
|
37
|
+
t.string :resolution_reference
|
|
38
|
+
t.string :resolved_by_name
|
|
39
|
+
t.datetime :resolved_at
|
|
40
|
+
t.datetime :occurred_at, null: false
|
|
41
|
+
|
|
42
|
+
# Enhanced tracking fields (from 20251224081522)
|
|
43
|
+
t.string :error_hash
|
|
44
|
+
t.datetime :first_seen_at
|
|
45
|
+
t.datetime :last_seen_at
|
|
46
|
+
t.integer :occurrence_count, default: 1, null: false
|
|
47
|
+
|
|
48
|
+
# Controller/action context (from 20251224101217)
|
|
49
|
+
t.string :controller_name
|
|
50
|
+
t.string :action_name
|
|
51
|
+
|
|
52
|
+
# Enhanced metrics (from 20251225085859)
|
|
53
|
+
t.string :app_version
|
|
54
|
+
t.string :git_sha
|
|
55
|
+
t.integer :priority_score
|
|
56
|
+
|
|
57
|
+
# Similarity tracking (from 20251225093603)
|
|
58
|
+
t.float :similarity_score
|
|
59
|
+
t.string :backtrace_signature
|
|
60
|
+
|
|
61
|
+
# Workflow fields (from 20251226020000)
|
|
62
|
+
t.string :status, default: "new"
|
|
63
|
+
t.string :assigned_to
|
|
64
|
+
t.datetime :snoozed_until
|
|
65
|
+
t.integer :priority_level, default: 0
|
|
66
|
+
|
|
67
|
+
# Application association (from 20260106094233)
|
|
68
|
+
t.integer :application_id, null: false
|
|
69
|
+
|
|
70
|
+
t.timestamps
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Add ALL indexes from incremental migrations
|
|
74
|
+
# Basic indexes (from 20251224000001)
|
|
75
|
+
add_index :rails_error_dashboard_error_logs, :error_type
|
|
76
|
+
add_index :rails_error_dashboard_error_logs, :resolved
|
|
77
|
+
add_index :rails_error_dashboard_error_logs, :user_id
|
|
78
|
+
add_index :rails_error_dashboard_error_logs, :occurred_at
|
|
79
|
+
add_index :rails_error_dashboard_error_logs, :platform
|
|
80
|
+
|
|
81
|
+
# Tracking indexes (from 20251224081522)
|
|
82
|
+
add_index :rails_error_dashboard_error_logs, :error_hash
|
|
83
|
+
add_index :rails_error_dashboard_error_logs, :first_seen_at
|
|
84
|
+
add_index :rails_error_dashboard_error_logs, :last_seen_at
|
|
85
|
+
add_index :rails_error_dashboard_error_logs, :occurrence_count
|
|
86
|
+
|
|
87
|
+
# Composite indexes for performance (from 20251225071314)
|
|
88
|
+
add_index :rails_error_dashboard_error_logs, [ :error_type, :occurred_at ], name: "index_error_logs_on_error_type_and_occurred_at"
|
|
89
|
+
add_index :rails_error_dashboard_error_logs, [ :resolved, :occurred_at ], name: "index_error_logs_on_resolved_and_occurred_at"
|
|
90
|
+
add_index :rails_error_dashboard_error_logs, [ :platform, :occurred_at ], name: "index_error_logs_on_platform_and_occurred_at"
|
|
91
|
+
add_index :rails_error_dashboard_error_logs, [ :error_hash, :resolved, :occurred_at ], name: "index_error_logs_on_hash_resolved_occurred"
|
|
92
|
+
add_index :rails_error_dashboard_error_logs, [ :controller_name, :action_name, :error_hash ], name: "index_error_logs_on_controller_action_hash"
|
|
93
|
+
|
|
94
|
+
# Enhanced metrics indexes (from 20251225085859)
|
|
95
|
+
add_index :rails_error_dashboard_error_logs, :app_version
|
|
96
|
+
add_index :rails_error_dashboard_error_logs, :git_sha
|
|
97
|
+
add_index :rails_error_dashboard_error_logs, :priority_score
|
|
98
|
+
|
|
99
|
+
# Similarity tracking indexes (from 20251225093603)
|
|
100
|
+
add_index :rails_error_dashboard_error_logs, :similarity_score
|
|
101
|
+
add_index :rails_error_dashboard_error_logs, :backtrace_signature
|
|
102
|
+
|
|
103
|
+
# Application indexes (from 20260106094233, 20251229111223)
|
|
104
|
+
add_index :rails_error_dashboard_error_logs, :application_id
|
|
105
|
+
add_index :rails_error_dashboard_error_logs, [ :application_id, :occurred_at ], name: "index_error_logs_on_app_occurred"
|
|
106
|
+
add_index :rails_error_dashboard_error_logs, [ :application_id, :resolved ], name: "index_error_logs_on_app_resolved"
|
|
107
|
+
|
|
108
|
+
# Workflow indexes (from 20251229111223)
|
|
109
|
+
add_index :rails_error_dashboard_error_logs, [ :assigned_to, :status, :occurred_at ], name: "index_error_logs_on_assignment_workflow"
|
|
110
|
+
add_index :rails_error_dashboard_error_logs, [ :priority_level, :resolved, :occurred_at ], name: "index_error_logs_on_priority_resolution"
|
|
111
|
+
add_index :rails_error_dashboard_error_logs, [ :platform, :status, :occurred_at ], name: "index_error_logs_on_platform_status_time"
|
|
112
|
+
add_index :rails_error_dashboard_error_logs, [ :app_version, :resolved, :occurred_at ], name: "index_error_logs_on_version_resolution_time"
|
|
113
|
+
|
|
114
|
+
# Create error_occurrences table (from 20251225100236)
|
|
115
|
+
create_table :rails_error_dashboard_error_occurrences do |t|
|
|
116
|
+
t.integer :error_log_id, null: false
|
|
117
|
+
t.datetime :occurred_at, null: false
|
|
118
|
+
t.integer :user_id
|
|
119
|
+
t.string :request_id
|
|
120
|
+
t.string :session_id
|
|
121
|
+
t.timestamps
|
|
122
|
+
end
|
|
123
|
+
add_index :rails_error_dashboard_error_occurrences, :error_log_id, name: "index_error_occurrences_on_error_log"
|
|
124
|
+
add_index :rails_error_dashboard_error_occurrences, [ :occurred_at, :error_log_id ], name: "index_error_occurrences_on_time_and_error"
|
|
125
|
+
add_index :rails_error_dashboard_error_occurrences, :user_id, name: "index_error_occurrences_on_user"
|
|
126
|
+
add_index :rails_error_dashboard_error_occurrences, :request_id, name: "index_error_occurrences_on_request"
|
|
127
|
+
|
|
128
|
+
# Create cascade_patterns table (from 20251225101920)
|
|
129
|
+
create_table :rails_error_dashboard_cascade_patterns do |t|
|
|
130
|
+
t.integer :parent_error_id, null: false
|
|
131
|
+
t.integer :child_error_id, null: false
|
|
132
|
+
t.integer :frequency, default: 1, null: false
|
|
133
|
+
t.float :avg_delay_seconds
|
|
134
|
+
t.float :cascade_probability
|
|
135
|
+
t.datetime :last_detected_at
|
|
136
|
+
t.timestamps
|
|
137
|
+
end
|
|
138
|
+
add_index :rails_error_dashboard_cascade_patterns, :parent_error_id, name: "index_cascade_patterns_on_parent"
|
|
139
|
+
add_index :rails_error_dashboard_cascade_patterns, :child_error_id, name: "index_cascade_patterns_on_child"
|
|
140
|
+
add_index :rails_error_dashboard_cascade_patterns, [ :parent_error_id, :child_error_id ], unique: true, name: "index_cascade_patterns_on_parent_and_child"
|
|
141
|
+
add_index :rails_error_dashboard_cascade_patterns, :cascade_probability, name: "index_cascade_patterns_on_probability"
|
|
142
|
+
|
|
143
|
+
# Create error_baselines table (from 20251225102500)
|
|
144
|
+
create_table :rails_error_dashboard_error_baselines do |t|
|
|
145
|
+
t.string :error_type, null: false
|
|
146
|
+
t.string :platform, null: false
|
|
147
|
+
t.string :baseline_type, null: false
|
|
148
|
+
t.datetime :period_start, null: false
|
|
149
|
+
t.datetime :period_end, null: false
|
|
150
|
+
t.integer :count, default: 0, null: false
|
|
151
|
+
t.float :mean
|
|
152
|
+
t.float :std_dev
|
|
153
|
+
t.float :percentile_95
|
|
154
|
+
t.float :percentile_99
|
|
155
|
+
t.integer :sample_size, default: 0, null: false
|
|
156
|
+
t.timestamps
|
|
157
|
+
end
|
|
158
|
+
add_index :rails_error_dashboard_error_baselines, [ :error_type, :platform ], name: "index_error_baselines_on_error_type_and_platform"
|
|
159
|
+
add_index :rails_error_dashboard_error_baselines, :period_end, name: "index_error_baselines_on_period_end"
|
|
160
|
+
add_index :rails_error_dashboard_error_baselines, [ :error_type, :platform, :baseline_type, :period_start ], name: "index_error_baselines_on_type_platform_baseline_period"
|
|
161
|
+
|
|
162
|
+
# Create error_comments table (from 20251226020100)
|
|
163
|
+
create_table :rails_error_dashboard_error_comments do |t|
|
|
164
|
+
t.integer :error_log_id, null: false
|
|
165
|
+
t.string :author_name, null: false
|
|
166
|
+
t.text :body, null: false
|
|
167
|
+
t.timestamps
|
|
168
|
+
end
|
|
169
|
+
add_index :rails_error_dashboard_error_comments, :error_log_id
|
|
170
|
+
add_index :rails_error_dashboard_error_comments, [ :error_log_id, :created_at ], name: "index_error_comments_on_error_and_time"
|
|
171
|
+
|
|
172
|
+
# Add foreign keys
|
|
173
|
+
add_foreign_key :rails_error_dashboard_error_logs, :rails_error_dashboard_applications, column: :application_id
|
|
174
|
+
add_foreign_key :rails_error_dashboard_error_occurrences, :rails_error_dashboard_error_logs, column: :error_log_id
|
|
175
|
+
add_foreign_key :rails_error_dashboard_cascade_patterns, :rails_error_dashboard_error_logs, column: :parent_error_id
|
|
176
|
+
add_foreign_key :rails_error_dashboard_cascade_patterns, :rails_error_dashboard_error_logs, column: :child_error_id
|
|
177
|
+
add_foreign_key :rails_error_dashboard_error_comments, :rails_error_dashboard_error_logs, column: :error_log_id
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def down
|
|
181
|
+
drop_table :rails_error_dashboard_error_comments, if_exists: true
|
|
182
|
+
drop_table :rails_error_dashboard_cascade_patterns, if_exists: true
|
|
183
|
+
drop_table :rails_error_dashboard_error_baselines, if_exists: true
|
|
184
|
+
drop_table :rails_error_dashboard_error_occurrences, if_exists: true
|
|
185
|
+
drop_table :rails_error_dashboard_error_logs, if_exists: true
|
|
186
|
+
drop_table :rails_error_dashboard_applications, if_exists: true
|
|
187
|
+
end
|
|
188
|
+
end
|
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
class CreateRailsErrorDashboardErrorLogs < ActiveRecord::Migration[7.0]
|
|
4
4
|
def change
|
|
5
|
+
# Skip if squashed migration already ran (checks for column added in later migration)
|
|
6
|
+
# If application_id column exists, the squashed migration created the table
|
|
7
|
+
return if table_exists?(:rails_error_dashboard_error_logs) &&
|
|
8
|
+
column_exists?(:rails_error_dashboard_error_logs, :application_id)
|
|
9
|
+
|
|
5
10
|
create_table :rails_error_dashboard_error_logs do |t|
|
|
6
11
|
# Error details
|
|
7
12
|
t.string :error_type, null: false
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
class AddBetterTrackingToErrorLogs < ActiveRecord::Migration[8.1]
|
|
2
2
|
def change
|
|
3
|
+
# Skip if squashed migration already added these columns
|
|
4
|
+
return if column_exists?(:rails_error_dashboard_error_logs, :error_hash)
|
|
5
|
+
|
|
3
6
|
add_column :rails_error_dashboard_error_logs, :error_hash, :string
|
|
4
7
|
add_column :rails_error_dashboard_error_logs, :first_seen_at, :datetime
|
|
5
8
|
add_column :rails_error_dashboard_error_logs, :last_seen_at, :datetime
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
class AddControllerActionToErrorLogs < ActiveRecord::Migration[8.1]
|
|
2
2
|
def change
|
|
3
|
+
# Skip if squashed migration already added these columns
|
|
4
|
+
return if column_exists?(:rails_error_dashboard_error_logs, :controller_name)
|
|
5
|
+
|
|
3
6
|
add_column :rails_error_dashboard_error_logs, :controller_name, :string
|
|
4
7
|
add_column :rails_error_dashboard_error_logs, :action_name, :string
|
|
5
8
|
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
class AddOptimizedIndexesToErrorLogs < ActiveRecord::Migration[8.1]
|
|
4
4
|
def change
|
|
5
|
+
# Skip if squashed migration already added these indexes
|
|
6
|
+
return if index_exists?(:rails_error_dashboard_error_logs, [ :resolved, :occurred_at ],
|
|
7
|
+
name: 'index_error_logs_on_resolved_and_occurred_at')
|
|
8
|
+
|
|
5
9
|
# Composite indexes for common query patterns
|
|
6
10
|
# These improve performance when filtering and sorting together
|
|
7
11
|
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
class RemoveEnvironmentFromErrorLogs < ActiveRecord::Migration[8.1]
|
|
2
2
|
def up
|
|
3
|
+
# Skip if squashed migration ran (column never existed) or already removed
|
|
4
|
+
return unless column_exists?(:rails_error_dashboard_error_logs, :environment)
|
|
5
|
+
|
|
3
6
|
# Remove composite index first
|
|
4
7
|
remove_index :rails_error_dashboard_error_logs,
|
|
5
8
|
name: 'index_error_logs_on_environment_and_occurred_at',
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
class AddEnhancedMetricsToErrorLogs < ActiveRecord::Migration[8.0]
|
|
2
2
|
def change
|
|
3
|
+
# Skip if squashed migration already added these columns
|
|
4
|
+
return if column_exists?(:rails_error_dashboard_error_logs, :app_version)
|
|
5
|
+
|
|
3
6
|
add_column :rails_error_dashboard_error_logs, :app_version, :string
|
|
4
7
|
add_column :rails_error_dashboard_error_logs, :git_sha, :string
|
|
5
8
|
add_column :rails_error_dashboard_error_logs, :priority_score, :integer
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
class AddSimilarityTrackingToErrorLogs < ActiveRecord::Migration[8.0]
|
|
2
2
|
def change
|
|
3
|
+
# Skip if squashed migration already added these columns
|
|
4
|
+
return if column_exists?(:rails_error_dashboard_error_logs, :similarity_score)
|
|
5
|
+
|
|
3
6
|
add_column :rails_error_dashboard_error_logs, :similarity_score, :float
|
|
4
7
|
add_column :rails_error_dashboard_error_logs, :backtrace_signature, :string
|
|
5
8
|
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
class CreateErrorOccurrences < ActiveRecord::Migration[8.0]
|
|
4
4
|
def change
|
|
5
|
+
# Skip if squashed migration already created this table
|
|
6
|
+
return if table_exists?(:rails_error_dashboard_error_occurrences)
|
|
7
|
+
|
|
5
8
|
create_table :rails_error_dashboard_error_occurrences do |t|
|
|
6
9
|
t.references :error_log, null: false, foreign_key: { to_table: :rails_error_dashboard_error_logs }
|
|
7
10
|
t.datetime :occurred_at, null: false
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
class CreateCascadePatterns < ActiveRecord::Migration[8.0]
|
|
4
4
|
def change
|
|
5
|
+
# Skip if squashed migration already created this table
|
|
6
|
+
return if table_exists?(:rails_error_dashboard_cascade_patterns)
|
|
7
|
+
|
|
5
8
|
create_table :rails_error_dashboard_cascade_patterns do |t|
|
|
6
9
|
t.references :parent_error, null: false, foreign_key: { to_table: :rails_error_dashboard_error_logs }
|
|
7
10
|
t.references :child_error, null: false, foreign_key: { to_table: :rails_error_dashboard_error_logs }
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
class CreateErrorBaselines < ActiveRecord::Migration[8.0]
|
|
4
4
|
def change
|
|
5
|
+
# Skip if squashed migration already created this table
|
|
6
|
+
return if table_exists?(:rails_error_dashboard_error_baselines)
|
|
7
|
+
|
|
5
8
|
create_table :rails_error_dashboard_error_baselines do |t|
|
|
6
9
|
t.string :error_type, null: false
|
|
7
10
|
t.string :platform, null: false
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
class AddWorkflowFieldsToErrorLogs < ActiveRecord::Migration[8.0]
|
|
4
4
|
def change
|
|
5
|
+
# Skip if squashed migration already added these columns
|
|
6
|
+
return if column_exists?(:rails_error_dashboard_error_logs, :status)
|
|
7
|
+
|
|
5
8
|
add_column :rails_error_dashboard_error_logs, :status, :string, default: 'new', null: false
|
|
6
9
|
add_column :rails_error_dashboard_error_logs, :assigned_to, :string
|
|
7
10
|
add_column :rails_error_dashboard_error_logs, :assigned_at, :datetime
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
class CreateErrorComments < ActiveRecord::Migration[8.0]
|
|
4
4
|
def change
|
|
5
|
+
# Skip if squashed migration already created this table
|
|
6
|
+
return if table_exists?(:rails_error_dashboard_error_comments)
|
|
7
|
+
|
|
5
8
|
create_table :rails_error_dashboard_error_comments do |t|
|
|
6
9
|
t.references :error_log,
|
|
7
10
|
null: false,
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
class AddAdditionalPerformanceIndexes < ActiveRecord::Migration[7.0]
|
|
4
4
|
def change
|
|
5
|
+
# Skip if squashed migration already added these indexes
|
|
6
|
+
return if index_exists?(:rails_error_dashboard_error_logs, [ :assigned_to, :status, :occurred_at ],
|
|
7
|
+
name: 'index_error_logs_on_assignment_workflow')
|
|
8
|
+
|
|
5
9
|
# Composite index for workflow filtering (assigned errors with status)
|
|
6
10
|
# Common query: WHERE assigned_to = ? AND status = ? ORDER BY occurred_at DESC
|
|
7
11
|
# Used in: "Show me all errors assigned to John that are investigating"
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
class CreateRailsErrorDashboardApplications < ActiveRecord::Migration[7.0]
|
|
2
2
|
def change
|
|
3
|
+
# Skip if squashed migration already ran (applications table already exists)
|
|
4
|
+
return if table_exists?(:rails_error_dashboard_applications)
|
|
5
|
+
|
|
3
6
|
create_table :rails_error_dashboard_applications do |t|
|
|
4
7
|
t.string :name, null: false, limit: 255
|
|
5
8
|
t.text :description
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
class AddApplicationToErrorLogs < ActiveRecord::Migration[7.0]
|
|
2
2
|
def up
|
|
3
|
+
# Skip if squashed migration already added this column
|
|
4
|
+
return if column_exists?(:rails_error_dashboard_error_logs, :application_id)
|
|
5
|
+
|
|
3
6
|
# Add nullable column first (for existing records)
|
|
4
7
|
add_column :rails_error_dashboard_error_logs, :application_id, :bigint
|
|
5
8
|
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
class FinalizeApplicationForeignKey < ActiveRecord::Migration[7.0]
|
|
2
2
|
def up
|
|
3
|
+
# Skip if squashed migration already added the foreign key
|
|
4
|
+
return if foreign_key_exists?(:rails_error_dashboard_error_logs,
|
|
5
|
+
:rails_error_dashboard_applications,
|
|
6
|
+
column: :application_id)
|
|
7
|
+
|
|
3
8
|
# Make NOT NULL
|
|
4
9
|
change_column_null :rails_error_dashboard_error_logs, :application_id, false
|
|
5
10
|
|