rails_error_dashboard 0.14.3 → 0.14.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f96e195659bca5562fcf7c4ed02c1f8f69c058daec06f6902523958f5746a8a4
4
- data.tar.gz: 2ea940fe412652bef9e0f47eb818d4da93a6d7b7cb1e32cd5820e96cf032fafe
3
+ metadata.gz: bd115b4f0fa37f0b90cc22ceea5d5f8db5bdb59ceb40972c3d9ff70c6055a643
4
+ data.tar.gz: b1339930832c6e108d0e55aae93ad2cf14ed1935fa5af23bee24b9f9f4d8943d
5
5
  SHA512:
6
- metadata.gz: b7b8a36c5ffde9bcd71a0649a0d2df111345625717ec67a997e4bd4929215700408efd95844b41506e3a48aa47fc7179cd1df140aa1274451a1a8760a5f32db7
7
- data.tar.gz: d6b3722433ce12451ef44242874dfe98620441832626368d0e8ce9d852396d8e4b00b5cc5773f04a348d48f64254fc763cd4fae4a1580d5b399cb4aab21f3de3
6
+ metadata.gz: 9901f9f6fcc64cf53f71cdf43545e2314fe410638eafb414619159d9955763e8251007b13aa5d02e3967ebfbe4b87fb6dff3700694d27031d67ed39c8281e8ac
7
+ data.tar.gz: d039b6f3796fad5884ae83bbe374671d5d2f90137747c0f404fd917cbcf8088f3b4101caf5c9b77524562b4e4c2c2ca3581d99abadfc16797d9bd35122c83afc
data/README.md CHANGED
@@ -19,7 +19,7 @@ rails generate rails_error_dashboard:install
19
19
  rails db:migrate
20
20
  ```
21
21
 
22
- Open `/red` and raise a test exception. No monitoring account or ingestion service is required.
22
+ Open `/red`, then send a test error from its Settings page. No monitoring account or ingestion service is required.
23
23
 
24
24
  [Try the live demo](https://rails-error-dashboard.anjan.dev) (`gandalf` / `youshallnotpass`) · [Read the documentation](https://anjanj.github.io/rails_error_dashboard/) · [View on RubyGems](https://rubygems.org/gems/rails_error_dashboard)
25
25
 
@@ -91,7 +91,7 @@ That makes RED a self-hosted Sentry alternative for teams that want Rails-specif
91
91
 
92
92
  - Store data in the application's existing PostgreSQL, MySQL/Trilogy or SQLite database.
93
93
  - Isolate monitoring writes in a separate error database.
94
- - Use synchronous writes, or async logging through Sidekiq or Solid Queue (GoodJob is detected for job-health stats but is not an async adapter).
94
+ - Use synchronous writes, or async logging through your app's own Active Job backend (Sidekiq, Solid Queue, GoodJob or Rails' in-process `:async`).
95
95
  - Track several Rails applications through a shared database.
96
96
 
97
97
  No RED licence or event-ingestion fee, and no plan limits — your database is the only cap, and storm protection deliberately sheds context during floods.
@@ -619,6 +619,11 @@ Span names follow the `rails_error_dashboard.<operation>` convention, e.g. `rail
619
619
  gem 'rails_error_dashboard'
620
620
  ```
621
621
 
622
+ RED requires Rails 7.0 or newer, Ruby 3.2 or newer, `pagy ~> 43` and `groupdate ~> 6`. An app that pins an older Pagy or groupdate must upgrade it first, or `bundle install` fails.
623
+
624
+ - **Rails before 8.1.4 or 7.2.4** (every 8.0, 7.1 and 7.0 release): also add `gem "json", "< 3"`. json 3 breaks those Rails versions, and the dashboard returns 500. See [the json pin](docs/UPGRADING.md#rails-before-814-or-724-pin-json-below-3).
625
+ - **MySQL**: load the time-zone tables (`mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root mysql`), or the charts fail.
626
+
622
627
  ### 2. Install with Interactive Setup
623
628
 
624
629
  ```bash
@@ -627,7 +632,9 @@ rails generate rails_error_dashboard:install
627
632
  rails db:migrate
628
633
  ```
629
634
 
630
- The installer guides you through optional feature selection — notifications, performance optimizations, advanced analytics. All features are opt-in.
635
+ The installer asks about notifications and advanced analytics, then where to store errors: in your app's database (the default), a separate one, or one shared by several apps. Without a terminal (CI, Docker) it asks nothing: async logging is on, and every other optional feature is off.
636
+
637
+ **Chose a separate or shared database?** Add the `config/database.yml` entry the installer prints, then run `bin/rails db:create` before `db:migrate`. See [Database Options](docs/guides/DATABASE_OPTIONS.md).
631
638
 
632
639
  ### 3. Visit your dashboard
633
640
 
@@ -637,17 +644,30 @@ http://localhost:3000/red
637
644
 
638
645
  Default credentials: `gandalf` / `youshallnotpass`, for development and test only.
639
646
 
640
- **Before you deploy anywhere else**, set `ERROR_DASHBOARD_USER` and `ERROR_DASHBOARD_PASSWORD` in that environment. Outside development and test, the app refuses to boot on the defaults. See [Dashboard Credentials](docs/guides/CONFIGURATION.md#dashboard-credentials).
647
+ **Before you deploy anywhere else**, set `ERROR_DASHBOARD_USER` and `ERROR_DASHBOARD_PASSWORD` in that environment. Outside development and test, the app refuses to boot on the defaults. See [Dashboard Credentials](docs/guides/CONFIGURATION.md#dashboard-credentials). For the rest of what production needs (a worker, scheduled jobs, migrations on deploy), see [Running in Production](docs/PRODUCTION.md).
641
648
 
642
649
  ### 4. Test it out
643
650
 
651
+ Open `/red/settings` and click **Send Test Error**, or raise an exception in any controller action. Errors raised at the Rails console prompt are not captured. To report one from the console, use:
652
+
644
653
  ```ruby
645
- # In Rails console or any controller
646
- raise "Test error from Rails Error Dashboard!"
654
+ Rails.error.report(RuntimeError.new("Test error from Rails Error Dashboard"), handled: false)
647
655
  ```
648
656
 
657
+ With async logging on (the default), leave the console open for a second or two so the background job can write it.
658
+
649
659
  [Full installation guide →](docs/QUICKSTART.md)
650
660
 
661
+ ### Upgrading
662
+
663
+ ```bash
664
+ bundle update rails_error_dashboard
665
+ bin/rails generate rails_error_dashboard:install --no-interactive
666
+ bin/rails db:migrate
667
+ ```
668
+
669
+ The installer keeps your initializer and copies only the new migrations. Some releases need a step of their own, and two of them can stop an app booting: [read the upgrade guide](docs/UPGRADING.md) before you deploy.
670
+
651
671
  ---
652
672
 
653
673
  ## Configuration
@@ -661,8 +681,7 @@ RailsErrorDashboard.configure do |config|
661
681
  # Optional features — enable as needed
662
682
  config.enable_slack_notifications = true
663
683
  config.slack_webhook_url = ENV['SLACK_WEBHOOK_URL']
664
- config.async_logging = true
665
- config.async_adapter = :sidekiq # or :solid_queue, :async
684
+ config.async_logging = true # jobs run on your app's Active Job adapter; production needs a worker
666
685
  end
667
686
  ```
