rails_error_dashboard 0.1.29 → 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 +34 -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 +66 -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/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 +44 -20
- 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/dashboard_stats.rb +19 -4
- data/lib/rails_error_dashboard/queries/errors_list.rb +20 -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 +13 -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
|
@@ -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>
|
|
@@ -779,7 +805,7 @@
|
|
|
779
805
|
</small>
|
|
780
806
|
<% end %>
|
|
781
807
|
</div>
|
|
782
|
-
<%= button_to unassign_error_path(@error), method: :
|
|
808
|
+
<%= button_to unassign_error_path(@error), method: :post, class: "btn btn-sm btn-outline-secondary",
|
|
783
809
|
data: { turbo_confirm: "Remove assignment?" } do %>
|
|
784
810
|
<i class="bi bi-x"></i>
|
|
785
811
|
<% end %>
|
|
@@ -813,7 +839,7 @@
|
|
|
813
839
|
<strong>Snoozed</strong><br>
|
|
814
840
|
<small>Until <%= local_time(@error.snoozed_until, format: :datetime) %></small>
|
|
815
841
|
</div>
|
|
816
|
-
<%= button_to unsnooze_error_path(@error), method: :
|
|
842
|
+
<%= button_to unsnooze_error_path(@error), method: :post, class: "btn btn-sm btn-outline-warning" do %>
|
|
817
843
|
<i class="bi bi-alarm-fill"></i> Unsnooze
|
|
818
844
|
<% end %>
|
|
819
845
|
<% else %>
|
|
@@ -842,7 +868,7 @@
|
|
|
842
868
|
<div class="mb-3">
|
|
843
869
|
<small class="text-muted d-block mb-1">Resolution Notes</small>
|
|
844
870
|
<div class="alert alert-success mb-0">
|
|
845
|
-
<%=
|
|
871
|
+
<%= auto_link_urls(@error.resolution_comment, error: @error).html_safe %>
|
|
846
872
|
</div>
|
|
847
873
|
</div>
|
|
848
874
|
<% end %>
|
|
@@ -1068,7 +1094,7 @@
|
|
|
1068
1094
|
<div class="modal fade" id="assignModal" tabindex="-1" aria-labelledby="assignModalLabel" aria-hidden="true">
|
|
1069
1095
|
<div class="modal-dialog">
|
|
1070
1096
|
<div class="modal-content">
|
|
1071
|
-
<%= form_with url: assign_error_path(@error), method: :
|
|
1097
|
+
<%= form_with url: assign_error_path(@error), method: :post do |f| %>
|
|
1072
1098
|
<div class="modal-header">
|
|
1073
1099
|
<h5 class="modal-title" id="assignModalLabel">
|
|
1074
1100
|
<i class="bi bi-person-plus"></i> Assign Error
|
|
@@ -1095,7 +1121,7 @@
|
|
|
1095
1121
|
<div class="modal fade" id="priorityModal" tabindex="-1" aria-labelledby="priorityModalLabel" aria-hidden="true">
|
|
1096
1122
|
<div class="modal-dialog">
|
|
1097
1123
|
<div class="modal-content">
|
|
1098
|
-
<%= form_with url: update_priority_error_path(@error), method: :
|
|
1124
|
+
<%= form_with url: update_priority_error_path(@error), method: :post do |f| %>
|
|
1099
1125
|
<div class="modal-header">
|
|
1100
1126
|
<h5 class="modal-title" id="priorityModalLabel">
|
|
1101
1127
|
<i class="bi bi-flag"></i> Update Priority
|
|
@@ -1106,12 +1132,10 @@
|
|
|
1106
1132
|
<div class="mb-3">
|
|
1107
1133
|
<label for="priority_level" class="form-label">Priority Level <span class="text-danger">*</span></label>
|
|
1108
1134
|
<%= select_tag :priority_level,
|
|
1109
|
-
options_for_select(
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
['Low (P0)', 0]
|
|
1114
|
-
], @error.priority_level),
|
|
1135
|
+
options_for_select(
|
|
1136
|
+
RailsErrorDashboard::ErrorLog.priority_options,
|
|
1137
|
+
@error.priority_level
|
|
1138
|
+
),
|
|
1115
1139
|
class: "form-select", required: true %>
|
|
1116
1140
|
<small class="text-muted">Current: <%= @error.priority_label %></small>
|
|
1117
1141
|
</div>
|
|
@@ -1129,7 +1153,7 @@
|
|
|
1129
1153
|
<div class="modal fade" id="snoozeModal" tabindex="-1" aria-labelledby="snoozeModalLabel" aria-hidden="true">
|
|
1130
1154
|
<div class="modal-dialog">
|
|
1131
1155
|
<div class="modal-content">
|
|
1132
|
-
<%= form_with url: snooze_error_path(@error), method: :
|
|
1156
|
+
<%= form_with url: snooze_error_path(@error), method: :post do |f| %>
|
|
1133
1157
|
<div class="modal-header">
|
|
1134
1158
|
<h5 class="modal-title" id="snoozeModalLabel">
|
|
1135
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
|
|
|
@@ -27,6 +27,9 @@ module RailsErrorDashboard
|
|
|
27
27
|
class_option :error_correlation, type: :boolean, default: false, desc: "Enable error correlation analysis"
|
|
28
28
|
class_option :platform_comparison, type: :boolean, default: false, desc: "Enable platform comparison analytics"
|
|
29
29
|
class_option :occurrence_patterns, type: :boolean, default: false, desc: "Enable occurrence pattern detection"
|
|
30
|
+
# Developer tools options
|
|
31
|
+
class_option :source_code_integration, type: :boolean, default: false, desc: "Enable source code viewer (NEW!)"
|
|
32
|
+
class_option :git_blame, type: :boolean, default: false, desc: "Enable git blame integration (NEW!)"
|
|
30
33
|
|
|
31
34
|
def welcome_message
|
|
32
35
|
say "\n"
|
|
@@ -148,6 +151,20 @@ module RailsErrorDashboard
|
|
|
148
151
|
name: "Occurrence Pattern Detection",
|
|
149
152
|
description: "Detect cyclical patterns and bursts",
|
|
150
153
|
category: "Advanced Analytics"
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
# === DEVELOPER TOOLS ===
|
|
157
|
+
{
|
|
158
|
+
key: :source_code_integration,
|
|
159
|
+
name: "Source Code Integration (NEW!)",
|
|
160
|
+
description: "View source code directly in error details",
|
|
161
|
+
category: "Developer Tools"
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
key: :git_blame,
|
|
165
|
+
name: "Git Blame Integration (NEW!)",
|
|
166
|
+
description: "Show git blame info (author, commit, timestamp)",
|
|
167
|
+
category: "Developer Tools"
|
|
151
168
|
}
|
|
152
169
|
]
|
|
153
170
|
|
|
@@ -197,6 +214,10 @@ module RailsErrorDashboard
|
|
|
197
214
|
@enable_platform_comparison = @selected_features&.dig(:platform_comparison) || options[:platform_comparison]
|
|
198
215
|
@enable_occurrence_patterns = @selected_features&.dig(:occurrence_patterns) || options[:occurrence_patterns]
|
|
199
216
|
|
|
217
|
+
# Developer Tools
|
|
218
|
+
@enable_source_code_integration = @selected_features&.dig(:source_code_integration) || options[:source_code_integration]
|
|
219
|
+
@enable_git_blame = @selected_features&.dig(:git_blame) || options[:git_blame]
|
|
220
|
+
|
|
200
221
|
template "initializer.rb", "config/initializers/rails_error_dashboard.rb"
|
|
201
222
|
end
|
|
202
223
|
|
|
@@ -268,6 +289,17 @@ module RailsErrorDashboard
|
|
|
268
289
|
enabled_count += analytics_features.size
|
|
269
290
|
end
|
|
270
291
|
|
|
292
|
+
# Developer Tools
|
|
293
|
+
developer_tools_features = []
|
|
294
|
+
developer_tools_features << "Source Code Integration" if @enable_source_code_integration
|
|
295
|
+
developer_tools_features << "Git Blame" if @enable_git_blame
|
|
296
|
+
|
|
297
|
+
if developer_tools_features.any?
|
|
298
|
+
say "\nDeveloper Tools:", :cyan
|
|
299
|
+
say " ✓ #{developer_tools_features.join(", ")}", :green
|
|
300
|
+
enabled_count += developer_tools_features.size
|
|
301
|
+
end
|
|
302
|
+
|
|
271
303
|
say "\n"
|
|
272
304
|
say "Configuration Required:", :yellow if enabled_count > 0
|
|
273
305
|
say " → Edit config/initializers/rails_error_dashboard.rb", :yellow if @enable_error_sampling
|
|
@@ -22,8 +22,9 @@ RailsErrorDashboard.configure do |config|
|
|
|
22
22
|
# User model for error associations
|
|
23
23
|
config.user_model = "User"
|
|
24
24
|
|
|
25
|
-
# Error retention policy
|
|
26
|
-
|
|
25
|
+
# Error retention policy - nil means keep forever (no automatic deletion)
|
|
26
|
+
# To manually cleanup old errors: rails error_dashboard:cleanup_resolved DAYS=90
|
|
27
|
+
config.retention_days = nil
|
|
27
28
|
|
|
28
29
|
# ============================================================================
|
|
29
30
|
# NOTIFICATION SETTINGS
|
|
@@ -120,8 +121,8 @@ RailsErrorDashboard.configure do |config|
|
|
|
120
121
|
# config.async_adapter = :sidekiq # Options: :sidekiq, :solid_queue, :async
|
|
121
122
|
|
|
122
123
|
<% end -%>
|
|
123
|
-
# Backtrace size limiting (
|
|
124
|
-
config.max_backtrace_lines =
|
|
124
|
+
# Backtrace size limiting (100 lines is industry standard: Rollbar, Airbrake, Bugsnag)
|
|
125
|
+
config.max_backtrace_lines = 100
|
|
125
126
|
|
|
126
127
|
<% if @enable_error_sampling -%>
|
|
127
128
|
# Error Sampling - ENABLED
|
|
@@ -262,6 +263,38 @@ RailsErrorDashboard.configure do |config|
|
|
|
262
263
|
config.enable_occurrence_patterns = false
|
|
263
264
|
|
|
264
265
|
<% end -%>
|
|
266
|
+
# ============================================================================
|
|
267
|
+
# DEVELOPER TOOLS (NEW!)
|
|
268
|
+
# ============================================================================
|
|
269
|
+
|
|
270
|
+
<% if @enable_source_code_integration -%>
|
|
271
|
+
# Source Code Integration - ENABLED (NEW!)
|
|
272
|
+
# View source code directly in error details with inline viewer
|
|
273
|
+
config.enable_source_code_integration = true
|
|
274
|
+
# To disable: Set config.enable_source_code_integration = false
|
|
275
|
+
|
|
276
|
+
<% else -%>
|
|
277
|
+
# Source Code Integration - DISABLED (NEW!)
|
|
278
|
+
# To enable: Set config.enable_source_code_integration = true
|
|
279
|
+
config.enable_source_code_integration = false
|
|
280
|
+
|
|
281
|
+
<% end -%>
|
|
282
|
+
<% if @enable_git_blame -%>
|
|
283
|
+
# Git Blame Integration - ENABLED (NEW!)
|
|
284
|
+
# Show git blame info (author, commit, timestamp) for each source line
|
|
285
|
+
config.enable_git_blame = true
|
|
286
|
+
# To disable: Set config.enable_git_blame = false
|
|
287
|
+
|
|
288
|
+
<% else -%>
|
|
289
|
+
# Git Blame Integration - DISABLED (NEW!)
|
|
290
|
+
# To enable: Set config.enable_git_blame = true (requires Git installed)
|
|
291
|
+
config.enable_git_blame = false
|
|
292
|
+
|
|
293
|
+
<% end -%>
|
|
294
|
+
# Repository settings (auto-detected from git remote, optional override)
|
|
295
|
+
# config.repository_url = ENV["REPOSITORY_URL"] # e.g., "https://github.com/user/repo"
|
|
296
|
+
# config.repository_branch = ENV.fetch("REPOSITORY_BRANCH", "main") # Default branch
|
|
297
|
+
|
|
265
298
|
# ============================================================================
|
|
266
299
|
# INTERNAL LOGGING (Silent by Default)
|
|
267
300
|
# ============================================================================
|