rails_error_dashboard 0.14.3 → 0.15.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 (86) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +45 -17
  3. data/app/controllers/rails_error_dashboard/application_controller.rb +28 -4
  4. data/app/controllers/rails_error_dashboard/errors_controller.rb +68 -2
  5. data/app/controllers/rails_error_dashboard/health_controller.rb +38 -0
  6. data/app/helpers/rails_error_dashboard/application_helper.rb +10 -2
  7. data/app/helpers/rails_error_dashboard/backtrace_helper.rb +56 -2
  8. data/app/jobs/rails_error_dashboard/baseline_alert_job.rb +20 -2
  9. data/app/jobs/rails_error_dashboard/cascade_detection_job.rb +30 -0
  10. data/app/jobs/rails_error_dashboard/concerns/plain_channel_message.rb +33 -6
  11. data/app/jobs/rails_error_dashboard/discord_error_notification_job.rb +1 -1
  12. data/app/jobs/rails_error_dashboard/pagerduty_error_notification_job.rb +1 -1
  13. data/app/jobs/rails_error_dashboard/retention_cleanup_job.rb +26 -0
  14. data/app/jobs/rails_error_dashboard/scheduled_digest_job.rb +6 -1
  15. data/app/jobs/rails_error_dashboard/slack_error_notification_job.rb +1 -1
  16. data/app/jobs/rails_error_dashboard/telegram_error_notification_job.rb +25 -0
  17. data/app/jobs/rails_error_dashboard/webhook_error_notification_job.rb +2 -18
  18. data/app/models/rails_error_dashboard/error_log.rb +43 -12
  19. data/app/models/rails_error_dashboard/missing_translation.rb +43 -0
  20. data/app/views/layouts/rails_error_dashboard.html.erb +24 -1
  21. data/app/views/rails_error_dashboard/errors/_error_info.html.erb +3 -1
  22. data/app/views/rails_error_dashboard/errors/_sidebar_metadata.html.erb +3 -1
  23. data/app/views/rails_error_dashboard/errors/_source_code.html.erb +3 -9
  24. data/app/views/rails_error_dashboard/errors/activestorage_health_summary.html.erb +28 -0
  25. data/app/views/rails_error_dashboard/errors/boot_errors.html.erb +130 -0
  26. data/app/views/rails_error_dashboard/errors/index.html.erb +1 -1
  27. data/app/views/rails_error_dashboard/errors/missing_translations.html.erb +143 -0
  28. data/app/views/rails_error_dashboard/errors/settings/_value_badge.html.erb +7 -4
  29. data/app/views/rails_error_dashboard/errors/settings.html.erb +19 -5
  30. data/config/locales/de.yml +72 -5
  31. data/config/locales/en.yml +83 -4
  32. data/config/locales/es.yml +72 -5
  33. data/config/locales/fr.yml +72 -5
  34. data/config/locales/it.yml +72 -5
  35. data/config/locales/ja.yml +72 -4
  36. data/config/locales/pl.yml +72 -5
  37. data/config/locales/pt-BR.yml +72 -5
  38. data/config/locales/ru.yml +72 -5
  39. data/config/locales/uk.yml +72 -5
  40. data/config/locales/zh-CN.yml +72 -4
  41. data/config/routes.rb +6 -0
  42. data/db/migrate/20251223000000_create_rails_error_dashboard_complete_schema.rb +10 -2
  43. data/db/migrate/20251225074653_remove_environment_from_error_logs.rb +9 -0
  44. data/db/migrate/20261010000001_create_missing_translations.rb +50 -0
  45. data/lib/generators/rails_error_dashboard/install/install_generator.rb +40 -38
  46. data/lib/generators/rails_error_dashboard/install/templates/initializer.rb +31 -3
  47. data/lib/generators/rails_error_dashboard/uninstall/uninstall_generator.rb +11 -2
  48. data/lib/rails_error_dashboard/commands/assign_error.rb +24 -1
  49. data/lib/rails_error_dashboard/commands/batch_resolve_errors.rb +9 -1
  50. data/lib/rails_error_dashboard/commands/flush_missing_translations.rb +103 -0
  51. data/lib/rails_error_dashboard/commands/log_error.rb +6 -3
  52. data/lib/rails_error_dashboard/commands/resolve_error.rb +2 -15
  53. data/lib/rails_error_dashboard/commands/update_error_status.rb +7 -0
  54. data/lib/rails_error_dashboard/configuration.rb +75 -2
  55. data/lib/rails_error_dashboard/engine.rb +52 -5
  56. data/lib/rails_error_dashboard/middleware/rate_limiter.rb +21 -43
  57. data/lib/rails_error_dashboard/plugin.rb +49 -3
  58. data/lib/rails_error_dashboard/plugin_registry.rb +61 -12
  59. data/lib/rails_error_dashboard/queries/boot_errors.rb +76 -0
  60. data/lib/rails_error_dashboard/queries/dashboard_stats.rb +3 -11
  61. data/lib/rails_error_dashboard/queries/errors_list.rb +11 -17
  62. data/lib/rails_error_dashboard/queries/health_status.rb +191 -0
  63. data/lib/rails_error_dashboard/queries/missing_translation_summary.rb +82 -0
  64. data/lib/rails_error_dashboard/services/active_storage_probe.rb +91 -0
  65. data/lib/rails_error_dashboard/services/backtrace_parser.rb +13 -7
  66. data/lib/rails_error_dashboard/services/backtrace_processor.rb +15 -0
  67. data/lib/rails_error_dashboard/services/baseline_alert_payload_builder.rb +30 -0
  68. data/lib/rails_error_dashboard/services/crash_capture.rb +85 -2
  69. data/lib/rails_error_dashboard/services/digest_builder.rb +9 -6
  70. data/lib/rails_error_dashboard/services/error_notification_dispatcher.rb +6 -0
  71. data/lib/rails_error_dashboard/services/exception_filter.rb +4 -1
  72. data/lib/rails_error_dashboard/services/github_link_generator.rb +25 -9
  73. data/lib/rails_error_dashboard/services/missing_translation_handler.rb +152 -0
  74. data/lib/rails_error_dashboard/services/missing_translation_tracker.rb +316 -0
  75. data/lib/rails_error_dashboard/services/resolution_callbacks.rb +44 -0
  76. data/lib/rails_error_dashboard/services/severity_classifier.rb +52 -0
  77. data/lib/rails_error_dashboard/services/solid_queue_config_check.rb +47 -0
  78. data/lib/rails_error_dashboard/services/source_code_reader.rb +23 -3
  79. data/lib/rails_error_dashboard/services/telegram_delivery.rb +134 -0
  80. data/lib/rails_error_dashboard/services/telegram_payload_builder.rb +105 -0
  81. data/lib/rails_error_dashboard/services/webhook_delivery.rb +63 -0
  82. data/lib/rails_error_dashboard/services/webhook_signer.rb +80 -0
  83. data/lib/rails_error_dashboard/version.rb +1 -1
  84. data/lib/rails_error_dashboard.rb +12 -0
  85. data/lib/tasks/error_dashboard.rake +43 -2
  86. metadata +21 -2