668
687
 
@@ -670,7 +689,7 @@ end
670
689
 
671
690
  **Multi-App Support** — Track errors from multiple Rails apps in a single shared database. Auto-detects app name, supports per-app filtering. [Multi-App guide →](docs/MULTI_APP_PERFORMANCE.md)
672
691
 
673
- **OpenTelemetry Export** — Emit error-capture operations as OTel spans to Datadog, Honeycomb, or Jaeger. Add `gem "opentelemetry-api"` and set `config.enable_otel_export = true`. See [OpenTelemetry Export](#opentelemetry-export--emit-gem-operations-as-spans) above for full options.
692
+ **OpenTelemetry Export** — Emit error-capture operations as OTel spans to Datadog, Honeycomb, or Jaeger. Add `gem "opentelemetry-api"` and set `config.enable_otel_export = true`. See **OpenTelemetry Export** under [Features](#features) above for the full options.
674
693
 
675
694
  ---
676
695
 
@@ -711,7 +730,7 @@ RED translates through its own private I18n backend, so it never reads, writes o
711
730
  ## FAQ
712
731
 
713
732
  **Does Rails Error Dashboard support a separate database for errors?**
714
- Yes. You can store errors in your app's existing database (shared) **or** in a dedicated, isolated database (separate). Set `config.use_separate_database = true` (or `USE_SEPARATE_ERROR_DB=true`) and point it at a separate connection — the engine routes all of its tables through `connects_to`, keeping error data fully isolated from your app data. Both modes are first-class and covered by the [Database Options guide](docs/guides/DATABASE_OPTIONS.md).
733
+ Yes. You can store errors in your app's existing database **or** in a dedicated one. Choose it at the installer's database prompt (or pass `--separate-database`), then add the `error_dashboard` entry it prints to `config/database.yml` for every environment. The engine routes all of its tables through `connects_to`, keeping error data isolated from your app data. Both modes are first-class and covered by the [Database Options guide](docs/guides/DATABASE_OPTIONS.md).
715
734
 
716
735
  **Which databases does it work with?**
717
736
  SQLite, PostgreSQL, and MySQL/Trilogy — in either shared or separate-database mode.
@@ -726,13 +745,13 @@ Yes — local **and** instance variables at the moment the exception is raised,
726
745
  No. Storm protection (a circuit breaker with adaptive sampling, **ON by default**) makes the gem degrade itself first during error floods — occurrence counts stay exact while it sheds the expensive work, and a Storm History page shows exactly what was shed. There is no I/O on the hot path — the check is a digest and an atomic increment.
727
746
 
728
747
  **Does it work with my background jobs?**
729
- Yes — errors raised in jobs are captured, and it can log errors asynchronously through Sidekiq or SolidQueue (or the in-process `:async` adapter). Sidekiq, SolidQueue and GoodJob are all auto-detected for the job-queue stats stored on each error.
748
+ Yes — errors raised in jobs are captured, and it can log errors asynchronously through your app's Active Job adapter (Sidekiq, Solid Queue, GoodJob or the in-process `:async`). In production, run a worker for the `default` and `error_notifications` queues. Sidekiq, Solid Queue and GoodJob are all auto-detected for the job-queue stats stored on each error.
730
749
 
731
750
  **Does it work with my authentication?**
732
751
  Yes — HTTP Basic Auth out of the box, or a custom `authenticate_with` lambda that integrates with Devise, Warden, or session-based auth.
733
752
 
734
753
  **Can it track more than one app?**
735
- Yes — multi-app support tracks errors from multiple Rails apps in one dashboard with per-app filtering.
754
+ Yes — apps that point at the same error database share one dashboard, with per-app filtering.
736
755
 
737
756
  **What Rails and Ruby versions are supported?**
738
757
  Rails 7.0–8.1 and Ruby 3.2–4.0.
@@ -744,6 +763,8 @@ Rails 7.0–8.1 and Ruby 3.2–4.0.
744
763
  ### Getting Started
745
764
  - **[Quickstart Guide](docs/QUICKSTART.md)** — 5-minute setup
746
765
  - **[Configuration](docs/guides/CONFIGURATION.md)** — All configuration options
766
+ - **[Upgrading](docs/UPGRADING.md)** — The upgrade, and the releases that need a step
767
+ - **[Running in Production](docs/PRODUCTION.md)** — A worker, scheduled jobs and the other things RED needs before the first deploy
747
768
  - **[Uninstalling](docs/UNINSTALL.md)** — Clean removal
748
769
 
749
770
  ### Features
@@ -1,5 +1,13 @@
1
1
  module RailsErrorDashboard
2
2
  module ApplicationHelper
3
+ # Problems in config/queue.yml that stop RED's jobs in this environment:
4
+ # empty when the config is fine, or when Solid Queue doesn't run RED's jobs.
5
+ def red_solid_queue_problems
6
+ Services::SolidQueueConfigCheck.current_problems
7
+ rescue StandardError
8
+ []
9
+ end
10
+
3
11
  # Returns the host app's CSP nonce (if any) so inline <script> tags pass strict CSP.
4
12
  # Falls back to nil when the host has no CSP configured — in that case the script tag
5
13
  # works without a nonce attribute. Strict CSPs (script-src 'self' 'nonce-...') require
@@ -1335,6 +1335,19 @@ tr[data-red-row-href]:hover .sev-bar { opacity: 1 !important; }
1335
1335
  </div>
1336
1336
  <script<%= " nonce=\"#{red_csp_nonce}\"".html_safe if red_csp_nonce %>>try { if (sessionStorage.getItem('red_dismiss_creds_warning') === '1') { document.getElementById('security-warning').style.display = 'none'; } } catch(e) {}</script>
1337
1337
  <% end %>
1338
+ <% solid_queue_problems = red_solid_queue_problems %>
1339
+ <% if solid_queue_problems.any? %>
1340
+ <div id="solid-queue-warning" class="alert alert-warning" style="display: flex; align-items: flex-start; gap: 10px; margin-top: var(--space-2); margin-bottom: var(--space-4); border-left: 4px solid var(--status-warning);">
1341
+ <i class="bi bi-exclamation-triangle-fill" style="font-size: 18px; flex-shrink: 0;"></i>
1342
+ <div style="flex: 1;">
1343
+ <strong><%= red_t("red.banners.solid_queue.label") %></strong> <%= red_t("red.banners.solid_queue.body_html") %>
1344
+ <ul style="margin: var(--space-2) 0; padding-left: var(--space-5); font-family: var(--font-mono); font-size: 12px;">
1345
+ <% solid_queue_problems.each do |problem| %><li><%= problem %></li><% end %>
1346
+ </ul>
1347
+ <a href="<%= RailsErrorDashboard::Services::SolidQueueConfigCheck::GUIDE_URL %>" target="_blank" rel="noopener"><%= red_t("red.banners.solid_queue.guide") %></a>
1348
+ </div>
1349
+ </div>
1350
+ <% end %>
1338
1351
  <% if defined?(@storm_banner_event) && @storm_banner_event %>
1339
1352
  <% storm_active = @storm_banner_event.active? %>
1340
1353
  <div id="storm-banner" class="alert <%= storm_active ? 'alert-danger' : 'alert-info' %>" style="display: flex; align-items: center; gap: 10px; margin-top: var(--space-2); margin-bottom: var(--space-4); border-left: 4px solid <%= storm_active ? 'var(--status-critical)' : 'var(--status-info)' %>;">
@@ -87,6 +87,10 @@ de:
87
87
  <code>ERROR_DASHBOARD_PASSWORD</code> oder konfigurieren Sie <code>authenticate_with</code>
88
88
  in Ihrem Initializer.
89
89
  dismiss: Für diese Sitzung ausblenden
90
+ solid_queue:
91
+ label: "Solid Queue führt die Jobs von RED nicht aus."
92
+ body_html: "Im Hintergrund erfasste Fehler und Benachrichtigungen warten in der Warteschlange, bis <code>config/queue.yml</code> korrigiert ist. Führen Sie <code>bin/rails error_dashboard:verify</code> aus, um die Korrektur zu sehen, und starten Sie die App danach neu."
93
+ guide: "Was RED von Solid Queue braucht"
90
94
  storm:
91
95
  active_title: Fehlersturm im Gange
92
96
  recent_title: Sturmschutz war kürzlich aktiv
@@ -155,6 +155,11 @@ en:
155
155
  initializer.
156
156
  dismiss: "Dismiss for this session"
157
157
 
158
+ solid_queue:
159
+ # Shown when Solid Queue runs RED's jobs with a config/queue.yml that never would.
160
+ label: "Solid Queue won't run RED's jobs."
161
+ body_html: "Errors captured in the background and notifications wait in the queue until <code>config/queue.yml</code> is fixed. Run <code>bin/rails error_dashboard:verify</code> for the fix, then restart."
162
+ guide: "What RED needs from Solid Queue"
158
163
  storm:
159
164
  # Whole sentences, not fragments. The English original assembled these
160
165
  # from a stem plus a conditional clause, which cannot be reordered into
@@ -86,6 +86,10 @@ es:
86
86
  Defina las variables de entorno <code>ERROR_DASHBOARD_USER</code> y <code>ERROR_DASHBOARD_PASSWORD</code>,
87
87
  o configure <code>authenticate_with</code> en su initializer.
88
88
  dismiss: Ocultar durante esta sesión
89
+ solid_queue:
90
+ label: "Solid Queue no ejecutará los trabajos de RED."
91
+ body_html: "Los errores capturados en segundo plano y las notificaciones esperan en la cola hasta que se corrija <code>config/queue.yml</code>. Ejecute <code>bin/rails error_dashboard:verify</code> para ver la corrección y luego reinicie."
92
+ guide: "Lo que RED necesita de Solid Queue"
89
93
  storm:
90
94
  active_title: Tormenta de errores en curso
91
95
  recent_title: La protección ante tormentas se activó recientemente
@@ -87,6 +87,10 @@ fr:
87
87
  et <code>ERROR_DASHBOARD_PASSWORD</code>, ou configurez <code>authenticate_with</code>
88
88
  dans votre initializer.
89
89
  dismiss: Masquer pour cette session
90
+ solid_queue:
91
+ label: "Solid Queue n'exécutera pas les tâches de RED."
92
+ body_html: "Les erreurs capturées en arrière-plan et les notifications restent en file d'attente tant que <code>config/queue.yml</code> n'est pas corrigé. Lancez <code>bin/rails error_dashboard:verify</code> pour voir la correction, puis redémarrez."
93
+ guide: "Ce dont RED a besoin de Solid Queue"
90
94
  storm:
91
95
  active_title: Tempête d'erreurs en cours
92
96
  recent_title: La protection anti-tempête s'est déclenchée récemment
@@ -86,6 +86,10 @@ it:
86
86
  Imposta le variabili d'ambiente <code>ERROR_DASHBOARD_USER</code> e <code>ERROR_DASHBOARD_PASSWORD</code>
87
87
  oppure configura <code>authenticate_with</code> nel tuo inizializzatore.
88
88
  dismiss: Nascondi per questa sessione
89
+ solid_queue:
90
+ label: "Solid Queue non eseguirà i job di RED."
91
+ body_html: "Gli errori acquisiti in background e le notifiche restano in coda finché <code>config/queue.yml</code> non viene corretto. Esegui <code>bin/rails error_dashboard:verify</code> per vedere la correzione, poi riavvia."
92
+ guide: "Cosa serve a RED da Solid Queue"
89
93
  storm:
90
94
  active_title: Tempesta di errori in corso
91
95
  recent_title: Protezione dalle tempeste attivata di recente
@@ -86,6 +86,10 @@ ja:
86
86
  と <code>ERROR_DASHBOARD_PASSWORD</code> を設定するか、イニシャライザで <code>authenticate_with</code>
87
87
  を設定してください。
88
88
  dismiss: このセッションでは表示しない
89
+ solid_queue:
90
+ label: "Solid Queue は RED のジョブを実行しません。"
91
+ body_html: "<code>config/queue.yml</code> を修正するまで、バックグラウンドで記録されたエラーと通知はキューで待機します。<code>bin/rails error_dashboard:verify</code> を実行して修正方法を確認し、再起動してください。"
92
+ guide: "RED が Solid Queue に必要とするもの"
89
93
  storm:
90
94
  active_title: エラーストームが発生中です
91
95
  recent_title: ストーム保護が最近作動しました
@@ -86,6 +86,10 @@ pl:
86
86
  Ustaw zmienne środowiskowe <code>ERROR_DASHBOARD_USER</code> i <code>ERROR_DASHBOARD_PASSWORD</code>
87
87
  albo skonfiguruj <code>authenticate_with</code> w swoim inicjalizatorze.
88
88
  dismiss: Ukryj na czas tej sesji
89
+ solid_queue:
90
+ label: "Solid Queue nie uruchomi zadań RED."
91
+ body_html: "Błędy przechwycone w tle i powiadomienia czekają w kolejce, dopóki <code>config/queue.yml</code> nie zostanie poprawiony. Uruchom <code>bin/rails error_dashboard:verify</code>, aby zobaczyć poprawkę, a następnie zrestartuj aplikację."
92
+ guide: "Czego RED potrzebuje od Solid Queue"
89
93
  storm:
90
94
  active_title: Trwa sztorm błędów
91
95
  recent_title: Ochrona przed sztormem zadziałała niedawno
@@ -86,6 +86,10 @@ pt-BR:
86
86
  Defina as variáveis de ambiente <code>ERROR_DASHBOARD_USER</code> e <code>ERROR_DASHBOARD_PASSWORD</code>,
87
87
  ou configure <code>authenticate_with</code> no seu initializer.
88
88
  dismiss: Ocultar nesta sessão
89
+ solid_queue:
90
+ label: "O Solid Queue não executará os jobs do RED."
91
+ body_html: "Erros capturados em segundo plano e notificações aguardam na fila até que <code>config/queue.yml</code> seja corrigido. Execute <code>bin/rails error_dashboard:verify</code> para ver a correção e reinicie."
92
+ guide: "O que o RED precisa do Solid Queue"
89
93
  storm:
90
94
  active_title: Tempestade de erros em andamento
91
95
  recent_title: A proteção contra tempestades foi acionada recentemente
@@ -86,6 +86,10 @@ ru:
86
86
  Задайте переменные окружения <code>ERROR_DASHBOARD_USER</code> и <code>ERROR_DASHBOARD_PASSWORD</code>
87
87
  или настройте <code>authenticate_with</code> в инициализаторе.
88
88
  dismiss: Скрыть до конца сессии
89
+ solid_queue:
90
+ label: "Solid Queue не будет выполнять задания RED."
91
+ body_html: "Ошибки, записанные в фоне, и уведомления ждут в очереди, пока не исправлен <code>config/queue.yml</code>. Запустите <code>bin/rails error_dashboard:verify</code>, чтобы увидеть исправление, затем перезапустите приложение."
92
+ guide: "Что RED нужно от Solid Queue"
89
93
  storm:
90
94
  active_title: Идёт шторм ошибок
91
95
  recent_title: Недавно сработала защита от шторма
@@ -86,6 +86,10 @@ uk:
86
86
  Встановіть змінні середовища <code>ERROR_DASHBOARD_USER</code> і <code>ERROR_DASHBOARD_PASSWORD</code>
87
87
  або налаштуйте <code>authenticate_with</code> у своєму ініціалізаторі.
88
88
  dismiss: Приховати для цієї сесії
89
+ solid_queue:
90
+ label: "Solid Queue не виконуватиме завдання RED."
91
+ body_html: "Помилки, записані у фоні, і сповіщення чекають у черзі, доки не виправлено <code>config/queue.yml</code>. Запустіть <code>bin/rails error_dashboard:verify</code>, щоб побачити виправлення, потім перезапустіть застосунок."
92
+ guide: "Що RED потрібно від Solid Queue"
89
93
  storm:
90
94
  active_title: Триває шторм помилок
91
95
  recent_title: Нещодавно спрацював захист від штормів
@@ -85,6 +85,10 @@ zh-CN:
85
85
  body_html: 你正在使用默认凭据(gandalf/youshallnotpass)。请设置环境变量 <code>ERROR_DASHBOARD_USER</code>
86
86
  和 <code>ERROR_DASHBOARD_PASSWORD</code>,或在初始化文件中配置 <code>authenticate_with</code>。
87
87
  dismiss: 本次会话内不再提示
88
+ solid_queue:
89
+ label: "Solid Queue 不会运行 RED 的任务。"
90
+ body_html: "在修复 <code>config/queue.yml</code> 之前,后台捕获的错误和通知会一直在队列中等待。运行 <code>bin/rails error_dashboard:verify</code> 查看修复方法,然后重启应用。"
91
+ guide: "RED 对 Solid Queue 的要求"
88
92
  storm:
89
93
  active_title: 错误风暴进行中
90
94
  recent_title: 近期触发过风暴保护
@@ -3,6 +3,15 @@ class RemoveEnvironmentFromErrorLogs < ActiveRecord::Migration[7.0]
3
3
  # Skip if squashed migration ran (column never existed) or already removed
4
4
  return unless column_exists?(:rails_error_dashboard_error_logs, :environment)
5
5
 
6
+ # Only the v0.1.x column is removed: it was NOT NULL. The column
7
+ # AddEnvironmentToErrorLogs adds back (0.11.0) is nullable and holds real
8
+ # data. This migration replays over it whenever RED's migrations run
9
+ # against tables that already exist (an app joining a shared error
10
+ # database runs its own renumbered copy of every migration), and dropping
11
+ # it there wiped every error's environment.
12
+ environment = columns(:rails_error_dashboard_error_logs).find { |column| column.name == "environment" }
13
+ return if environment.nil? || environment.null
14
+
6
15
  # Remove composite index first
7
16
  remove_index :rails_error_dashboard_error_logs,
8
17
  name: 'index_error_logs_on_environment_and_occurred_at',
@@ -58,7 +58,7 @@ module RailsErrorDashboard
58
58
 
59
59
  if quick_mode?
60
60
  @selected_features = build_quick_defaults
61
- say " Using sensible defaults (analytics ON, notifications OFF, breadcrumbs OFF)", :green
61
+ say " Using sensible defaults (analytics, breadcrumbs ON, system health ON, 50% sampling; notifications OFF)", :green
62
62
  return
63
63
  end
64
64
 
@@ -131,7 +131,10 @@ module RailsErrorDashboard
131
131
  advanced_keys = %i[async_logging error_sampling breadcrumbs system_health
132
132
  source_code_integration git_blame swallowed_exceptions
133
133
  crash_capture diagnostic_dump]
134
- any_advanced_cli_flag = advanced_keys.any? { |k| options[k] }
134
+ # A flag the user passed, not an option left at its default: async_logging
135
+ # defaults to true, so "is any of these set?" was always yes and this
136
+ # question was never asked.
137
+ any_advanced_cli_flag = advanced_keys.any? { |k| options[k] != self.class.class_options[k]&.default }
135
138
 
136
139
  say "\n[3/3] Advanced Options (performance tuning & diagnostics)", :cyan
137
140
  say " Async logging, error sampling, breadcrumbs, system health,", :white
@@ -169,8 +172,10 @@ module RailsErrorDashboard
169
172
  advanced_features.each do |f|
170
173
  say "\n #{f[:name]}", :cyan
171
174
  say " #{f[:desc]}", :white
172
- r = ask(" Enable? (y/N):", :yellow, limited_to: [ "y", "Y", "n", "N", "" ])
173
- @selected_features[f[:key]] = r.downcase == "y"
175
+ # Async logging is on unless declined, as it is without this question.
176
+ default_on = f[:key] == :async_logging
177
+ r = ask(" Enable? (#{default_on ? 'Y/n' : 'y/N'}):", :yellow, limited_to: [ "y", "Y", "n", "N", "" ])
178
+ @selected_features[f[:key]] = default_on ? r.downcase != "n" : r.downcase == "y"
174
179
  if @selected_features[f[:key]] && f[:key] == :swallowed_exceptions && RUBY_VERSION < "3.3"
175
180
  say " ⚠ Requires Ruby 3.3+ (you have #{RUBY_VERSION}) — will activate after upgrade", :yellow
176
181
  end
@@ -293,7 +298,9 @@ module RailsErrorDashboard
293
298
  @enable_webhooks = @selected_features&.dig(:webhooks) || options[:webhooks]
294
299
 
295
300
  # Performance
296
- @enable_async_logging = @selected_features&.dig(:async_logging) || options[:async_logging]
301
+ # An answer, when there was one, wins: `answer || default` turned a "no"
302
+ # into the default's "yes".
303
+ @enable_async_logging = @selected_features&.key?(:async_logging) ? @selected_features[:async_logging] : options[:async_logging]
297
304
  @enable_error_sampling = @selected_features&.dig(:error_sampling) || options[:error_sampling]
298
305
 
299
306
  # Database mode (set by select_database_mode or CLI flags)
@@ -493,12 +500,12 @@ module RailsErrorDashboard
493
500
  say "Next Steps:", :cyan
494
501
  if @enable_separate_database
495
502
  say " 1. Add the database.yml entry shown above"
496
- say " 2. Run: rails db:create:error_dashboard"
503
+ say " 2. Run: rails db:create:#{@database_name}"
497
504
  if @enable_multi_app
498
505
  say " 3. Run migrations (only needed on the FIRST app):"
499
- say " rails db:migrate:error_dashboard"
506
+ say " rails db:migrate:#{@database_name}"
500
507
  else
501
- say " 3. Run: rails db:migrate:error_dashboard"
508
+ say " 3. Run: rails db:migrate:#{@database_name}"
502
509
  end
503
510
  say " 4. Before deploying: set ERROR_DASHBOARD_USER and ERROR_DASHBOARD_PASSWORD there"
504
511
  say " 5. Restart your Rails server"
@@ -595,33 +602,23 @@ module RailsErrorDashboard
595
602
  say " # Separate error database", :white
596
603
  end
597
604
 
598
- say "\n development:", :cyan
599
- say " primary:", :white
600
- say " <<: *default", :white
601
- say " database: #{app_name_snake}_development", :white
602
- say " error_dashboard:", :white
603
- say " <<: *default", :white
604
- if @enable_multi_app
605
- shared_db_base = @shared_db_name || "shared_errors"
606
- say " database: #{shared_db_base}_development", :white
607
- else
608
- say " database: #{app_name_snake}_errors_development", :white
609
- end
610
- say " migrations_paths: db/error_dashboard_migrate", :white
611
-
612
- say "\n production:", :cyan
613
- say " primary:", :white
614
- say " <<: *default", :white
615
- say " database: #{app_name_snake}_production", :white
616
- say " error_dashboard:", :white
617
- say " <<: *default", :white
618
- if @enable_multi_app
619
- shared_db_base = @shared_db_name || "shared_errors"
620
- say " database: #{shared_db_base}_production", :white
621
- else
622
- say " database: #{app_name_snake}_errors_production", :white
605
+ # Every environment the app boots in needs the entry, test included:
606
+ # without it RED falls back to the main database and records nothing.
607
+ %w[development test production].each do |env|
608
+ say "\n #{env}:", :cyan
609
+ say " primary:", :white
610
+ say " <<: *default", :white
611
+ say " database: #{app_name_snake}_#{env}", :white
612
+ say " #{@database_name}:", :white
613
+ say " <<: *default", :white
614
+ if @enable_multi_app
615
+ shared_db_base = @shared_db_name || "shared_errors"
616
+ say " database: #{shared_db_base}_#{env}", :white
617
+ else
618
+ say " database: #{app_name_snake}_errors_#{env}", :white
619
+ end
620
+ say " migrations_paths: db/error_dashboard_migrate", :white
623
621
  end
624
- say " migrations_paths: db/error_dashboard_migrate", :white
625
622
  say "\n"
626
623
 
627
624
  if @enable_multi_app
@@ -186,7 +186,8 @@ RailsErrorDashboard.configure do |config|
186
186
  <% if @enable_error_sampling -%>
187
187
  # Error Sampling - ENABLED
188
188
  # Samples non-critical errors to reduce storage volume.
189
- # Critical and high severity errors are ALWAYS logged at 100% regardless of this setting.
189
+ # Critical errors are always logged, regardless of this setting. So is the first
190
+ # occurrence of each error in each process.
190
191
  # 0.5 = log 50% of non-critical occurrences — halves storage while keeping
191
192
  # occurrence counts meaningful and error patterns visible.
192
193
  # Tune lower (e.g. 0.1) if one error is flooding the DB; set to 1.0 to log everything.
@@ -244,7 +245,7 @@ RailsErrorDashboard.configure do |config|
244
245
  # Automatically detect when error rates exceed normal patterns
245
246
  config.enable_baseline_alerts = true
246
247
  config.baseline_alert_threshold_std_devs = 2.0 # Alert when > 2 std devs above baseline
247
- config.baseline_alert_severities = [ :critical, :high ]
248
+ config.baseline_alert_severities = [ :critical, :high ] # Only errors of these severities alert
248
249
  config.baseline_alert_cooldown_minutes = 120 # 2 hours between alerts
249
250
  # To disable: Set config.enable_baseline_alerts = false
250
251
 
@@ -253,7 +254,7 @@ RailsErrorDashboard.configure do |config|
253
254
  # To enable: Set config.enable_baseline_alerts = true
254
255
  config.enable_baseline_alerts = false
255
256
  # config.baseline_alert_threshold_std_devs = 2.0
256
- # config.baseline_alert_severities = [ :critical, :high ]
257
+ # config.baseline_alert_severities = [ :critical, :high ] # Only errors of these severities alert
257
258
  # config.baseline_alert_cooldown_minutes = 120
258
259
 
259
260
  <% end -%>
@@ -236,13 +236,22 @@ module RailsErrorDashboard
236
236
  say "Remaining manual steps:", :cyan
237
237
  say "\n"
238
238
 
239
- say "1. Remove from Gemfile:", :yellow
239
+ unless options[:keep_data]
240
+ say "1. Regenerate the schema file:", :yellow
241
+ say " Run: bin/rails db:schema:dump (otherwise db:schema:load recreates the tables)"
242
+ say "\n"
243
+
244
+ say "2. Separate database only: remove its entry from config/database.yml", :yellow
245
+ say "\n"
246
+ end
247
+
248
+ say "#{options[:keep_data] ? 1 : 3}. Remove from Gemfile:", :yellow
240
249
  say " Open: Gemfile"
241
250
  say " Remove: gem 'rails_error_dashboard'"
242
251
  say " Run: bundle install"
243
252
  say "\n"
244
253
 
245
- say "2. Restart your application:", :yellow
254
+ say "#{options[:keep_data] ? 2 : 4}. Restart your application:", :yellow
246
255
  say " Run: rails restart"
247
256
  say " Or: kill and restart your server process"
248
257
  say "\n"
@@ -878,8 +878,11 @@ module RailsErrorDashboard
878
878
  # Return if no anomaly detected
879
879
  return unless anomaly[:anomaly]
880
880
 
881
- # Check if severity level should trigger alert
882
- return unless config.baseline_alert_severities.include?(anomaly[:level])
881
+ # baseline_alert_severities lists ERROR severities (critical/high/
882
+ # medium/low, as validated), not anomaly levels. Comparing it with
883
+ # anomaly[:level] (elevated/high/critical) skipped every anomaly below
884
+ # threshold + 1 sigma, and :medium/:low never matched at all.
885
+ return unless config.baseline_alert_severities.include?(error_log.severity)
883
886
 
884
887
  # Enqueue alert job (which will handle throttling)
885
888
  BaselineAlertJob.perform_later(error_log.id, anomaly, ApplicationJob.enqueue_locale)
@@ -8,6 +8,14 @@ module RailsErrorDashboard
8
8
  DEFAULT_DASHBOARD_USERNAME = "gandalf"
9
9
  DEFAULT_DASHBOARD_PASSWORD = "youshallnotpass"
10
10
 
11
+ # Settings that hold credentials. #inspect masks them, so printing the
12
+ # configuration in a console (IRB shows results with pp, which uses
13
+ # #inspect) or pasting it into an issue doesn't leak them.
14
+ SECRET_ATTRIBUTES = %i[
15
+ dashboard_password slack_webhook_url discord_webhook_url pagerduty_integration_key
16
+ webhook_urls issue_tracker_token issue_webhook_secret llm_api_key
17
+ ].freeze
18
+
11
19
  # Dashboard authentication (always required)
12
20
  attr_accessor :dashboard_username
13
21
  attr_accessor :dashboard_password
@@ -351,7 +359,7 @@ module RailsErrorDashboard
351
359
 
352
360
  # Rate limiting defaults
353
361
  @enable_rate_limiting = false # OFF by default (opt-in)
354
- @rate_limit_per_minute = 100 # Requests per minute per IP for API endpoints
362
+ @rate_limit_per_minute = 300 # Requests per minute per IP, per dashboard path
355
363
 
356
364
  # Enhanced metrics defaults
357
365
  @app_version = ENV["APP_VERSION"]
@@ -380,7 +388,7 @@ module RailsErrorDashboard
380
388
  # Baseline alert defaults
381
389
  @enable_baseline_alerts = false # OFF by default (opt-in)
382
390
  @baseline_alert_threshold_std_devs = ENV.fetch("BASELINE_ALERT_THRESHOLD", "2.0").to_f
383
- @baseline_alert_severities = [ :critical, :high ] # Alert on critical and high severity anomalies
391
+ @baseline_alert_severities = [ :critical, :high ] # Alert only for errors of these severities
384
392
  @baseline_alert_cooldown_minutes = ENV.fetch("BASELINE_ALERT_COOLDOWN", "120").to_i
385
393
 
386
394
  # Source code integration defaults - OFF by default (opt-in)
@@ -522,6 +530,30 @@ module RailsErrorDashboard
522
530
  initialize
523
531
  end
524
532
 
533
+ # Whether config/database.yml has the entry config.database names for this
534
+ # environment. Without it the engine skips connects_to and RED uses the
535
+ # main database, where its tables usually don't exist, so nothing is
536
+ # recorded. The engine and error_dashboard:verify both ask this.
537
+ #
538
+ # @param env_name [String] the Rails environment
539
+ # @return [Boolean]
540
+ def separate_database_entry?(env_name = Rails.env)
541
+ name = (database || :error_dashboard).to_s
542
+ ActiveRecord::Base.configurations.configs_for(env_name: env_name.to_s).any? { |config| config.name == name }
543
+ end
544
+
545
+ # Like Object#inspect, with every credential in SECRET_ATTRIBUTES replaced
546
+ # by [FILTERED] when set. Unset ones still show nil or [], which is useful
547
+ # when checking why a channel stays off.
548
+ def inspect
549
+ attributes = instance_variables.map do |ivar|
550
+ value = instance_variable_get(ivar)
551
+ secret = SECRET_ATTRIBUTES.include?(ivar.to_s.delete_prefix("@").to_sym) && value.present?
552
+ "#{ivar}=#{secret ? '[FILTERED]' : value.inspect}"
553
+ end
554
+ "#<#{self.class.name} #{attributes.join(', ')}>"
555
+ end
556
+
525
557
  # Validate configuration values
526
558
  # Raises ConfigurationError if any validation fails
527
559
  # Logs warnings for non-fatal issues (e.g., Ruby version incompatibilities)
@@ -11,10 +11,11 @@ module RailsErrorDashboard
11
11
 
12
12
  # Guard: skip connects_to if the database config doesn't exist yet in database.yml.
13
13
  # This happens during `rails generate` when the initializer was just created but
14
- # the user hasn't added the database.yml entry yet.
15
- db_configs = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env)
16
- unless db_configs.any? { |c| c.name == database_name.to_s }
17
- Rails.logger.warn "[Rails Error Dashboard] Separate database '#{database_name}' is not configured in database.yml for the '#{Rails.env}' environment. Skipping connects_to. See https://github.com/AnjanJ/rails_error_dashboard/blob/main/docs/guides/DATABASE_OPTIONS.md"
14
+ # the user hasn't added the database.yml entry yet. Logged at error level:
15
+ # RED then uses the main database, and unless that has RED's tables no
16
+ # error is recorded. error_dashboard:verify reports it as FAILED.
17
+ unless RailsErrorDashboard.configuration.separate_database_entry?
18
+ Rails.logger.error "[Rails Error Dashboard] Separate database '#{database_name}' is not configured in database.yml for the '#{Rails.env}' environment. Skipping connects_to: errors are not recorded until it is added. See https://github.com/AnjanJ/rails_error_dashboard/blob/main/docs/guides/DATABASE_OPTIONS.md"
18
19
  next
19
20
  end
20
21
 
@@ -76,6 +77,11 @@ module RailsErrorDashboard
76
77
  # configured: then it is never consulted.
77
78
  RailsErrorDashboard.detected_git_sha if RailsErrorDashboard.configuration.git_sha.blank?
78
79
 
80
+ # One error-level line if Solid Queue runs RED's jobs with a
81
+ # config/queue.yml that never would (no dispatcher, no worker for RED's
82
+ # queues). Reads the file once; never raises. The dashboard shows a banner.
83
+ RailsErrorDashboard::Services::SolidQueueConfigCheck.log_current_problems
84
+
79
85
  if RailsErrorDashboard.configuration.enable_error_subscriber
80
86
  Rails.error.subscribe(RailsErrorDashboard::ErrorReporter.new)
81
87
  end
@@ -2,12 +2,11 @@
2
2
 
3
3
  module RailsErrorDashboard
4
4
  module Middleware
5
- # Rate limiting middleware for Rails Error Dashboard routes
6
- # Protects both dashboard UI and API endpoints from abuse
5
+ # Rate limiting middleware for Rails Error Dashboard routes: at most
6
+ # config.rate_limit_per_minute requests per IP per path, per minute.
7
7
  class RateLimiter
8
- # Rate limits by endpoint type (relative to engine mount path)
9
- API_LIMIT = { limit: 100, period: 60 }.freeze # 100 req/min
10
- DASHBOARD_LIMIT = { limit: 300, period: 60 }.freeze # 300 req/min
8
+ PERIOD = 60 # seconds
9
+ DEFAULT_LIMIT = 300 # requests per PERIOD, when rate_limit_per_minute isn't a positive number
11
10
 
12
11
  def initialize(app)
13
12
  @app = app
@@ -22,16 +21,14 @@ module RailsErrorDashboard
22
21
  # Only apply rate limiting to error dashboard routes
23
22
  return @app.call(env) unless error_dashboard_route?(request.path)
24
23
 
25
- # Find matching rate limit configuration
26
- limit_config = find_limit_config(request.path)
27
- return @app.call(env) unless limit_config
24
+ limit_config = { limit: configured_limit, period: PERIOD }
28
25
 
29
26
  # Check rate limit
30
- key = rate_limit_key(request)
27
+ key = rate_limit_key(request, limit_config)
31
28
  current_count = @cache.read(key).to_i
32
29
 
33
30
  if current_count >= limit_config[:limit]
34
- return rate_limit_response(request, limit_config)
31
+ return html_rate_limit_response(limit_config)
35
32
  end
36
33
 
37
34
  # Increment counter with expiration
@@ -46,6 +43,12 @@ module RailsErrorDashboard
46
43
  RailsErrorDashboard.configuration.enable_rate_limiting
47
44
  end
48
45
 
46
+ # nil passes validation, and a limit of 0 would refuse every request.
47
+ def configured_limit
48
+ limit = RailsErrorDashboard.configuration.rate_limit_per_minute.to_i
49
+ limit.positive? ? limit : DEFAULT_LIMIT
50
+ end
51
+
49
52
  def engine_mount_path
50
53
  @engine_mount_path ||= RailsErrorDashboard.configuration.engine_mount_path
51
54
  rescue
@@ -56,45 +59,14 @@ module RailsErrorDashboard
56
59
  path.start_with?(engine_mount_path)
57
60
  end
58
61
 
59
- def find_limit_config(path)
60
- if path.start_with?("#{engine_mount_path}/api")
61
- API_LIMIT
62
- else
63
- DASHBOARD_LIMIT
64
- end
65
- end
66
-
67
- def rate_limit_key(request)
68
- # Key format: rate_limit:IP:path_prefix:time_window
62
+ def rate_limit_key(request, limit_config)
63
+ # Key format: rate_limit:IP:path:time_window
69
64
  # Time window ensures keys expire and reset
70
- limit_config = find_limit_config(request.path)
71
65
  time_window = Time.now.to_i / limit_config[:period]
72
66
 
73
67
  "rate_limit:#{request.ip}:#{request.path}:#{time_window}"
74
68
  end
75
69
 
76
- def rate_limit_response(request, limit_config)
77
- # Return JSON for API requests, HTML for dashboard
78
- if request.path.start_with?("#{engine_mount_path}/api")
79
- json_rate_limit_response(limit_config)
80
- else
81
- html_rate_limit_response(limit_config)
82
- end
83
- end
84
-
85
- def json_rate_limit_response(limit_config)
86
- [
87
- 429,
88
- {
89
- "Content-Type" => "application/json",
90
- "Retry-After" => limit_config[:period].to_s,
91
- "X-RateLimit-Limit" => limit_config[:limit].to_s,
92
- "X-RateLimit-Period" => "#{limit_config[:period]} seconds"
93
- },
94
- [ { error: "Rate limit exceeded. Please try again later." }.to_json ]
95
- ]
96
- end
97
-
98
70
  def html_rate_limit_response(limit_config)
99
71
  body = <<~HTML
100
72
  <!DOCTYPE html>
@@ -54,12 +54,42 @@ module RailsErrorDashboard
54
54
  # Override in subclass to handle event
55
55
  end
56
56
 
57
+ # Called when a resolved error occurs again and is reopened
58
+ # @param error_log [ErrorLog] The reopened error log
59
+ def on_error_reopened(error_log)
60
+ # Override in subclass to handle event
61
+ end
62
+
57
63
  # Called when an error is resolved
58
64
  # @param error_log [ErrorLog] The resolved error log
59
65
  def on_error_resolved(error_log)
60
66
  # Override in subclass to handle event
61
67
  end
62
68
 
69
+ # Called when an error is muted
70
+ # @param error_log [ErrorLog] The muted error log
71
+ def on_error_muted(error_log)
72
+ # Override in subclass to handle event
73
+ end
74
+
75
+ # Called when an error is unmuted
76
+ # @param error_log [ErrorLog] The unmuted error log
77
+ def on_error_unmuted(error_log)
78
+ # Override in subclass to handle event
79
+ end
80
+
81
+ # Called when errors are batch muted
82
+ # @param error_logs [Array<ErrorLog>] The muted error logs
83
+ def on_errors_batch_muted(error_logs)
84
+ # Override in subclass to handle event
85
+ end
86
+
87
+ # Called when errors are batch unmuted
88
+ # @param error_logs [Array<ErrorLog>] The unmuted error logs
89
+ def on_errors_batch_unmuted(error_logs)
90
+ # Override in subclass to handle event
91
+ end
92
+
63
93
  # Called when errors are batch resolved
64
94
  # @param error_logs [Array<ErrorLog>] The resolved error logs
65
95
  def on_errors_batch_resolved(error_logs)
@@ -150,8 +150,10 @@ module RailsErrorDashboard
150
150
  end
151
151
  end
152
152
 
153
+ # RED's own connection: with a separate error database, the main
154
+ # database can use a different adapter.
153
155
  def postgresql?
154
- ActiveRecord::Base.connection.adapter_name.downcase == "postgresql"
156
+ ErrorLogsRecord.connection.adapter_name.downcase == "postgresql"
155
157
  end
156
158
 
157
159
  def filter_by_severity(query)
@@ -28,7 +28,8 @@ module RailsErrorDashboard
28
28
  end
29
29
 
30
30
  # Check if exception is in the ignored exceptions list
31
- # Supports both string class names and regex patterns
31
+ # Supports class-name strings and Class/Module objects (both match
32
+ # subclasses), and regex patterns (matched against the class name)
32
33
  # @param exception [Exception] The exception to check
33
34
  # @return [Boolean] true if the exception should be ignored
34
35
  def self.ignored?(exception)
@@ -41,6 +42,8 @@ module RailsErrorDashboard
41
42
  case ignored
42
43
  when String
43
44
  exception.is_a?(ignored.constantize)
45
+ when Module
46
+ exception.is_a?(ignored)
44
47
  when Regexp
45
48
  exception_class_name.match?(ignored)
46
49
  else
@@ -57,6 +57,53 @@ module RailsErrorDashboard
57
57
  .sort
58
58
  end
59
59
 
60
+ # The running app's verdict: the problems that would stop RED's jobs in
61
+ # this environment, or [] when Solid Queue doesn't run them or there is
62
+ # no config file. Worked out once per process (it reads and parses a
63
+ # file); the dashboard banner and the boot log line both ask. Never raises.
64
+ #
65
+ # @return [Array<String>]
66
+ def self.current_problems
67
+ return @current_problems if defined?(@current_problems)
68
+
69
+ @current_problems = compute_current_problems.freeze
70
+ end
71
+
72
+ # One error-level line at boot when there is a problem.
73
+ #
74
+ # @param logger [Logger]
75
+ # @return [void]
76
+ def self.log_current_problems(logger = Rails.logger)
77
+ problems = current_problems
78
+ return if problems.empty?
79
+
80
+ logger.error(
81
+ "[Rails Error Dashboard] Solid Queue won't run RED's jobs in #{problems.join('; ')}. " \
82
+ "Errors captured in the background and notifications wait in the queue until " \
83
+ "config/queue.yml is fixed: bin/rails error_dashboard:verify shows how. Guide: #{GUIDE_URL}"
84
+ )
85
+ rescue StandardError
86
+ nil
87
+ end
88
+
89
+ # For specs.
90
+ def self.reset_current_problems!
91
+ remove_instance_variable(:@current_problems) if defined?(@current_problems)
92
+ end
93
+
94
+ def self.compute_current_problems
95
+ return [] unless RailsErrorDashboard::ApplicationJob.queue_adapter_name.to_s == "solid_queue"
96
+
97
+ path = config_path(Rails.root)
98
+ return [] unless path.exist?
99
+
100
+ result = call(path, environments: [ Rails.env.to_s ])
101
+ result[:skipped] ? [] : result[:problems]
102
+ rescue StandardError, ScriptError
103
+ []
104
+ end
105
+ private_class_method :compute_current_problems
106
+
60
107
  # The queues RED's jobs are enqueued on, as this app names them
61
108
  # (queue_name_prefix included).
62
109
  #
@@ -1,3 +1,3 @@
1
1
  module RailsErrorDashboard
2
- VERSION = "0.14.3"
2
+ VERSION = "0.14.4"
3
3
  end
@@ -34,6 +34,25 @@ namespace :error_dashboard do
34
34
  end
35
35
  checks_passed += 1
36
36
 
37
+ # 2b. Separate database: the database.yml entry for this environment. Without
38
+ # it the engine skips connects_to and RED uses the main database, which
39
+ # usually has no RED tables, so nothing is recorded.
40
+ if config.use_separate_database
41
+ print " Error database entry... "
42
+ db_name = config.database || :error_dashboard
43
+ if config.separate_database_entry?
44
+ puts "OK"
45
+ checks_passed += 1
46
+ else
47
+ puts "FAILED"
48
+ puts " config/database.yml has no '#{db_name}' entry for the '#{Rails.env}' environment."
49
+ puts " RED is using the main database instead, so errors are not recorded in the error database."
50
+ puts " Add '#{db_name}:' under '#{Rails.env}:' with migrations_paths: db/error_dashboard_migrate"
51
+ puts " (see docs/guides/DATABASE_OPTIONS.md), then restart."
52
+ checks_failed += 1
53
+ end
54
+ end
55
+
37
56
  # 3. Database connection
38
57
  print " Database connection... "
39
58
  begin
@@ -218,6 +237,28 @@ namespace :error_dashboard do
218
237
  end
219
238
  end
220
239
 
240
+ # 10. json 3.0 breaks Rails before 8.1.4 and 7.2.4: dashboard pages return
241
+ # 500 and captures can fail, with no error message. The bug is in Rails.
242
+ json_version = Gem.loaded_specs["json"]&.version
243
+ if json_version && json_version >= Gem::Version.new("3")
244
+ print " json gem... "
245
+ rails_version = Gem::Version.new(Rails.version)
246
+ series = rails_version.segments.first(2)
247
+ json3_ok = (series == [ 8, 1 ] && rails_version >= Gem::Version.new("8.1.4")) ||
248
+ (series == [ 7, 2 ] && rails_version >= Gem::Version.new("7.2.4")) ||
249
+ (series <=> [ 8, 1 ]) == 1
250
+ if json3_ok
251
+ puts "OK (json #{json_version}, Rails #{Rails.version})"
252
+ checks_passed += 1
253
+ else
254
+ puts "WARNING"
255
+ puts " json #{json_version} breaks Rails #{Rails.version} (fixed in Rails 8.1.4 and 7.2.4):"
256
+ puts " dashboard pages return 500 and errors can fail to be captured."
257
+ puts " Add gem \"json\", \"< 3\" to your Gemfile and run bundle update json, or upgrade Rails."
258
+ warnings += 1
259
+ end
260
+ end
261
+
221
262
  # Summary
222
263
  puts "\n" + "-" * 70
223
264
  puts " Results: #{checks_passed} passed, #{checks_failed} failed, #{warnings} warnings"
@@ -324,7 +365,7 @@ namespace :error_dashboard do
324
365
 
325
366
  # Confirm before proceeding
326
367
  print "\nProceed with backfill? (y/N): "
327
- confirmation = $stdin.gets.chomp.downcase
368
+ confirmation = $stdin.gets.to_s.chomp.downcase
328
369
 
329
370
  unless confirmation == "y" || confirmation == "yes"
330
371
  puts "\n✗ Backfill cancelled"
@@ -569,7 +610,7 @@ namespace :error_dashboard do
569
610
  end
570
611
 
571
612
  print "\n Proceed with deletion? (y/N): "
572
- confirmation = $stdin.gets.chomp.downcase
613
+ confirmation = $stdin.gets.to_s.chomp.downcase
573
614
 
574
615
  unless confirmation == "y" || confirmation == "yes"
575
616
  puts "\n Cleanup cancelled"
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.14.3
4
+ version: 0.14.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Anjan Jagirdar
@@ -601,7 +601,7 @@ metadata:
601
601
  funding_uri: https://github.com/sponsors/AnjanJ
602
602
  post_install_message: |
603
603
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
604
- RED (Rails Error Dashboard) v0.14.3
604
+ RED (Rails Error Dashboard) v0.14.4
605
605
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
606
606
 
607
607
  First install: