rails_error_dashboard 0.1.0 → 0.1.3
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 +305 -703
- data/app/assets/stylesheets/rails_error_dashboard/_catppuccin_mocha.scss +107 -0
- data/app/assets/stylesheets/rails_error_dashboard/_components.scss +625 -0
- data/app/assets/stylesheets/rails_error_dashboard/_layout.scss +257 -0
- data/app/assets/stylesheets/rails_error_dashboard/_theme_variables.scss +203 -0
- data/app/assets/stylesheets/rails_error_dashboard/application.css +926 -15
- data/app/assets/stylesheets/rails_error_dashboard/application.css.map +7 -0
- data/app/assets/stylesheets/rails_error_dashboard/application.scss +61 -0
- data/app/controllers/rails_error_dashboard/application_controller.rb +18 -0
- data/app/controllers/rails_error_dashboard/errors_controller.rb +140 -4
- data/app/helpers/rails_error_dashboard/application_helper.rb +55 -0
- data/app/helpers/rails_error_dashboard/backtrace_helper.rb +91 -0
- data/app/helpers/rails_error_dashboard/overview_helper.rb +78 -0
- data/app/helpers/rails_error_dashboard/user_agent_helper.rb +118 -0
- data/app/jobs/rails_error_dashboard/application_job.rb +19 -0
- data/app/jobs/rails_error_dashboard/async_error_logging_job.rb +48 -0
- data/app/jobs/rails_error_dashboard/baseline_alert_job.rb +263 -0
- data/app/jobs/rails_error_dashboard/discord_error_notification_job.rb +4 -8
- data/app/jobs/rails_error_dashboard/email_error_notification_job.rb +2 -1
- data/app/jobs/rails_error_dashboard/pagerduty_error_notification_job.rb +5 -5
- data/app/jobs/rails_error_dashboard/slack_error_notification_job.rb +10 -6
- data/app/jobs/rails_error_dashboard/webhook_error_notification_job.rb +5 -6
- data/app/mailers/rails_error_dashboard/application_mailer.rb +1 -1
- data/app/mailers/rails_error_dashboard/error_notification_mailer.rb +1 -1
- data/app/models/rails_error_dashboard/cascade_pattern.rb +74 -0
- data/app/models/rails_error_dashboard/error_baseline.rb +100 -0
- data/app/models/rails_error_dashboard/error_comment.rb +27 -0
- data/app/models/rails_error_dashboard/error_log.rb +471 -3
- data/app/models/rails_error_dashboard/error_occurrence.rb +49 -0
- data/app/views/layouts/rails_error_dashboard.html.erb +816 -178
- data/app/views/layouts/rails_error_dashboard_old_backup.html.erb +383 -0
- data/app/views/rails_error_dashboard/error_notification_mailer/error_alert.html.erb +3 -10
- data/app/views/rails_error_dashboard/error_notification_mailer/error_alert.text.erb +1 -2
- data/app/views/rails_error_dashboard/errors/_error_row.html.erb +78 -0
- data/app/views/rails_error_dashboard/errors/_pattern_insights.html.erb +209 -0
- data/app/views/rails_error_dashboard/errors/_stats.html.erb +34 -0
- data/app/views/rails_error_dashboard/errors/_timeline.html.erb +167 -0
- data/app/views/rails_error_dashboard/errors/analytics.html.erb +152 -56
- data/app/views/rails_error_dashboard/errors/correlation.html.erb +373 -0
- data/app/views/rails_error_dashboard/errors/index.html.erb +294 -138
- data/app/views/rails_error_dashboard/errors/overview.html.erb +253 -0
- data/app/views/rails_error_dashboard/errors/platform_comparison.html.erb +399 -0
- data/app/views/rails_error_dashboard/errors/show.html.erb +781 -65
- data/config/routes.rb +9 -0
- data/db/migrate/20251225071314_add_optimized_indexes_to_error_logs.rb +66 -0
- data/db/migrate/20251225074653_remove_environment_from_error_logs.rb +26 -0
- data/db/migrate/20251225085859_add_enhanced_metrics_to_error_logs.rb +12 -0
- data/db/migrate/20251225093603_add_similarity_tracking_to_error_logs.rb +9 -0
- data/db/migrate/20251225100236_create_error_occurrences.rb +31 -0
- data/db/migrate/20251225101920_create_cascade_patterns.rb +33 -0
- data/db/migrate/20251225102500_create_error_baselines.rb +38 -0
- data/db/migrate/20251226020000_add_workflow_fields_to_error_logs.rb +27 -0
- data/db/migrate/20251226020100_create_error_comments.rb +18 -0
- data/lib/generators/rails_error_dashboard/install/install_generator.rb +276 -1
- data/lib/generators/rails_error_dashboard/install/templates/initializer.rb +272 -37
- data/lib/generators/rails_error_dashboard/solid_queue/solid_queue_generator.rb +36 -0
- data/lib/generators/rails_error_dashboard/solid_queue/templates/queue.yml +55 -0
- data/lib/rails_error_dashboard/commands/batch_delete_errors.rb +1 -1
- data/lib/rails_error_dashboard/commands/batch_resolve_errors.rb +2 -2
- data/lib/rails_error_dashboard/commands/log_error.rb +272 -7
- data/lib/rails_error_dashboard/commands/resolve_error.rb +16 -0
- data/lib/rails_error_dashboard/configuration.rb +90 -5
- data/lib/rails_error_dashboard/error_reporter.rb +15 -7
- data/lib/rails_error_dashboard/logger.rb +105 -0
- data/lib/rails_error_dashboard/middleware/error_catcher.rb +17 -10
- data/lib/rails_error_dashboard/plugin.rb +6 -3
- data/lib/rails_error_dashboard/plugin_registry.rb +2 -2
- data/lib/rails_error_dashboard/plugins/audit_log_plugin.rb +0 -1
- data/lib/rails_error_dashboard/plugins/jira_integration_plugin.rb +3 -4
- data/lib/rails_error_dashboard/plugins/metrics_plugin.rb +1 -3
- data/lib/rails_error_dashboard/queries/analytics_stats.rb +44 -6
- data/lib/rails_error_dashboard/queries/baseline_stats.rb +107 -0
- data/lib/rails_error_dashboard/queries/co_occurring_errors.rb +86 -0
- data/lib/rails_error_dashboard/queries/dashboard_stats.rb +242 -2
- data/lib/rails_error_dashboard/queries/error_cascades.rb +74 -0
- data/lib/rails_error_dashboard/queries/error_correlation.rb +375 -0
- data/lib/rails_error_dashboard/queries/errors_list.rb +106 -10
- data/lib/rails_error_dashboard/queries/filter_options.rb +0 -1
- data/lib/rails_error_dashboard/queries/platform_comparison.rb +254 -0
- data/lib/rails_error_dashboard/queries/similar_errors.rb +93 -0
- data/lib/rails_error_dashboard/services/backtrace_parser.rb +113 -0
- data/lib/rails_error_dashboard/services/baseline_alert_throttler.rb +88 -0
- data/lib/rails_error_dashboard/services/baseline_calculator.rb +269 -0
- data/lib/rails_error_dashboard/services/cascade_detector.rb +95 -0
- data/lib/rails_error_dashboard/services/pattern_detector.rb +268 -0
- data/lib/rails_error_dashboard/services/similarity_calculator.rb +144 -0
- data/lib/rails_error_dashboard/value_objects/error_context.rb +27 -1
- data/lib/rails_error_dashboard/version.rb +1 -1
- data/lib/rails_error_dashboard.rb +57 -7
- metadata +69 -10
- data/app/models/rails_error_dashboard/application_record.rb +0 -5
- data/lib/rails_error_dashboard/queries/developer_insights.rb +0 -277
- data/lib/rails_error_dashboard/queries/errors_list_v2.rb +0 -149
- data/lib/tasks/rails_error_dashboard_tasks.rake +0 -4
data/config/routes.rb
CHANGED
|
@@ -4,9 +4,18 @@ RailsErrorDashboard::Engine.routes.draw do
|
|
|
4
4
|
resources :errors, only: [ :index, :show ] do
|
|
5
5
|
member do
|
|
6
6
|
post :resolve
|
|
7
|
+
post :assign
|
|
8
|
+
post :unassign
|
|
9
|
+
post :update_priority
|
|
10
|
+
post :snooze
|
|
11
|
+
post :unsnooze
|
|
12
|
+
post :update_status
|
|
13
|
+
post :add_comment
|
|
7
14
|
end
|
|
8
15
|
collection do
|
|
9
16
|
get :analytics
|
|
17
|
+
get :platform_comparison
|
|
18
|
+
get :correlation
|
|
10
19
|
post :batch_action
|
|
11
20
|
end
|
|
12
21
|
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class AddOptimizedIndexesToErrorLogs < ActiveRecord::Migration[8.1]
|
|
4
|
+
def change
|
|
5
|
+
# Composite indexes for common query patterns
|
|
6
|
+
# These improve performance when filtering and sorting together
|
|
7
|
+
|
|
8
|
+
# Dashboard stats: Count unresolved errors from recent time periods
|
|
9
|
+
# Query: WHERE resolved = false AND occurred_at >= ?
|
|
10
|
+
add_index :rails_error_dashboard_error_logs, [ :resolved, :occurred_at ],
|
|
11
|
+
name: 'index_error_logs_on_resolved_and_occurred_at'
|
|
12
|
+
|
|
13
|
+
# Error type filtering with time ordering
|
|
14
|
+
# Query: WHERE error_type = ? ORDER BY occurred_at DESC
|
|
15
|
+
add_index :rails_error_dashboard_error_logs, [ :error_type, :occurred_at ],
|
|
16
|
+
name: 'index_error_logs_on_error_type_and_occurred_at'
|
|
17
|
+
|
|
18
|
+
# Platform filtering with time ordering
|
|
19
|
+
# Query: WHERE platform = ? ORDER BY occurred_at DESC
|
|
20
|
+
add_index :rails_error_dashboard_error_logs, [ :platform, :occurred_at ],
|
|
21
|
+
name: 'index_error_logs_on_platform_and_occurred_at'
|
|
22
|
+
|
|
23
|
+
# Deduplication lookup: Find existing unresolved errors by hash within 24 hours
|
|
24
|
+
# Query: WHERE error_hash = ? AND resolved = false AND occurred_at >= ?
|
|
25
|
+
# This is the hot path for error logging - happens on EVERY error
|
|
26
|
+
add_index :rails_error_dashboard_error_logs, [ :error_hash, :resolved, :occurred_at ],
|
|
27
|
+
name: 'index_error_logs_on_hash_resolved_occurred'
|
|
28
|
+
|
|
29
|
+
# Partial index for unresolved errors (most queries filter by resolved=false)
|
|
30
|
+
# Only indexes unresolved errors, making it smaller and faster
|
|
31
|
+
# PostgreSQL-specific but gracefully ignored on other databases
|
|
32
|
+
if postgresql?
|
|
33
|
+
add_index :rails_error_dashboard_error_logs, :occurred_at,
|
|
34
|
+
where: "resolved = false",
|
|
35
|
+
name: 'index_error_logs_on_occurred_at_unresolved'
|
|
36
|
+
|
|
37
|
+
# Full-text search index for message field (PostgreSQL only)
|
|
38
|
+
# Dramatically improves search performance
|
|
39
|
+
execute <<-SQL
|
|
40
|
+
CREATE INDEX index_error_logs_on_message_gin
|
|
41
|
+
ON rails_error_dashboard_error_logs
|
|
42
|
+
USING gin(to_tsvector('english', message))
|
|
43
|
+
SQL
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def down
|
|
48
|
+
# Remove composite indexes
|
|
49
|
+
remove_index :rails_error_dashboard_error_logs, name: 'index_error_logs_on_resolved_and_occurred_at'
|
|
50
|
+
remove_index :rails_error_dashboard_error_logs, name: 'index_error_logs_on_error_type_and_occurred_at'
|
|
51
|
+
remove_index :rails_error_dashboard_error_logs, name: 'index_error_logs_on_platform_and_occurred_at'
|
|
52
|
+
remove_index :rails_error_dashboard_error_logs, name: 'index_error_logs_on_hash_resolved_occurred'
|
|
53
|
+
|
|
54
|
+
# Remove partial/GIN indexes if PostgreSQL
|
|
55
|
+
if postgresql?
|
|
56
|
+
remove_index :rails_error_dashboard_error_logs, name: 'index_error_logs_on_occurred_at_unresolved'
|
|
57
|
+
execute "DROP INDEX IF EXISTS index_error_logs_on_message_gin"
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
private
|
|
62
|
+
|
|
63
|
+
def postgresql?
|
|
64
|
+
ActiveRecord::Base.connection.adapter_name.downcase == 'postgresql'
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
class RemoveEnvironmentFromErrorLogs < ActiveRecord::Migration[8.1]
|
|
2
|
+
def up
|
|
3
|
+
# Remove composite index first
|
|
4
|
+
remove_index :rails_error_dashboard_error_logs,
|
|
5
|
+
name: 'index_error_logs_on_environment_and_occurred_at',
|
|
6
|
+
if_exists: true
|
|
7
|
+
|
|
8
|
+
# Remove single column index
|
|
9
|
+
remove_index :rails_error_dashboard_error_logs,
|
|
10
|
+
column: :environment,
|
|
11
|
+
if_exists: true
|
|
12
|
+
|
|
13
|
+
# Remove the column
|
|
14
|
+
remove_column :rails_error_dashboard_error_logs, :environment, :string
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def down
|
|
18
|
+
# Add column back
|
|
19
|
+
add_column :rails_error_dashboard_error_logs, :environment, :string, null: false, default: 'production'
|
|
20
|
+
|
|
21
|
+
# Recreate indexes
|
|
22
|
+
add_index :rails_error_dashboard_error_logs, :environment
|
|
23
|
+
add_index :rails_error_dashboard_error_logs, [ :environment, :occurred_at ],
|
|
24
|
+
name: 'index_error_logs_on_environment_and_occurred_at'
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
class AddEnhancedMetricsToErrorLogs < ActiveRecord::Migration[8.0]
|
|
2
|
+
def change
|
|
3
|
+
add_column :rails_error_dashboard_error_logs, :app_version, :string
|
|
4
|
+
add_column :rails_error_dashboard_error_logs, :git_sha, :string
|
|
5
|
+
add_column :rails_error_dashboard_error_logs, :priority_score, :integer
|
|
6
|
+
|
|
7
|
+
# Indexes for enhanced metrics
|
|
8
|
+
add_index :rails_error_dashboard_error_logs, :app_version
|
|
9
|
+
add_index :rails_error_dashboard_error_logs, :git_sha
|
|
10
|
+
add_index :rails_error_dashboard_error_logs, :priority_score
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
class AddSimilarityTrackingToErrorLogs < ActiveRecord::Migration[8.0]
|
|
2
|
+
def change
|
|
3
|
+
add_column :rails_error_dashboard_error_logs, :similarity_score, :float
|
|
4
|
+
add_column :rails_error_dashboard_error_logs, :backtrace_signature, :string
|
|
5
|
+
|
|
6
|
+
add_index :rails_error_dashboard_error_logs, :similarity_score
|
|
7
|
+
add_index :rails_error_dashboard_error_logs, :backtrace_signature
|
|
8
|
+
end
|
|
9
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateErrorOccurrences < ActiveRecord::Migration[8.0]
|
|
4
|
+
def change
|
|
5
|
+
create_table :rails_error_dashboard_error_occurrences do |t|
|
|
6
|
+
t.references :error_log, null: false, foreign_key: { to_table: :rails_error_dashboard_error_logs }
|
|
7
|
+
t.datetime :occurred_at, null: false
|
|
8
|
+
t.integer :user_id
|
|
9
|
+
t.string :request_id
|
|
10
|
+
t.string :session_id
|
|
11
|
+
|
|
12
|
+
t.timestamps
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Index for finding co-occurring errors by time window
|
|
16
|
+
add_index :rails_error_dashboard_error_occurrences, [ :occurred_at, :error_log_id ],
|
|
17
|
+
name: 'index_error_occurrences_on_time_and_error'
|
|
18
|
+
|
|
19
|
+
# Index for finding all occurrences of a specific error
|
|
20
|
+
add_index :rails_error_dashboard_error_occurrences, :error_log_id,
|
|
21
|
+
name: 'index_error_occurrences_on_error_log'
|
|
22
|
+
|
|
23
|
+
# Index for finding errors by user
|
|
24
|
+
add_index :rails_error_dashboard_error_occurrences, :user_id,
|
|
25
|
+
name: 'index_error_occurrences_on_user'
|
|
26
|
+
|
|
27
|
+
# Index for finding errors by request
|
|
28
|
+
add_index :rails_error_dashboard_error_occurrences, :request_id,
|
|
29
|
+
name: 'index_error_occurrences_on_request'
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateCascadePatterns < ActiveRecord::Migration[8.0]
|
|
4
|
+
def change
|
|
5
|
+
create_table :rails_error_dashboard_cascade_patterns do |t|
|
|
6
|
+
t.references :parent_error, null: false, foreign_key: { to_table: :rails_error_dashboard_error_logs }
|
|
7
|
+
t.references :child_error, null: false, foreign_key: { to_table: :rails_error_dashboard_error_logs }
|
|
8
|
+
t.integer :frequency, default: 1, null: false
|
|
9
|
+
t.float :avg_delay_seconds
|
|
10
|
+
t.float :cascade_probability # 0.0-1.0, percentage of time parent leads to child
|
|
11
|
+
t.datetime :last_detected_at
|
|
12
|
+
|
|
13
|
+
t.timestamps
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Composite index for finding cascade patterns
|
|
17
|
+
add_index :rails_error_dashboard_cascade_patterns, [ :parent_error_id, :child_error_id ],
|
|
18
|
+
name: 'index_cascade_patterns_on_parent_and_child',
|
|
19
|
+
unique: true
|
|
20
|
+
|
|
21
|
+
# Index for finding children of a parent error
|
|
22
|
+
add_index :rails_error_dashboard_cascade_patterns, :parent_error_id,
|
|
23
|
+
name: 'index_cascade_patterns_on_parent'
|
|
24
|
+
|
|
25
|
+
# Index for finding parents of a child error
|
|
26
|
+
add_index :rails_error_dashboard_cascade_patterns, :child_error_id,
|
|
27
|
+
name: 'index_cascade_patterns_on_child'
|
|
28
|
+
|
|
29
|
+
# Index for finding by probability (for high-confidence cascades)
|
|
30
|
+
add_index :rails_error_dashboard_cascade_patterns, :cascade_probability,
|
|
31
|
+
name: 'index_cascade_patterns_on_probability'
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateErrorBaselines < ActiveRecord::Migration[8.0]
|
|
4
|
+
def change
|
|
5
|
+
create_table :rails_error_dashboard_error_baselines do |t|
|
|
6
|
+
t.string :error_type, null: false
|
|
7
|
+
t.string :platform, null: false
|
|
8
|
+
t.string :baseline_type, null: false # hourly, daily, weekly
|
|
9
|
+
t.datetime :period_start, null: false
|
|
10
|
+
t.datetime :period_end, null: false
|
|
11
|
+
|
|
12
|
+
# Statistical metrics
|
|
13
|
+
t.integer :count, null: false, default: 0
|
|
14
|
+
t.float :mean
|
|
15
|
+
t.float :std_dev
|
|
16
|
+
t.float :percentile_95
|
|
17
|
+
t.float :percentile_99
|
|
18
|
+
t.integer :sample_size, null: false, default: 0
|
|
19
|
+
|
|
20
|
+
t.timestamps
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Composite index for efficient baseline lookups
|
|
24
|
+
add_index :rails_error_dashboard_error_baselines,
|
|
25
|
+
[ :error_type, :platform, :baseline_type, :period_start ],
|
|
26
|
+
name: 'index_error_baselines_on_type_platform_baseline_period'
|
|
27
|
+
|
|
28
|
+
# Index for querying by error_type and platform
|
|
29
|
+
add_index :rails_error_dashboard_error_baselines,
|
|
30
|
+
[ :error_type, :platform ],
|
|
31
|
+
name: 'index_error_baselines_on_error_type_and_platform'
|
|
32
|
+
|
|
33
|
+
# Index for cleaning up old baselines
|
|
34
|
+
add_index :rails_error_dashboard_error_baselines,
|
|
35
|
+
:period_end,
|
|
36
|
+
name: 'index_error_baselines_on_period_end'
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class AddWorkflowFieldsToErrorLogs < ActiveRecord::Migration[8.0]
|
|
4
|
+
def change
|
|
5
|
+
add_column :rails_error_dashboard_error_logs, :status, :string, default: 'new', null: false
|
|
6
|
+
add_column :rails_error_dashboard_error_logs, :assigned_to, :string
|
|
7
|
+
add_column :rails_error_dashboard_error_logs, :assigned_at, :datetime
|
|
8
|
+
add_column :rails_error_dashboard_error_logs, :snoozed_until, :datetime
|
|
9
|
+
add_column :rails_error_dashboard_error_logs, :priority_level, :integer, default: 0, null: false
|
|
10
|
+
|
|
11
|
+
add_index :rails_error_dashboard_error_logs, :status
|
|
12
|
+
add_index :rails_error_dashboard_error_logs, :assigned_to
|
|
13
|
+
add_index :rails_error_dashboard_error_logs, :snoozed_until
|
|
14
|
+
add_index :rails_error_dashboard_error_logs, :priority_level
|
|
15
|
+
|
|
16
|
+
# Update existing resolved errors to have status='resolved'
|
|
17
|
+
reversible do |dir|
|
|
18
|
+
dir.up do
|
|
19
|
+
execute <<-SQL
|
|
20
|
+
UPDATE rails_error_dashboard_error_logs
|
|
21
|
+
SET status = 'resolved'
|
|
22
|
+
WHERE resolved = true
|
|
23
|
+
SQL
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateErrorComments < ActiveRecord::Migration[8.0]
|
|
4
|
+
def change
|
|
5
|
+
create_table :rails_error_dashboard_error_comments do |t|
|
|
6
|
+
t.references :error_log,
|
|
7
|
+
null: false,
|
|
8
|
+
foreign_key: { to_table: :rails_error_dashboard_error_logs }
|
|
9
|
+
t.string :author_name, null: false
|
|
10
|
+
t.text :body, null: false
|
|
11
|
+
|
|
12
|
+
t.timestamps
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
add_index :rails_error_dashboard_error_comments, [ :error_log_id, :created_at ],
|
|
16
|
+
name: 'index_error_comments_on_error_and_time'
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -7,7 +7,194 @@ module RailsErrorDashboard
|
|
|
7
7
|
|
|
8
8
|
desc "Installs Rails Error Dashboard and generates the necessary files"
|
|
9
9
|
|
|
10
|
+
class_option :interactive, type: :boolean, default: true, desc: "Interactive feature selection"
|
|
11
|
+
# Notification options
|
|
12
|
+
class_option :slack, type: :boolean, default: false, desc: "Enable Slack notifications"
|
|
13
|
+
class_option :email, type: :boolean, default: false, desc: "Enable email notifications"
|
|
14
|
+
class_option :discord, type: :boolean, default: false, desc: "Enable Discord notifications"
|
|
15
|
+
class_option :pagerduty, type: :boolean, default: false, desc: "Enable PagerDuty notifications"
|
|
16
|
+
class_option :webhooks, type: :boolean, default: false, desc: "Enable webhook notifications"
|
|
17
|
+
# Performance options
|
|
18
|
+
class_option :async_logging, type: :boolean, default: false, desc: "Enable async error logging"
|
|
19
|
+
class_option :error_sampling, type: :boolean, default: false, desc: "Enable error sampling (reduce volume)"
|
|
20
|
+
class_option :separate_database, type: :boolean, default: false, desc: "Use separate database for errors"
|
|
21
|
+
# Advanced analytics options
|
|
22
|
+
class_option :baseline_alerts, type: :boolean, default: false, desc: "Enable baseline anomaly alerts"
|
|
23
|
+
class_option :similar_errors, type: :boolean, default: false, desc: "Enable fuzzy error matching"
|
|
24
|
+
class_option :co_occurring_errors, type: :boolean, default: false, desc: "Enable co-occurring error detection"
|
|
25
|
+
class_option :error_cascades, type: :boolean, default: false, desc: "Enable error cascade detection"
|
|
26
|
+
class_option :error_correlation, type: :boolean, default: false, desc: "Enable error correlation analysis"
|
|
27
|
+
class_option :platform_comparison, type: :boolean, default: false, desc: "Enable platform comparison analytics"
|
|
28
|
+
class_option :occurrence_patterns, type: :boolean, default: false, desc: "Enable occurrence pattern detection"
|
|
29
|
+
|
|
30
|
+
def welcome_message
|
|
31
|
+
say "\n"
|
|
32
|
+
say "=" * 70
|
|
33
|
+
say " 📊 Rails Error Dashboard Installation", :cyan
|
|
34
|
+
say "=" * 70
|
|
35
|
+
say "\n"
|
|
36
|
+
say "Core features will be enabled automatically:", :green
|
|
37
|
+
say " ✓ Error capture (controllers, jobs, middleware)"
|
|
38
|
+
say " ✓ Dashboard UI at /error_dashboard"
|
|
39
|
+
say " ✓ Real-time updates"
|
|
40
|
+
say " ✓ Analytics & spike detection"
|
|
41
|
+
say " ✓ 90-day error retention"
|
|
42
|
+
say "\n"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def select_optional_features
|
|
46
|
+
return unless options[:interactive] && behavior == :invoke
|
|
47
|
+
return unless $stdin.tty? # Skip interactive mode if not running in a terminal
|
|
48
|
+
|
|
49
|
+
say "Let's configure optional features...\n", :cyan
|
|
50
|
+
say "(You can always enable/disable these later in the initializer)\n\n", :yellow
|
|
51
|
+
|
|
52
|
+
@selected_features = {}
|
|
53
|
+
|
|
54
|
+
# Feature definitions with descriptions - organized by category
|
|
55
|
+
features = [
|
|
56
|
+
# === NOTIFICATIONS ===
|
|
57
|
+
{
|
|
58
|
+
key: :slack,
|
|
59
|
+
name: "Slack Notifications",
|
|
60
|
+
description: "Send error alerts to Slack channels",
|
|
61
|
+
category: "Notifications"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
key: :email,
|
|
65
|
+
name: "Email Notifications",
|
|
66
|
+
description: "Send error alerts via email",
|
|
67
|
+
category: "Notifications"
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
key: :discord,
|
|
71
|
+
name: "Discord Notifications",
|
|
72
|
+
description: "Send error alerts to Discord",
|
|
73
|
+
category: "Notifications"
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
key: :pagerduty,
|
|
77
|
+
name: "PagerDuty Integration",
|
|
78
|
+
description: "Critical errors to PagerDuty",
|
|
79
|
+
category: "Notifications"
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
key: :webhooks,
|
|
83
|
+
name: "Generic Webhooks",
|
|
84
|
+
description: "Send data to custom endpoints",
|
|
85
|
+
category: "Notifications"
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
# === PERFORMANCE & SCALABILITY ===
|
|
89
|
+
{
|
|
90
|
+
key: :async_logging,
|
|
91
|
+
name: "Async Error Logging",
|
|
92
|
+
description: "Process errors in background jobs (faster responses)",
|
|
93
|
+
category: "Performance"
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
key: :error_sampling,
|
|
97
|
+
name: "Error Sampling",
|
|
98
|
+
description: "Log only % of non-critical errors (reduce volume)",
|
|
99
|
+
category: "Performance"
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
key: :separate_database,
|
|
103
|
+
name: "Separate Error Database",
|
|
104
|
+
description: "Store errors in dedicated database",
|
|
105
|
+
category: "Performance"
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
# === ADVANCED ANALYTICS ===
|
|
109
|
+
{
|
|
110
|
+
key: :baseline_alerts,
|
|
111
|
+
name: "Baseline Anomaly Alerts",
|
|
112
|
+
description: "Auto-detect unusual error rate spikes",
|
|
113
|
+
category: "Advanced Analytics"
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
key: :similar_errors,
|
|
117
|
+
name: "Fuzzy Error Matching",
|
|
118
|
+
description: "Find similar errors across different hashes",
|
|
119
|
+
category: "Advanced Analytics"
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
key: :co_occurring_errors,
|
|
123
|
+
name: "Co-occurring Errors",
|
|
124
|
+
description: "Detect errors that happen together",
|
|
125
|
+
category: "Advanced Analytics"
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
key: :error_cascades,
|
|
129
|
+
name: "Error Cascade Detection",
|
|
130
|
+
description: "Identify error chains (A causes B causes C)",
|
|
131
|
+
category: "Advanced Analytics"
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
key: :error_correlation,
|
|
135
|
+
name: "Error Correlation Analysis",
|
|
136
|
+
description: "Correlate with versions, users, time",
|
|
137
|
+
category: "Advanced Analytics"
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
key: :platform_comparison,
|
|
141
|
+
name: "Platform Comparison",
|
|
142
|
+
description: "Compare iOS vs Android vs Web health",
|
|
143
|
+
category: "Advanced Analytics"
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
key: :occurrence_patterns,
|
|
147
|
+
name: "Occurrence Pattern Detection",
|
|
148
|
+
description: "Detect cyclical patterns and bursts",
|
|
149
|
+
category: "Advanced Analytics"
|
|
150
|
+
}
|
|
151
|
+
]
|
|
152
|
+
|
|
153
|
+
features.each_with_index do |feature, index|
|
|
154
|
+
say "\n[#{index + 1}/#{features.length}] #{feature[:name]}", :cyan
|
|
155
|
+
say " #{feature[:description]}", :light_black
|
|
156
|
+
|
|
157
|
+
# Check if feature was passed via command line option
|
|
158
|
+
if options[feature[:key]]
|
|
159
|
+
@selected_features[feature[:key]] = true
|
|
160
|
+
say " ✓ Enabled (via --#{feature[:key]} flag)", :green
|
|
161
|
+
else
|
|
162
|
+
response = ask(" Enable? (y/N):", :yellow, limited_to: [ "y", "Y", "n", "N", "" ])
|
|
163
|
+
@selected_features[feature[:key]] = response.downcase == "y"
|
|
164
|
+
|
|
165
|
+
if @selected_features[feature[:key]]
|
|
166
|
+
say " ✓ Enabled", :green
|
|
167
|
+
else
|
|
168
|
+
say " ✗ Disabled", :light_black
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
say "\n"
|
|
174
|
+
end
|
|
175
|
+
|
|
10
176
|
def create_initializer_file
|
|
177
|
+
# Notifications
|
|
178
|
+
@enable_slack = @selected_features&.dig(:slack) || options[:slack]
|
|
179
|
+
@enable_email = @selected_features&.dig(:email) || options[:email]
|
|
180
|
+
@enable_discord = @selected_features&.dig(:discord) || options[:discord]
|
|
181
|
+
@enable_pagerduty = @selected_features&.dig(:pagerduty) || options[:pagerduty]
|
|
182
|
+
@enable_webhooks = @selected_features&.dig(:webhooks) || options[:webhooks]
|
|
183
|
+
|
|
184
|
+
# Performance
|
|
185
|
+
@enable_async_logging = @selected_features&.dig(:async_logging) || options[:async_logging]
|
|
186
|
+
@enable_error_sampling = @selected_features&.dig(:error_sampling) || options[:error_sampling]
|
|
187
|
+
@enable_separate_database = @selected_features&.dig(:separate_database) || options[:separate_database]
|
|
188
|
+
|
|
189
|
+
# Advanced Analytics
|
|
190
|
+
@enable_baseline_alerts = @selected_features&.dig(:baseline_alerts) || options[:baseline_alerts]
|
|
191
|
+
@enable_similar_errors = @selected_features&.dig(:similar_errors) || options[:similar_errors]
|
|
192
|
+
@enable_co_occurring_errors = @selected_features&.dig(:co_occurring_errors) || options[:co_occurring_errors]
|
|
193
|
+
@enable_error_cascades = @selected_features&.dig(:error_cascades) || options[:error_cascades]
|
|
194
|
+
@enable_error_correlation = @selected_features&.dig(:error_correlation) || options[:error_correlation]
|
|
195
|
+
@enable_platform_comparison = @selected_features&.dig(:platform_comparison) || options[:platform_comparison]
|
|
196
|
+
@enable_occurrence_patterns = @selected_features&.dig(:occurrence_patterns) || options[:occurrence_patterns]
|
|
197
|
+
|
|
11
198
|
template "initializer.rb", "config/initializers/rails_error_dashboard.rb"
|
|
12
199
|
end
|
|
13
200
|
|
|
@@ -19,8 +206,96 @@ module RailsErrorDashboard
|
|
|
19
206
|
route "mount RailsErrorDashboard::Engine => '/error_dashboard'"
|
|
20
207
|
end
|
|
21
208
|
|
|
209
|
+
def show_feature_summary
|
|
210
|
+
return unless behavior == :invoke
|
|
211
|
+
|
|
212
|
+
say "\n"
|
|
213
|
+
say "=" * 70
|
|
214
|
+
say " ✓ Installation Complete!", :green
|
|
215
|
+
say "=" * 70
|
|
216
|
+
say "\n"
|
|
217
|
+
|
|
218
|
+
say "Core Features (Always ON):", :cyan
|
|
219
|
+
say " ✓ Error Capture", :green
|
|
220
|
+
say " ✓ Dashboard UI", :green
|
|
221
|
+
say " ✓ Real-time Updates", :green
|
|
222
|
+
say " ✓ Analytics", :green
|
|
223
|
+
|
|
224
|
+
# Count optional features enabled
|
|
225
|
+
enabled_count = 0
|
|
226
|
+
|
|
227
|
+
# Notifications
|
|
228
|
+
notification_features = []
|
|
229
|
+
notification_features << "Slack" if @enable_slack
|
|
230
|
+
notification_features << "Email" if @enable_email
|
|
231
|
+
notification_features << "Discord" if @enable_discord
|
|
232
|
+
notification_features << "PagerDuty" if @enable_pagerduty
|
|
233
|
+
notification_features << "Webhooks" if @enable_webhooks
|
|
234
|
+
|
|
235
|
+
if notification_features.any?
|
|
236
|
+
say "\nNotifications:", :cyan
|
|
237
|
+
say " ✓ #{notification_features.join(", ")}", :green
|
|
238
|
+
enabled_count += notification_features.size
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# Performance
|
|
242
|
+
performance_features = []
|
|
243
|
+
performance_features << "Async Logging" if @enable_async_logging
|
|
244
|
+
performance_features << "Error Sampling" if @enable_error_sampling
|
|
245
|
+
performance_features << "Separate Database" if @enable_separate_database
|
|
246
|
+
|
|
247
|
+
if performance_features.any?
|
|
248
|
+
say "\nPerformance:", :cyan
|
|
249
|
+
say " ✓ #{performance_features.join(", ")}", :green
|
|
250
|
+
enabled_count += performance_features.size
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
# Advanced Analytics
|
|
254
|
+
analytics_features = []
|
|
255
|
+
analytics_features << "Baseline Alerts" if @enable_baseline_alerts
|
|
256
|
+
analytics_features << "Fuzzy Matching" if @enable_similar_errors
|
|
257
|
+
analytics_features << "Co-occurring Errors" if @enable_co_occurring_errors
|
|
258
|
+
analytics_features << "Error Cascades" if @enable_error_cascades
|
|
259
|
+
analytics_features << "Error Correlation" if @enable_error_correlation
|
|
260
|
+
analytics_features << "Platform Comparison" if @enable_platform_comparison
|
|
261
|
+
analytics_features << "Pattern Detection" if @enable_occurrence_patterns
|
|
262
|
+
|
|
263
|
+
if analytics_features.any?
|
|
264
|
+
say "\nAdvanced Analytics:", :cyan
|
|
265
|
+
say " ✓ #{analytics_features.join(", ")}", :green
|
|
266
|
+
enabled_count += analytics_features.size
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
say "\n"
|
|
270
|
+
say "Configuration Required:", :yellow if enabled_count > 0
|
|
271
|
+
say " → Edit config/initializers/rails_error_dashboard.rb", :yellow if @enable_error_sampling
|
|
272
|
+
say " → Set SLACK_WEBHOOK_URL in .env", :yellow if @enable_slack
|
|
273
|
+
say " → Set ERROR_NOTIFICATION_EMAILS in .env", :yellow if @enable_email
|
|
274
|
+
say " → Set DISCORD_WEBHOOK_URL in .env", :yellow if @enable_discord
|
|
275
|
+
say " → Set PAGERDUTY_INTEGRATION_KEY in .env", :yellow if @enable_pagerduty
|
|
276
|
+
say " → Set WEBHOOK_URLS in .env", :yellow if @enable_webhooks
|
|
277
|
+
say " → Ensure Sidekiq/Solid Queue running", :yellow if @enable_async_logging
|
|
278
|
+
say " → Configure database.yml (docs/guides/DATABASE_OPTIONS.md)", :yellow if @enable_separate_database
|
|
279
|
+
|
|
280
|
+
say "\n"
|
|
281
|
+
say "Next Steps:", :cyan
|
|
282
|
+
say " 1. Run: rails db:migrate"
|
|
283
|
+
say " 2. Update credentials in config/initializers/rails_error_dashboard.rb"
|
|
284
|
+
say " 3. Restart your Rails server"
|
|
285
|
+
say " 4. Visit http://localhost:3000/error_dashboard"
|
|
286
|
+
say "\n"
|
|
287
|
+
say "📖 Documentation:", :light_black
|
|
288
|
+
say " • Quick Start: docs/QUICKSTART.md", :light_black
|
|
289
|
+
say " • Complete Feature Guide: docs/FEATURES.md", :light_black
|
|
290
|
+
say " • All Docs: docs/README.md", :light_black
|
|
291
|
+
say "\n"
|
|
292
|
+
say "⚙️ To enable/disable features later:", :light_black
|
|
293
|
+
say " Edit config/initializers/rails_error_dashboard.rb", :light_black
|
|
294
|
+
say "\n"
|
|
295
|
+
end
|
|
296
|
+
|
|
22
297
|
def show_readme
|
|
23
|
-
|
|
298
|
+
# Skip the old README display since we have the new summary
|
|
24
299
|
end
|
|
25
300
|
end
|
|
26
301
|
end
|