@@ -0,0 +1,143 @@
1
+ <% content_for :page_title, red_t("red.errors.missing_translations_page.title") %>
2
+
3
+ <div>
4
+ <div class="d-flex justify-content-between align-items-center mb-4">
5
+ <h1 style="font-size: 20px; font-weight: 700; margin: 0;">
6
+ <i class="bi bi-translate me-2"></i>
7
+ <%= red_t("red.errors.missing_translations_page.title") %>
8
+ </h1>
9
+
10
+ <div class="btn-group" role="group">
11
+ <%= link_to missing_translations_errors_path(days: 7), class: "btn btn-sm #{@days == 7 ? 'btn-primary' : 'btn-outline-primary'}" do %>
12
+ <%= red_t("red.common.days_filter.7") %>
13
+ <% end %>
14
+ <%= link_to missing_translations_errors_path(days: 30), class: "btn btn-sm #{@days == 30 ? 'btn-primary' : 'btn-outline-primary'}" do %>
15
+ <%= red_t("red.common.days_filter.30") %>
16
+ <% end %>
17
+ <%= link_to missing_translations_errors_path(days: 90), class: "btn btn-sm #{@days == 90 ? 'btn-primary' : 'btn-outline-primary'}" do %>
18
+ <%= red_t("red.common.days_filter.90") %>
19
+ <% end %>
20
+ </div>
21
+ </div>
22
+
23
+ <% if @entry_count == 0 %>
24
+ <div class="red-empty-state">
25
+ <i class="bi bi-translate display-1 text-success mb-3"></i>
26
+ <div class="red-empty-state-title"><%= red_t("red.errors.missing_translations_page.empty_title") %></div>
27
+ <p class="text-muted">
28
+ <%= red_t("red.errors.missing_translations_page.empty_body", days: @days) %>
29
+ </p>
30
+ <div class="card mx-auto" style="max-width: 560px;">
31
+ <div class="card-body text-start">
32
+ <h6><%= red_t("red.errors.missing_translations_page.how_title") %></h6>
33
+ <ul class="mb-0">
34
+ <li><%= red_t("red.errors.missing_translations_page.how_enabled_html", option: content_tag(:code, "enable_missing_translation_tracking = true")) %></li>
35
+ <li><%= red_t("red.errors.missing_translations_page.how_hooked") %></li>
36
+ <li><%= red_t("red.errors.missing_translations_page.how_recorded") %></li>
37
+ <li><%= red_t("red.errors.missing_translations_page.how_not_seen") %></li>
38
+ <li><%= red_t("red.errors.missing_translations_page.how_buffered") %></li>
39
+ </ul>
40
+ </div>
41
+ </div>
42
+ </div>
43
+ <% else %>
44
+ <div class="row mb-4">
45
+ <div class="col-md-4">
46
+ <div class="card text-center">
47
+ <div class="card-body">
48
+ <div class="display-6 text-warning"><%= @unique_keys %></div>
49
+ <small class="text-muted"><%= red_t("red.errors.missing_translations_page.missing_keys") %></small>
50
+ </div>
51
+ </div>
52
+ </div>
53
+ <div class="col-md-4">
54
+ <div class="card text-center">
55
+ <div class="card-body">
56
+ <div class="display-6 text-danger"><%= @total_misses %></div>
57
+ <small class="text-muted"><%= red_t("red.errors.missing_translations_page.total_misses") %></small>
58
+ </div>
59
+ </div>
60
+ </div>
61
+ <div class="col-md-4">
62
+ <div class="card text-center">
63
+ <div class="card-body">
64
+ <div class="display-6 text-info"><%= @locale_count %></div>
65
+ <small class="text-muted"><%= red_t("red.errors.missing_translations_page.locales_affected") %></small>
66
+ </div>
67
+ </div>
68
+ </div>
69
+ </div>
70
+
71
+ <% if @overflow_count.to_i.positive? %>
72
+ <div class="alert alert-warning d-flex align-items-start" role="alert">
73
+ <i class="bi bi-exclamation-triangle me-2"></i>
74
+ <div>
75
+ <%= red_t("red.errors.missing_translations_page.overflow_notice",
76
+ count: @overflow_count,
77
+ limit: RailsErrorDashboard::Services::MissingTranslationTracker::MAX_BUFFERED_KEYS) %>
78
+ </div>
79
+ </div>
80
+ <% end %>
81
+
82
+ <div class="card mb-4">
83
+ <div class="card-header d-flex justify-content-between align-items-center">
84
+ <h5 class="mb-0">
85
+ <i class="bi bi-translate text-warning me-2"></i>
86
+ <%= red_t("red.errors.missing_translations_page.all_title") %>
87
+ <span class="badge bg-warning text-dark"><%= @entry_count %></span>
88
+ </h5>
89
+ <small class="text-muted"><%== @pagy.info_tag %></small>
90
+ </div>
91
+ <div class="card-body p-0">
92
+ <div class="table-responsive">
93
+ <table class="table table-hover mb-0">
94
+ <thead class="table-light">
95
+ <tr>
96
+ <th><%= red_t("red.errors.missing_translations_page.column_key") %></th>
97
+ <th width="90"><%= red_t("red.errors.missing_translations_page.column_locale") %></th>
98
+ <th width="90"><%= red_t("red.errors.missing_translations_page.column_misses") %></th>
99
+ <th><%= red_t("red.errors.missing_translations_page.column_source") %></th>
100
+ <th width="140"><%= red_t("red.errors.missing_translations_page.column_first_seen") %></th>
101
+ <th width="140"><%= red_t("red.errors.pages.column_last_seen") %></th>
102
+ </tr>
103
+ </thead>
104
+ <tbody>
105
+ <% @entries.each do |entry| %>
106
+ <tr>
107
+ <td><code><%= entry[:key] %></code></td>
108
+ <%# A locale tag is a machine value, shown as-is. %>
109
+ <td><span class="badge bg-light text-dark"><%= entry[:locale] %></span></td>
110
+ <td><strong><%= entry[:count] %></strong></td>
111
+ <td>
112
+ <% if entry[:source].present? %>
113
+ <code><%= entry[:source] %></code>
114
+ <% else %>
115
+ <span class="text-muted">&mdash;</span>
116
+ <% end %>
117
+ </td>
118
+ <td><%= local_time_ago(entry[:first_seen]) %></td>
119
+ <td><%= local_time_ago(entry[:last_seen]) %></td>
120
+ </tr>
121
+ <% end %>
122
+ </tbody>
123
+ </table>
124
+ </div>
125
+ </div>
126
+ <div class="card-footer border-top d-flex justify-content-between align-items-center">
127
+ <div>
128
+ <small class="text-muted">
129
+ <i class="bi bi-lightbulb text-warning"></i> <%= red_t("red.errors.missing_translations_page.footer_tip") %>
130
+ </small>
131
+ <small class="ms-3">
132
+ <a href="https://guides.rubyonrails.org/i18n.html" target="_blank" rel="noopener" class="text-decoration-none">
133
+ <i class="bi bi-book"></i> <%= red_t("red.errors.missing_translations_page.docs") %> <i class="bi bi-box-arrow-up-right" style="font-size: 0.7em;"></i>
134
+ </a>
135
+ </small>
136
+ </div>
137
+ <div>
138
+ <%== @pagy.series_nav(:bootstrap) if @pagy.pages > 1 %>
139
+ </div>
140
+ </div>
141
+ </div>
142
+ <% end %>
143
+ </div>
@@ -265,16 +265,19 @@
265
265
  end
