rails_error_dashboard 0.12.1 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/rails_error_dashboard/application_controller.rb +92 -11
  3. data/app/controllers/rails_error_dashboard/errors_controller.rb +111 -34
  4. data/app/controllers/rails_error_dashboard/webhooks_controller.rb +3 -0
  5. data/app/helpers/rails_error_dashboard/application_helper.rb +46 -1
  6. data/app/helpers/rails_error_dashboard/backtrace_helper.rb +8 -0
  7. data/app/jobs/rails_error_dashboard/async_error_logging_job.rb +10 -0
  8. data/app/jobs/rails_error_dashboard/concerns/plain_channel_message.rb +71 -0
  9. data/app/jobs/rails_error_dashboard/notification_burst_summary_job.rb +55 -0
  10. data/app/jobs/rails_error_dashboard/retention_cleanup_job.rb +122 -1
  11. data/app/jobs/rails_error_dashboard/storm_flush_job.rb +7 -4
  12. data/app/jobs/rails_error_dashboard/storm_notification_job.rb +8 -44
  13. data/app/models/rails_error_dashboard/error_baseline.rb +12 -9
  14. data/app/models/rails_error_dashboard/error_comment.rb +0 -5
  15. data/app/models/rails_error_dashboard/error_log.rb +19 -3
  16. data/app/models/rails_error_dashboard/error_logs_record.rb +34 -0
  17. data/app/models/rails_error_dashboard/error_occurrence.rb +9 -1
  18. data/app/models/rails_error_dashboard/event_count.rb +132 -0
  19. data/app/models/rails_error_dashboard/event_timing_gap.rb +55 -0
  20. data/app/views/layouts/rails_error_dashboard.html.erb +58 -5
  21. data/app/views/rails_error_dashboard/errors/_discussion.html.erb +18 -11
  22. data/app/views/rails_error_dashboard/errors/_issue_section.html.erb +22 -8
  23. data/app/views/rails_error_dashboard/errors/_request_context.html.erb +2 -0
  24. data/app/views/rails_error_dashboard/errors/_sidebar_metadata.html.erb +15 -6
  25. data/app/views/rails_error_dashboard/errors/analytics.html.erb +8 -8
  26. data/app/views/rails_error_dashboard/errors/diagnostic_dumps.html.erb +1 -1
  27. data/app/views/rails_error_dashboard/errors/index.html.erb +6 -1
  28. data/app/views/rails_error_dashboard/errors/overview.html.erb +12 -0
  29. data/app/views/rails_error_dashboard/errors/platform_comparison.html.erb +7 -7
  30. data/app/views/rails_error_dashboard/errors/releases.html.erb +2 -2
  31. data/app/views/rails_error_dashboard/errors/settings.html.erb +2 -0
  32. data/app/views/rails_error_dashboard/errors/show.html.erb +3 -3
  33. data/config/locales/de.yml +30 -0
  34. data/config/locales/en.yml +37 -0
  35. data/config/locales/es.yml +30 -0
  36. data/config/locales/fr.yml +30 -0
  37. data/config/locales/it.yml +30 -0
  38. data/config/locales/ja.yml +30 -0
  39. data/config/locales/pl.yml +30 -0
  40. data/config/locales/pt-BR.yml +30 -0
  41. data/config/locales/ru.yml +30 -0
  42. data/config/locales/uk.yml +30 -0
  43. data/config/locales/zh-CN.yml +30 -0
  44. data/db/migrate/20260917000001_add_last_notified_at_to_error_logs.rb +40 -0
  45. data/db/migrate/20260919000001_create_event_counts.rb +71 -0
  46. data/db/migrate/20260920000001_add_buckets_incomplete_to_storm_events.rb +25 -0
  47. data/db/migrate/20260920000002_create_event_timing_gaps.rb +55 -0
  48. data/lib/generators/rails_error_dashboard/install/templates/initializer.rb +12 -1
  49. data/lib/rails_error_dashboard/commands/assign_error.rb +12 -2
  50. data/lib/rails_error_dashboard/commands/backfill_environments.rb +2 -0
  51. data/lib/rails_error_dashboard/commands/backfill_resolved_at.rb +42 -0
  52. data/lib/rails_error_dashboard/commands/batch_delete_errors.rb +1 -0
  53. data/lib/rails_error_dashboard/commands/batch_mute_errors.rb +2 -0
  54. data/lib/rails_error_dashboard/commands/batch_resolve_errors.rb +2 -0
  55. data/lib/rails_error_dashboard/commands/batch_unmute_errors.rb +2 -0
  56. data/lib/rails_error_dashboard/commands/find_or_increment_error.rb +109 -11
  57. data/lib/rails_error_dashboard/commands/flush_rack_attack_events.rb +3 -1
  58. data/lib/rails_error_dashboard/commands/flush_storm_counts.rb +252 -12
  59. data/lib/rails_error_dashboard/commands/flush_swallowed_exceptions.rb +5 -2
  60. data/lib/rails_error_dashboard/commands/link_existing_issue.rb +1 -0
  61. data/lib/rails_error_dashboard/commands/log_error.rb +291 -40
  62. data/lib/rails_error_dashboard/commands/mute_error.rb +1 -0
  63. data/lib/rails_error_dashboard/commands/resolve_error.rb +2 -0
  64. data/lib/rails_error_dashboard/commands/scrub_invalid_encoding.rb +104 -0
  65. data/lib/rails_error_dashboard/commands/snooze_error.rb +34 -10
  66. data/lib/rails_error_dashboard/commands/unmute_error.rb +1 -0
  67. data/lib/rails_error_dashboard/commands/update_error_priority.rb +23 -2
  68. data/lib/rails_error_dashboard/commands/update_error_status.rb +33 -6
  69. data/lib/rails_error_dashboard/configuration.rb +39 -1
  70. data/lib/rails_error_dashboard/engine.rb +28 -0
  71. data/lib/rails_error_dashboard/manual_error_reporter.rb +16 -5
  72. data/lib/rails_error_dashboard/queries/analytics_stats.rb +89 -30
  73. data/lib/rails_error_dashboard/queries/baseline_stats.rb +107 -0
  74. data/lib/rails_error_dashboard/queries/dashboard_stats.rb +216 -74
  75. data/lib/rails_error_dashboard/queries/error_correlation.rb +6 -3
  76. data/lib/rails_error_dashboard/queries/errors_list.rb +15 -2
  77. data/lib/rails_error_dashboard/queries/event_volume.rb +503 -0
  78. data/lib/rails_error_dashboard/queries/similar_errors.rb +1 -1
  79. data/lib/rails_error_dashboard/services/analytics_cache_manager.rb +43 -18
  80. data/lib/rails_error_dashboard/services/backtrace_processor.rb +3 -1
  81. data/lib/rails_error_dashboard/services/breadcrumb_collector.rb +23 -0
  82. data/lib/rails_error_dashboard/services/cause_chain_extractor.rb +3 -1
  83. data/lib/rails_error_dashboard/services/codeberg_issue_client.rb +13 -2
  84. data/lib/rails_error_dashboard/services/diagnostic_dump_generator.rb +5 -3
  85. data/lib/rails_error_dashboard/services/encoding_sanitizer.rb +80 -0
  86. data/lib/rails_error_dashboard/services/error_broadcaster.rb +180 -32
  87. data/lib/rails_error_dashboard/services/error_hash_generator.rb +9 -3
  88. data/lib/rails_error_dashboard/services/error_notification_dispatcher.rb +13 -0
  89. data/lib/rails_error_dashboard/services/exception_filter.rb +55 -0
  90. data/lib/rails_error_dashboard/services/git_head_reader.rb +102 -0
  91. data/lib/rails_error_dashboard/services/notification_throttler.rb +172 -28
  92. data/lib/rails_error_dashboard/services/sensitive_data_filter.rb +47 -1
  93. data/lib/rails_error_dashboard/services/storm_protection/circuit_breaker.rb +59 -3
  94. data/lib/rails_error_dashboard/services/storm_protection/count_buffer.rb +64 -6
  95. data/lib/rails_error_dashboard/services/storm_protection/fingerprint_buckets.rb +25 -2
  96. data/lib/rails_error_dashboard/services/storm_protection/gate.rb +32 -5
  97. data/lib/rails_error_dashboard/services/swallowed_exception_tracker.rb +99 -23
  98. data/lib/rails_error_dashboard/services/url_safety.rb +40 -0
  99. data/lib/rails_error_dashboard/services/variable_serializer.rb +125 -10
  100. data/lib/rails_error_dashboard/subscribers/breadcrumb_subscriber.rb +111 -0
  101. data/lib/rails_error_dashboard/subscribers/issue_tracker_subscriber.rb +11 -2
  102. data/lib/rails_error_dashboard/value_objects/error_context.rb +40 -3
  103. data/lib/rails_error_dashboard/version.rb +1 -1
  104. data/lib/rails_error_dashboard.rb +34 -0
  105. data/lib/tasks/error_dashboard.rake +54 -4
  106. metadata +16 -2
@@ -56,11 +56,14 @@ require "rails_error_dashboard/services/pearson_correlation"
56
56
  require "rails_error_dashboard/services/statistical_classifier"
57
57
  require "rails_error_dashboard/services/source_code_reader"
58
58
  require "rails_error_dashboard/services/git_blame_reader"
59
+ require "rails_error_dashboard/services/git_head_reader"
59
60
  require "rails_error_dashboard/services/github_link_generator"
60
61
  require "rails_error_dashboard/services/cause_chain_extractor"
61
62
  require "rails_error_dashboard/services/environment_snapshot"
62
63
  require "rails_error_dashboard/services/system_health_snapshot"
64
+ require "rails_error_dashboard/services/encoding_sanitizer"
63
65
  require "rails_error_dashboard/services/sensitive_data_filter"
66
+ require "rails_error_dashboard/services/url_safety"
64
67
  require "rails_error_dashboard/services/notification_throttler"
65
68
  require "rails_error_dashboard/services/breadcrumb_collector"
66
69
  require "rails_error_dashboard/services/n_plus_one_detector"
@@ -132,7 +135,12 @@ require "rails_error_dashboard/commands/upsert_cascade_pattern"
132
135
  require "rails_error_dashboard/commands/upsert_baseline"
133
136
  require "rails_error_dashboard/commands/flush_swallowed_exceptions"