266
266
 
267
267
  when :retention_days
268
- # Show retention configuration with manual cleanup instructions
268
+ # Retention is enforced by RetentionCleanupJob, which the host schedules
269
+ # (docs/PRODUCTION.md). The badge used to claim "manual cleanup only" and
270
+ # cite cleanup_resolved, a different task that deletes only resolved
271
+ # errors and ignores this setting.
269
272
  if value.present?
270
273
  %>
271
274
  <div>
272
275
  <span class="badge bg-warning text-dark fs-6"><%= red_tp("red.settings.values.days", count: value) %></span>
273
- <small class="text-danger d-block">
274
- <i class="bi bi-exclamation-triangle"></i> <%= red_t("red.settings.values.manual_cleanup_required") %>
276
+ <small class="text-muted d-block">
277
+ <i class="bi bi-clock-history"></i> <%= red_t("red.settings.values.retention_enforced_by_job") %>
275
278
  </small>
276
279
  <small class="text-muted d-block mt-1">
277
- <%= red_t("red.settings.values.run_command") %> <code class="small">rails error_dashboard:cleanup_resolved DAYS=<%= value %></code>
280
+ <%= red_t("red.settings.values.run_command") %> <code class="small">RailsErrorDashboard::RetentionCleanupJob.perform_now</code>
278
281
  </small>
279
282
  </div>
280
283
  <%
@@ -56,10 +56,15 @@
56
56
  notification_email_from: { type: :string, show_if: :enable_email_notifications },
57
57
  enable_discord_notifications: { type: :boolean },
58
58
  discord_webhook_url: { type: :url, show_if: :enable_discord_notifications },
59
+ enable_telegram_notifications: { type: :boolean },
60
+ telegram_bot_token: { type: :secret, show_if: :enable_telegram_notifications },
61
+ telegram_chat_id: { type: :string, show_if: :enable_telegram_notifications },
62
+ telegram_api_base_url: { type: :url, show_if: :enable_telegram_notifications },
59
63
  enable_pagerduty_notifications: { type: :boolean },
60
64
  pagerduty_integration_key: { type: :string, show_if: :enable_pagerduty_notifications },
61
65
  enable_webhook_notifications: { type: :boolean },
62
66
  webhook_urls: { type: :array, show_if: :enable_webhook_notifications },
67
+ webhook_signing_secret: { type: :secret, show_if: :enable_webhook_notifications },
63
68
  enable_scheduled_digests: { type: :boolean },
64
69
  digest_frequency: { type: :symbol, show_if: :enable_scheduled_digests },
65
70
  digest_recipients: { type: :array, show_if: :enable_scheduled_digests }
@@ -100,7 +105,7 @@
100
105
  enable_breadcrumbs: { type: :boolean },
101
106
  breadcrumb_buffer_size: { type: :integer, show_if: :enable_breadcrumbs, unit: :events },
102
107
  enable_n_plus_one_detection: { type: :boolean, show_if: :enable_breadcrumbs },
103
- n_plus_one_threshold: { type: :integer, show_if: :enable_n_plus_one_detection, unit: :queries }
108
+ n_plus_one_threshold: { type: :integer, show_if: [ :enable_breadcrumbs, :enable_n_plus_one_detection ], unit: :queries }
104
109
  }
105
110
  },
106
111
  :system_health => {
@@ -149,6 +154,7 @@
149
154
  color: "info",
150
155
  settings: {
151
156
  enable_rack_attack_tracking: { type: :boolean },
157
+ enable_missing_translation_tracking: { type: :boolean },
152
158
  enable_actioncable_tracking: { type: :boolean },
153
159
  enable_activestorage_tracking: { type: :boolean }
154
160
  }
@@ -166,7 +172,9 @@
166
172
  color: "light",
167
173
  settings: {
168
174
  enable_internal_logging: { type: :boolean },
169
- log_level: { type: :symbol, show_if: :enable_internal_logging }
175
+ # Not gated on enable_internal_logging: Logger.error honours log_level
176
+ # whether or not internal logging is on.
177
+ log_level: { type: :symbol }
170
178
  }
171
179
  }
172
180
  }
@@ -198,16 +206,22 @@
198
206
  <div>
199
207
  <% group_config[:settings].each do |setting_key, setting_meta| %>
200
208
  <%
201
- show_if_key = setting_meta[:show_if]
202
- should_show = show_if_key.nil? || @config.send(show_if_key)
209
+ # One flag, or every flag in an array, must be on for the row to show.
210
+ should_show = Array(setting_meta[:show_if]).all? { |flag| @config.send(flag) }
203
211
  next unless should_show
204
212
  value = @config.send(setting_meta[:method] || setting_key)
213
+ # A credential is shown as Set / Not set whatever shape it has. The
214
+ # per-row :type documents the value's shape; this one rule is what
215
+ # keeps a webhook URL or an API key off the page, the same list
216
+ # Configuration#inspect masks, so a new secret cannot leak by a
217
+ # forgotten per-row type.
218
+ badge_type = RailsErrorDashboard::Configuration::SECRET_ATTRIBUTES.include?(setting_key) ? :secret : setting_meta[:type]
205
219
  %>
206
220
  <div style="display: flex; align-items: center; gap: var(--space-4); padding: var(--space-3) var(--space-6); border-bottom: 1px solid var(--border-primary); font-size: 13px;">
207
221
  <span style="font-family: var(--font-mono); font-size: 12px; color: var(--accent); font-weight: 500; width: 240px; flex-shrink: 0;"><%= setting_key %></span>
208
222
  <span style="font-family: var(--font-mono); font-size: 12px; color: var(--text-primary); font-weight: 500; width: 180px; flex-shrink: 0;">
209
223
  <%= render partial: 'rails_error_dashboard/errors/settings/value_badge',
210
- locals: { value: value, type: setting_meta[:type], unit: setting_meta[:unit] } %>
224
+ locals: { value: value, type: badge_type, unit: setting_meta[:unit] } %>
211
225
  </span>
212
226
  <%# Keyed by the config option's own name, so a key is greppable
213
227
  from the initializer that sets it. %>
@@ -60,6 +60,8 @@ de:
60
60
  llm: LLM
61
61
  n_plus_one: N+1-Abfragen
62
62
  deprecations: Deprecations
63
+ missing_translations: "Übersetzungen"
64
+ boot_errors: "Startfehler"
63
65
  swallowed: Verschluckt
64
66
  diagnostic_dumps: Diagnose
65
67
  storms: Fehlerstürme
@@ -87,6 +89,10 @@ de:
87
89
  <code>ERROR_DASHBOARD_PASSWORD</code> oder konfigurieren Sie <code>authenticate_with</code>
88
90
  in Ihrem Initializer.
89
91
  dismiss: Für diese Sitzung ausblenden
92
+ solid_queue:
93
+ label: "Solid Queue führt die Jobs von RED nicht aus."
94
+ 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."
95
+ guide: "Was RED von Solid Queue braucht"
90
96
  storm:
91
97
  active_title: Fehlersturm im Gange
92
98
  recent_title: Sturmschutz war kürzlich aktiv
@@ -824,6 +830,53 @@ de:
824
830
  gelangt sind
825
831
  footer_tip: Verschluckte Exceptions werden ausgelöst und dann still abgefangen.
826
832
  Sie können echte Fehler verbergen.
833
+ missing_translations_page:
834
+ title: "Fehlende Übersetzungen"
835
+ empty_title: "Keine fehlenden Übersetzungen erfasst"
836
+ empty_body: "In den letzten %{days} Tagen wurden keine fehlenden Übersetzungsschlüssel erfasst."
837
+ how_title: "So werden fehlende Übersetzungen erfasst:"
838
+ how_enabled_html: "Die Erfassung muss aktiviert sein (%{option})"
839
+ how_hooked: "RED umhüllt I18n.exception_handler und reicht an den Handler weiter, den Ihre App bereits hatte; sonst ändert sich nichts an Ihrer I18n-Konfiguration"
840
+ how_recorded: "Jede Abfrage, die beim Handler landet – über I18n.t ebenso wie über den View-Helper t() – wird nach Locale und Schlüssel gezählt, zusammen mit der zuerst gesehenen Aufrufstelle"
841
+ how_not_seen: "Eine Abfrage mit :default schlägt nie fehl, und eine mit raise_on_missing_translations löst eine Ausnahme aus, bevor ein Handler läuft; beide werden hier nicht gezählt"
842
+ how_buffered: "Fehlschläge werden im Speicher gezählt und am Ende der Anfrage oder des Jobs geschrieben; rechnen Sie mit einer kurzen Verzögerung, bis sie erscheinen"
843
+ missing_keys: "Fehlende Schlüssel"
844
+ total_misses: "Fehlschläge gesamt"
845
+ locales_affected: "Betroffene Locales"
846
+ all_title: "Fehlende Übersetzungsschlüssel"
847
+ column_key: "Schlüssel"
848
+ column_locale: "Locale"
849
+ column_source: "Zuerst gesehen in"
850
+ column_misses: "Fehlschläge"
851
+ column_first_seen: "Zuerst gesehen"
852
+ overflow_notice: "%{count} Fehlschläge konnten keinem Schlüssel zugeordnet werden, weil der Puffer des Trackers (%{limit} Schlüssel pro Thread) voll war."
853
+ footer_tip: "Fügen Sie den Schlüssel der Locale-Datei hinzu oder übergeben Sie ein :default. Schlüssel, auf die Nutzer tatsächlich gestoßen sind, stehen zuerst."
854
+ docs: "Rails-i18n-Leitfaden"
855
+
856
+ boot_errors_page:
857
+ title: "Startfehler"
858
+ empty_title: "Keine Startfehler erfasst"
859
+ empty_body: "In den letzten %{days} Tagen ist kein Prozess beim Starten abgestürzt."
860
+ how_title: "So werden Startfehler erfasst:"
861
+ how_enabled_html: "Die Absturzerfassung muss aktiviert sein (%{option})"
862
+ how_hooked: "RED registriert seinen at_exit-Hook direkt nach Ihren Initializern und vor dem Eager Loading von Rails, sodass ein Absturz beim Start erfasst wird, obwohl die App nie fertig gestartet ist"
863
+ how_captured_html: "%{zeitwerk} aus einer Datei, die die falsche Konstante definiert, ein %{syntax} in einem Model, ein Initializer, der eine Ausnahme auslöst: alles, was den Prozess beendet, bevor er eine Anfrage bedient"
864
+ how_imported: "Der Absturz wird auf die Festplatte geschrieben, weil es noch keine Datenbank gibt, und beim nächsten erfolgreichen Start importiert"
865
+ how_never: "Ein per SIGKILL beendeter oder abgestürzter (Segfault) Prozess wird von at_exit nie gesehen, in keinem Werkzeug"
866
+ unresolved: "Ungelöst"
867
+ boot_crashes: "Startabstürze"
868
+ failed_boots: "Fehlgeschlagene Starts"
869
+ all_title: "Startabstürze nach Fehler"
870
+ column_error: "Fehler"
871
+ column_file: "Datei"
872
+ column_constant: "Konstante"
873
+ column_boots: "Starts"
874
+ column_first_seen: "Zuerst gesehen"
875
+ column_status: "Status"
876
+ status_open: "Offen"
877
+ status_resolved: "Gelöst"
878
+ footer_tip: "Ein Startabsturz bedeutet, dass der Prozess nie eine Anfrage bedient hat. Beheben Sie ihn vor dem nächsten Deploy; die Zeile wird wie jeder andere Fehler gelöst."
879
+
827
880
  rack_attack_page:
828
881
  title: Rate-Limit-Ereignisse
829
882
  missing_title: Rack Attack ist nicht installiert
@@ -1083,7 +1136,7 @@ de:
1083
1136
  unable_to_detect: Erkennung nicht möglich
1084
1137
  no_user_model: Kein User-Modell erkannt
1085
1138
  unable_to_query_count: Anzahl konnte nicht abgefragt werden
1086
- manual_cleanup_required: Manuelle Bereinigung erforderlich
1139
+ retention_enforced_by_job: "Wird vom RetentionCleanupJob gelöscht, sobald Ihr Scheduler ihn ausführt"
1087
1140
  keep_forever: Dauerhaft behalten
1088
1141
  no_automatic_deletion: Keine automatische Löschung
1089
1142
  run_command: 'Ausführen:'
@@ -1091,8 +1144,7 @@ de:
1091
1144
  options:
1092
1145
  enable_middleware: Fängt unbehandelte Exceptions ab
1093
1146
  enable_error_subscriber: Abonniert Rails-Fehlerereignisse
1094
- retention_days: 'Nur manuelle Bereinigung (ausführen: rails error_dashboard:cleanup_resolved
1095
- DAYS=90)'
1147
+ retention_days: "Fehler löschen, die so viele Tage nicht mehr aufgetreten sind. Durchgesetzt vom RetentionCleanupJob, den das Gem selbst nicht einplant"
1096
1148
  max_backtrace_lines: Maximale Backtrace-Tiefe