134
137
  require "rails_error_dashboard/commands/flush_rack_attack_events"
138
+ require "rails_error_dashboard/commands/scrub_invalid_encoding"
139
+ require "rails_error_dashboard/commands/backfill_resolved_at"
135
140
  require "rails_error_dashboard/queries/errors_list"
141
+ # Before the queries that use it: window volume is shared by dashboard and
142
+ # analytics, and both would otherwise rescue a NameError into a silent zero.
143
+ require "rails_error_dashboard/queries/event_volume"
136
144
  require "rails_error_dashboard/queries/dashboard_stats"
137
145
  require "rails_error_dashboard/queries/analytics_stats"
138
146
  require "rails_error_dashboard/queries/filter_options"
@@ -177,6 +185,32 @@ module RailsErrorDashboard
177
185
  end
178
186
  end
179
187
 
188
+ # The commit this process is running, read from .git once and remembered.
189
+ #
190
+ # LogError stamps every error with a SHA. When neither config.git_sha nor a
191
+ # platform ENV variable supplies one it used to run `git rev-parse` in a
192
+ # subprocess PER CAPTURED ERROR. The answer cannot change while the process
193
+ # lives, so it is resolved once (the engine does it at boot, so normally
194
+ # not even the first capture pays for it) and nil is remembered too: an app
195
+ # with no repository must not go looking again for every error.
196
+ #
197
+ # @return [String, nil] short SHA
198
+ def self.detected_git_sha
199
+ return @detected_git_sha if defined?(@detected_git_sha)
200
+
201
+ @detected_git_sha = begin
202
+ Services::GitHeadReader.call(defined?(Rails) && Rails.respond_to?(:root) ? Rails.root : nil)
203
+ rescue => e
204
+ RailsErrorDashboard::Logger.debug("[RailsErrorDashboard] Could not detect git SHA: #{e.class}: #{e.message}")
205
+ nil
206
+ end
207
+ end
208
+
209
+ # Forget the memo (specs).
210
+ def self.reset_detected_git_sha!
211
+ remove_instance_variable(:@detected_git_sha) if defined?(@detected_git_sha)
212
+ end
213
+
180
214
  # Register a plugin
181
215
  # @param plugin [Plugin] The plugin instance to register
182
216
  # @return [Boolean] True if registered successfully, false otherwise
@@ -459,6 +459,8 @@ namespace :error_dashboard do
459
459
  puts "\nDeleting errors..."
460
460
  start_time = Time.current
461
461
  deleted = scope.delete_all
462
+ # delete_all skips callbacks, and the stat cards are cached.
463
+ RailsErrorDashboard::Services::AnalyticsCacheManager.clear
462
464
  elapsed = (Time.current - start_time).round(2)
463
465
 
464
466
  puts "\n✓ Cleanup complete!"
@@ -492,7 +494,7 @@ namespace :error_dashboard do
492
494
  application_id: app.id,
493
495
  dump_data: dump.to_json,
494
496
  captured_at: Time.current,
495
- note: ENV["NOTE"]
497
+ note: RailsErrorDashboard::Services::EncodingSanitizer.scrub(ENV["NOTE"])
496
498
  )
497
499
 
498
500
  puts "\n" + JSON.pretty_generate(dump)
@@ -502,7 +504,7 @@ namespace :error_dashboard do
502
504
  puts "\n" + "=" * 70 + "\n"
503
505
  end
504
506
 
505
- desc "Run retention cleanup (delete errors older than retention_days)"
507
+ desc "Run retention cleanup (delete errors not seen for retention_days)"
506
508
  task retention_cleanup: :environment do
507
509
  config = RailsErrorDashboard.configuration
508
510
 
@@ -517,14 +519,15 @@ namespace :error_dashboard do
517
519
  end
518
520
 
519
521
  cutoff = config.retention_days.days.ago
520
- count = RailsErrorDashboard::ErrorLog.where("occurred_at < ?", cutoff).count
522
+ # The job's own selection, so the number shown is the number deleted.
523
+ count = RailsErrorDashboard::RetentionCleanupJob.expired_scope(cutoff).count
521
524
 
522
525
  puts "\n Retention policy: #{config.retention_days} days"
523
526
  puts " Cutoff date: #{cutoff.strftime('%Y-%m-%d %H:%M:%S')}"
524
527
  puts " Errors to delete: #{count}"
525
528
 
526
529
  if count.zero?
527
- puts "\n No errors older than #{config.retention_days} days"
530
+ puts "\n No errors unseen for more than #{config.retention_days} days"
528
531
  puts "\n" + "=" * 80 + "\n"
529
532
  next
530
533
  end
@@ -550,6 +553,53 @@ namespace :error_dashboard do
550
553
  puts "\n" + "=" * 80 + "\n"
551
554
  end
552
555
 
556
+ desc "Repair rows stored with invalid UTF-8 or NUL bytes (run once after upgrading on SQLite/MySQL)"
557
+ task scrub_invalid_encoding: :environment do
558
+ puts "Scanning error logs and occurrences for invalid bytes..."
559
+ result = RailsErrorDashboard::Commands::ScrubInvalidEncoding.call
560
+
561
+ puts " scanned: #{result[:scanned]}"
562
+ puts " repaired: #{result[:repaired]}"
563
+ puts " unreadable: #{result[:unreadable].size}"
564
+ result[:unreadable].each { |row| puts " - #{row}" }
565
+ end
566
+
567
+ desc "Backfill resolved_at for errors resolved through the status workflow (run once after upgrading)"
568
+ task backfill_resolved_at: :environment do
569
+ result = RailsErrorDashboard::Commands::BackfillResolvedAt.call
570
+
571
+ puts "Backfilled resolved_at on #{result[:updated]} resolved error(s)."
572
+ puts "MTTR now includes them, so expect the figure to rise to its true value." if result[:updated].positive?
573
+ end
574
+
575
+ desc "Replace raw session IDs stored on occurrences with keyed digests (idempotent)"
576
+ task digest_session_ids: :environment do
577
+ filter = RailsErrorDashboard::Services::SensitiveDataFilter
578
+ prefix = filter::SESSION_DIGEST_PREFIX
579
+ scope = RailsErrorDashboard::ErrorOccurrence
580
+ .where.not(session_id: [ nil, "" ])
581
+ .where("session_id NOT LIKE ? OR LENGTH(session_id) <> 35", "#{prefix}%")
582
+
583
+ digested = 0
584
+ failed = 0
585
+ scope.in_batches(of: 1000) do |batch|
586
+ batch.pluck(:id, :session_id).each do |id, raw|
587
+ digest = filter.digest_session_id(raw)
588
+ next if digest == raw # already a digest that the SQL prefilter let through
589
+
590
+ if digest
591
+ RailsErrorDashboard::ErrorOccurrence.where(id: id).update_all(session_id: digest)
592
+ digested += 1
593
+ else
594
+ failed += 1
595
+ end
596
+ end
597
+ end
598
+
599
+ puts "Session IDs digested: #{digested}"
600
+ puts "Could not be digested (left unchanged): #{failed}" if failed.positive?
601
+ end
602
+
553
603
  desc "Send error digest email (PERIOD=daily|weekly, APP_ID=optional)"
554
604
  task send_digest: :environment do
555
605
  period = ENV.fetch("PERIOD", "daily")
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_error_dashboard
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.1
4
+ version: 0.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Anjan Jagirdar
@@ -283,9 +283,11 @@ files:
283
283
  - app/jobs/rails_error_dashboard/baseline_calculation_job.rb
284
284
  - app/jobs/rails_error_dashboard/close_linked_issue_job.rb
285
285
  - app/jobs/rails_error_dashboard/concerns/localized_job.rb
286
+ - app/jobs/rails_error_dashboard/concerns/plain_channel_message.rb
286
287
  - app/jobs/rails_error_dashboard/create_issue_job.rb
287
288
  - app/jobs/rails_error_dashboard/discord_error_notification_job.rb
288
289
  - app/jobs/rails_error_dashboard/email_error_notification_job.rb
290
+ - app/jobs/rails_error_dashboard/notification_burst_summary_job.rb
289
291
  - app/jobs/rails_error_dashboard/pagerduty_error_notification_job.rb
290
292
  - app/jobs/rails_error_dashboard/rack_attack_flush_job.rb
291
293
  - app/jobs/rails_error_dashboard/reopen_linked_issue_job.rb
@@ -307,6 +309,8 @@ files:
307
309
  - app/models/rails_error_dashboard/error_log.rb
308
310
  - app/models/rails_error_dashboard/error_logs_record.rb
309
311
  - app/models/rails_error_dashboard/error_occurrence.rb
312
+ - app/models/rails_error_dashboard/event_count.rb
313
+ - app/models/rails_error_dashboard/event_timing_gap.rb
310
314
  - app/models/rails_error_dashboard/rack_attack_event.rb
311
315
  - app/models/rails_error_dashboard/storm_event.rb
312
316
  - app/models/rails_error_dashboard/storm_flush_batch.rb
@@ -417,6 +421,10 @@ files:
417
421
  - db/migrate/20260915000002_add_issue_repo_identity_to_error_logs.rb
418
422
  - db/migrate/20260915000003_add_context_provenance_to_error_logs.rb
419
423
  - db/migrate/20260915000004_create_storm_flush_batches.rb
424
+ - db/migrate/20260917000001_add_last_notified_at_to_error_logs.rb
425
+ - db/migrate/20260919000001_create_event_counts.rb
426
+ - db/migrate/20260920000001_add_buckets_incomplete_to_storm_events.rb
427
+ - db/migrate/20260920000002_create_event_timing_gaps.rb
420
428
  - lib/generators/rails_error_dashboard/install/install_generator.rb
421
429
  - lib/generators/rails_error_dashboard/install/templates/README