1097
1149
  sampling_rate: Anteil der erfassten Fehler
1098
1150
  application_name: Name dieser Anwendung (wird automatisch erkannt, wenn nicht
@@ -1111,7 +1163,7 @@ de:
1111
1163
  erkannt, wenn nicht gesetzt)
1112
1164
  async_logging: Verarbeitung über Hintergrund-Jobs
1113
1165
  async_adapter: Adapter für die Job-Warteschlange
1114
- enable_rate_limiting: API-Anfragen drosseln
1166
+ enable_rate_limiting: "Dashboard-Anfragen pro IP und Pfad drosseln"
1115
1167
  rate_limit_per_minute: Anfragen pro Minute und IP
1116
1168
  enable_slack_notifications: Echtzeit-Benachrichtigungen
1117
1169
  slack_webhook_url: Slack-Webhook-Endpunkt
@@ -1120,10 +1172,15 @@ de:
1120
1172
  notification_email_from: Absenderadresse
1121
1173
  enable_discord_notifications: Discord-Webhook-Benachrichtigungen
1122
1174
  discord_webhook_url: Discord-Webhook-Endpunkt
1175
+ enable_telegram_notifications: "Telegram-Benachrichtigungen über die Bot-API"
1176
+ telegram_bot_token: "Bot-Token von @BotFather (Umgebungsvariable TELEGRAM_BOT_TOKEN setzen)"
1177
+ telegram_chat_id: "Chat, Gruppe oder Kanal, in den der Bot schreibt"
1178
+ telegram_api_base_url: "Bot-API-Server (nur für einen Proxy oder selbst gehosteten Server ändern)"
1123
1179
  enable_pagerduty_notifications: Eskalation bei kritischen Fehlern
1124
1180
  pagerduty_integration_key: PagerDuty-API-Schlüssel
1125
1181
  enable_webhook_notifications: Allgemeine Webhook-Endpunkte
1126
1182
  webhook_urls: Eigene Webhook-Endpunkte
1183
+ webhook_signing_secret: Signiert ausgehende Webhooks mit HMAC-SHA256 (Umgebungsvariable WEBHOOK_SIGNING_SECRET setzen)
1127
1184
  enable_scheduled_digests: Tägliche/wöchentliche Zusammenfassungs-E-Mails.
1128
1185
  Planung über SolidQueue, Sidekiq oder cron
1129
1186
  digest_frequency: Wie oft Zusammenfassungs-E-Mails gesendet werden
@@ -1144,7 +1201,7 @@ de:
1144
1201
  source_code_context_lines: Zeilen vor und nach der Zielzeile
1145
1202
  enable_git_blame: git-blame-Informationen anzeigen
1146
1203
  source_code_cache_ttl: Cache-Dauer für den Quellcode
1147
- only_show_app_code_source: Gems und stdlib aus Sicherheitsgründen ausblenden
1204
+ only_show_app_code_source: "Gem- und Vendor-Code im Quelltext-Viewer ausblenden"
1148
1205
  git_branch_strategy: Strategie zur Branch-Auflösung
1149
1206
  enable_breadcrumbs: Aktivitätsverlauf der Anfrage erfassen
1150
1207
  breadcrumb_buffer_size: Maximale Anzahl Breadcrumbs pro Anfrage
@@ -1176,6 +1233,7 @@ de:
1176
1233
  enable_crash_capture: at_exit-Hook für unbehandelte Abstürze
1177
1234
  enable_coverage_tracking: Diagnosemodus — über das Dashboard aktivieren, um
1178
1235
  ausgeführte Zeilen in der Quellcodeansicht zu sehen (Ruby 3.2+)
1236
+ enable_missing_translation_tracking: "Zählt die I18n-Fehlschläge der App nach Locale und Schlüssel (umhüllt I18n.exception_handler)"
1179
1237
  enable_rack_attack_tracking: Throttle- und Blocklist-Ereignisse in einer eigenen
1180
1238
  Tabelle erfassen
1181
1239
  enable_actioncable_tracking: WebSocket-Kanalereignisse als Breadcrumbs erfassen
@@ -1231,6 +1289,8 @@ de:
1231
1289
  breadcrumbs: Breadcrumbs
1232
1290
  system_health: Systemzustand
1233
1291
  swallowed_exceptions: Erkennung verschluckter Exceptions
1292
+ missing_translations: "Erfassung fehlender Übersetzungen"
1293
+ boot_errors: "Erfassung von Startfehlern"
1234
1294
  rack_attack: Rack-Attack-Erfassung
1235
1295
  actioncable: ActionCable-Erfassung
1236
1296
  activestorage: ActiveStorage-Erfassung
@@ -1636,6 +1696,13 @@ de:
1636
1696
  guide_link: ActionCable-Leitfaden
1637
1697
  activestorage:
1638
1698
  title: ActiveStorage-Zustand
1699
+ probe:
1700
+ title: "Erreichbarkeit des Dienstes"
1701
+ reachable: "Erreichbar"
1702
+ unreachable: "Nicht erreichbar"
1703
+ service: "Dienst: %{name}"
1704
+ latency: "%{ms} ms Umlaufzeit"
1705
+ how: "Eine Existenzprüfung für einen Schlüssel, der nie gespeichert wird, beim Laden dieser Seite. GET /health führt dieselbe Prüfung aus."
1639
1706
  empty:
1640
1707
  title: Keine ActiveStorage-Ereignisse gefunden
1641
1708
  message: In den Fehler-Breadcrumbs der letzten %{days} Tage wurden keine
@@ -116,6 +116,8 @@ en:
116
116
  llm: "LLM"
117
117
  n_plus_one: "N+1 Queries"
118
118
  deprecations: "Deprecations"
119
+ missing_translations: "Translations"
120
+ boot_errors: "Boot Errors"
119
121
  swallowed: "Swallowed"
120
122
  diagnostic_dumps: "Diagnostics"
121
123
  storms: "Storms"
@@ -155,6 +157,11 @@ en:
155
157
  initializer.
156
158
  dismiss: "Dismiss for this session"
157
159
 
160
+ solid_queue:
161
+ # Shown when Solid Queue runs RED's jobs with a config/queue.yml that never would.
162
+ label: "Solid Queue won't run RED's jobs."
163
+ 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."
164
+ guide: "What RED needs from Solid Queue"
158
165
  storm:
159
166
  # Whole sentences, not fragments. The English original assembled these
160
167
  # from a stem plus a conditional clause, which cannot be reordered into
@@ -1180,6 +1187,61 @@ en:
1180
1187
  view_unrescued: "View any unrescued %{exception} errors that leaked into error_log"
1181
1188
  footer_tip: "Swallowed exceptions are raised then silently rescued. They may hide real bugs."
1182
1189
 
1190
+ missing_translations_page:
1191
+ title: "Missing Translations"
1192
+ empty_title: "No Missing Translations Recorded"
1193
+ # %{days} is the selected day range.
1194
+ empty_body: "No missing translation keys were recorded over the last %{days} days."
1195
+ how_title: "How missing translations are tracked:"
1196
+ # %{option} is a config option name as a <code> element.
1197
+ how_enabled_html: "Tracking must be enabled (%{option})"
1198
+ # I18n.exception_handler, I18n.t, t(), :default and raise_on_missing_translations are Ruby/Rails API names, not prose.
1199
+ how_hooked: "RED wraps I18n.exception_handler and delegates to the handler your app already had; nothing else in your I18n configuration changes"
1200
+ how_recorded: "Every lookup that falls through to the handler — I18n.t and the t() view helper alike — is counted by locale and key, with the first call site seen"
1201
+ how_not_seen: "A lookup given a :default never misses, and one made with raise_on_missing_translations raises before any handler runs, so neither is counted here"
1202
+ how_buffered: "Misses are counted in memory and written at the end of the request or job, so allow a short delay before they appear"
1203
+ missing_keys: "Missing Keys"
1204
+ total_misses: "Total Misses"
1205
+ locales_affected: "Locales Affected"
1206
+ all_title: "Missing Translation Keys"
1207
+ column_key: "Key"
1208
+ column_locale: "Locale"
1209
+ column_source: "First Seen In"
1210
+ column_misses: "Misses"
1211
+ column_first_seen: "First Seen"
1212
+ # %{count} is a number of misses, %{limit} the per-thread buffer size.
1213
+ overflow_notice: "%{count} misses could not be attributed to a key because the tracker's buffer (%{limit} keys per thread) was full."
1214
+ footer_tip: "Add the key to the locale file, or pass a :default. Keys users actually hit are listed first."
1215
+ docs: "Rails i18n guide"
1216
+
1217
+ boot_errors_page:
1218
+ title: "Boot Errors"
1219
+ empty_title: "No Boot Errors Recorded"
1220
+ # %{days} is the selected day range.
1221
+ empty_body: "No process died while booting over the last %{days} days."
1222
+ how_title: "How boot errors are captured:"
1223
+ # %{option} is a config option name as a <code> element.
1224
+ how_enabled_html: "Crash capture must be enabled (%{option})"
1225
+ # at_exit, SIGKILL and segfault are Ruby/OS terms, not prose.
1226
+ how_hooked: "RED registers its at_exit hook right after your initializers run and before Rails eager-loads, so a crash during boot is seen even though the app never finished starting"
1227
+ # %{zeitwerk} and %{syntax} are exception class names as <code> elements.
1228
+ how_captured_html: "%{zeitwerk} from a file that defines the wrong constant, a %{syntax} in a model, an initializer that raised: anything that kills the process before it serves a request"
1229
+ how_imported: "The crash is written to disk, because there is no database yet, and imported on the next boot that succeeds"
1230
+ how_never: "A process killed by SIGKILL or a segfault is never seen by at_exit, in any tool"
1231
+ unresolved: "Unresolved"
1232
+ boot_crashes: "Boot Crashes"
1233
+ failed_boots: "Failed Boots"
1234
+ all_title: "Boot Crashes by Error"
1235
+ column_error: "Error"
1236
+ column_file: "File"
1237
+ column_constant: "Constant"
1238
+ column_boots: "Boots"
1239
+ column_first_seen: "First Seen"
1240
+ column_status: "Status"
1241
+ status_open: "Open"
1242
+ status_resolved: "Resolved"
1243
+ footer_tip: "A boot crash means the process never served a request. Fix it before the next deploy; the row resolves like any other error."
1244
+
1183
1245
  rack_attack_page:
1184
1246
  title: "Rate Limit Events"
1185
1247
  missing_title: "Rack Attack Is Not Installed"
@@ -1518,7 +1580,7 @@ en:
1518
1580
  unable_to_detect: "Unable to detect"
1519
1581
  no_user_model: "No User model detected"
1520
1582
  unable_to_query_count: "Unable to query count"
1521
- manual_cleanup_required: "Manual cleanup required"
1583
+ retention_enforced_by_job: "Deleted by RetentionCleanupJob once your scheduler runs it"
1522
1584
  keep_forever: "Keep Forever"
1523
1585
  no_automatic_deletion: "No automatic deletion"
1524
1586
  # These label a rake command rendered beside them. The command is never
@@ -1533,7 +1595,7 @@ en:
1533
1595
  options:
1534
1596
  enable_middleware: "Catches unhandled exceptions"
1535
1597
  enable_error_subscriber: "Subscribes to Rails error events"
1536
- retention_days: "Manual cleanup only (run: rails error_dashboard:cleanup_resolved DAYS=90)"
1598
+ retention_days: "Delete errors not seen for this many days. Enforced by RetentionCleanupJob, which nothing in the gem schedules"
1537
1599
  max_backtrace_lines: "Stack trace depth limit"
1538
1600
  sampling_rate: "Percentage of errors captured"
1539
1601
  application_name: "Name of this application (auto-detected if not set)"
@@ -1547,7 +1609,7 @@ en:
1547
1609
  total_users_for_impact: "For impact % calculation (auto-detected if not set)"
1548
1610
  async_logging: "Background job processing"
1549
1611
  async_adapter: "Job queue adapter"
1550
- enable_rate_limiting: "Throttle API requests"
1612
+ enable_rate_limiting: "Throttle dashboard requests per IP and path"
1551
1613
  rate_limit_per_minute: "Requests per minute per IP"
1552
1614
  enable_slack_notifications: "Real-time notifications"
1553
1615
  slack_webhook_url: "Slack webhook endpoint"
@@ -1556,10 +1618,15 @@ en:
1556
1618
  notification_email_from: "Sender address"
1557
1619
  enable_discord_notifications: "Discord webhook notifications"
1558
1620
  discord_webhook_url: "Discord webhook endpoint"
1621
+ enable_telegram_notifications: "Telegram Bot API notifications"
1622
+ telegram_bot_token: "Bot token from @BotFather (set TELEGRAM_BOT_TOKEN env var)"
1623
+ telegram_chat_id: "Chat, group or channel the bot posts to"
1624
+ telegram_api_base_url: "Bot API server (change only for a proxy or self-hosted server)"
1559
1625
  enable_pagerduty_notifications: "Critical error escalation"
1560
1626
  pagerduty_integration_key: "PagerDuty API key"
1561
1627
  enable_webhook_notifications: "Generic webhook endpoints"
1562
1628
  webhook_urls: "Custom webhook endpoints"
1629
+ webhook_signing_secret: "Signs outbound webhooks with HMAC-SHA256 (set WEBHOOK_SIGNING_SECRET env var)"
1563
1630
  enable_scheduled_digests: "Daily/weekly summary emails. Schedule via SolidQueue, Sidekiq, or cron"
1564
1631
  digest_frequency: "How often to send digest emails"
1565
1632
  digest_recipients: "Email addresses (falls back to notification recipients)"
@@ -1577,7 +1644,7 @@ en:
1577
1644
  source_code_context_lines: "Lines before/after target line"
1578
1645
  enable_git_blame: "Show git blame info"
1579
1646
  source_code_cache_ttl: "Source code cache duration"
1580
- only_show_app_code_source: "Hide gems/stdlib for security"
1647
+ only_show_app_code_source: "Hide gem and vendored code in the source viewer"
1581
1648
  git_branch_strategy: "Branch resolution strategy"
1582
1649
  enable_breadcrumbs: "Capture request activity trail"
1583
1650
  breadcrumb_buffer_size: "Max breadcrumbs per request"
@@ -1603,6 +1670,7 @@ en:
1603
1670
  enable_diagnostic_dump: "On-demand system state snapshots"
1604
1671
  enable_crash_capture: "at_exit hook for unhandled crashes"
1605
1672
  enable_coverage_tracking: "Diagnostic mode — enable via dashboard to see executed lines in source view (Ruby 3.2+)"
1673
+ enable_missing_translation_tracking: "Count the app's I18n misses by locale and key (wraps I18n.exception_handler)"
1606
1674
  enable_rack_attack_tracking: "Record throttle/blocklist events to their own table"
1607
1675
  enable_actioncable_tracking: "Track WebSocket channel events as breadcrumbs"
1608
1676
  enable_activestorage_tracking: "Track uploads/downloads/deletes as breadcrumbs"
@@ -1682,6 +1750,8 @@ en:
1682
1750
  breadcrumbs: "Breadcrumbs"
1683
1751
  system_health: "System health"
1684
1752
  swallowed_exceptions: "Swallowed exception detection"
1753
+ missing_translations: "Missing-translation tracking"
1754
+ boot_errors: "Boot error capture"
1685
1755
  rack_attack: "Rack Attack tracking"
1686
1756
  actioncable: "ActionCable tracking"
1687
1757
  activestorage: "ActiveStorage tracking"
@@ -2226,6 +2296,15 @@ en:
2226
2296
 
2227
2297
  activestorage:
2228
2298
  title: "ActiveStorage Health"
2299
+ probe:
2300
+ # Live reachability card. %{name} is the service name from
2301
+ # config/storage.yml and %{ms} a number of milliseconds.
2302
+ title: "Service reachability"
2303
+ reachable: "Reachable"
2304
+ unreachable: "Unreachable"
2305
+ service: "Service: %{name}"
2306
+ latency: "%{ms} ms round trip"
2307
+ how: "One existence check for a key that is never stored, made when this page loads. GET /health runs the same probe."
2229
2308
 
2230
2309
  empty:
2231
2310
  title: "No ActiveStorage Events Found"