422
430
  - lib/generators/rails_error_dashboard/install/templates/initializer.rb
@@ -427,6 +435,7 @@ files:
427
435
  - lib/rails_error_dashboard/commands/add_error_comment.rb
428
436
  - lib/rails_error_dashboard/commands/assign_error.rb
429
437
  - lib/rails_error_dashboard/commands/backfill_environments.rb
438
+ - lib/rails_error_dashboard/commands/backfill_resolved_at.rb
430
439
  - lib/rails_error_dashboard/commands/batch_delete_errors.rb
431
440
  - lib/rails_error_dashboard/commands/batch_mute_errors.rb
432
441
  - lib/rails_error_dashboard/commands/batch_resolve_errors.rb
@@ -443,6 +452,7 @@ files:
443
452
  - lib/rails_error_dashboard/commands/log_error.rb
444
453
  - lib/rails_error_dashboard/commands/mute_error.rb
445
454
  - lib/rails_error_dashboard/commands/resolve_error.rb
455
+ - lib/rails_error_dashboard/commands/scrub_invalid_encoding.rb
446
456
  - lib/rails_error_dashboard/commands/snooze_error.rb
447
457
  - lib/rails_error_dashboard/commands/unassign_error.rb
448
458
  - lib/rails_error_dashboard/commands/unmute_error.rb
@@ -485,6 +495,7 @@ files:
485
495
  - lib/rails_error_dashboard/queries/error_cascades.rb
486
496
  - lib/rails_error_dashboard/queries/error_correlation.rb
487
497
  - lib/rails_error_dashboard/queries/errors_list.rb
498
+ - lib/rails_error_dashboard/queries/event_volume.rb
488
499
  - lib/rails_error_dashboard/queries/filter_options.rb
489
500
  - lib/rails_error_dashboard/queries/job_health_summary.rb
490
501
  - lib/rails_error_dashboard/queries/llm_health_summary.rb
@@ -517,6 +528,7 @@ files:
517
528
  - lib/rails_error_dashboard/services/diagnostic_dump_generator.rb
518
529
  - lib/rails_error_dashboard/services/digest_builder.rb
519
530
  - lib/rails_error_dashboard/services/discord_payload_builder.rb
531
+ - lib/rails_error_dashboard/services/encoding_sanitizer.rb
520
532
  - lib/rails_error_dashboard/services/environment_snapshot.rb
521
533
  - lib/rails_error_dashboard/services/error_broadcaster.rb
522
534
  - lib/rails_error_dashboard/services/error_hash_generator.rb
@@ -524,6 +536,7 @@ files:
524
536
  - lib/rails_error_dashboard/services/error_notification_dispatcher.rb
525
537
  - lib/rails_error_dashboard/services/exception_filter.rb
526
538
  - lib/rails_error_dashboard/services/git_blame_reader.rb
539
+ - lib/rails_error_dashboard/services/git_head_reader.rb
527
540
  - lib/rails_error_dashboard/services/github_issue_client.rb
528
541
  - lib/rails_error_dashboard/services/github_link_generator.rb
529
542
  - lib/rails_error_dashboard/services/gitlab_issue_client.rb
@@ -558,6 +571,7 @@ files:
558
571
  - lib/rails_error_dashboard/services/storm_protection/gate.rb
559
572
  - lib/rails_error_dashboard/services/swallowed_exception_tracker.rb
560
573
  - lib/rails_error_dashboard/services/system_health_snapshot.rb
574
+ - lib/rails_error_dashboard/services/url_safety.rb
561
575
  - lib/rails_error_dashboard/services/variable_serializer.rb
562
576
  - lib/rails_error_dashboard/services/webhook_payload_builder.rb
563
577
  - lib/rails_error_dashboard/subscribers/action_cable_subscriber.rb
@@ -585,7 +599,7 @@ metadata:
585
599
  funding_uri: https://github.com/sponsors/AnjanJ
586
600
  post_install_message: |
587
601
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
588
- RED (Rails Error Dashboard) v0.12.1
602
+ RED (Rails Error Dashboard) v0.14.0
589
603
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
590
604
 
591
605
  First install: