rails_error_dashboard 0.14.4 → 0.16.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 (113) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +19 -8
  3. data/app/controllers/rails_error_dashboard/application_controller.rb +28 -4
  4. data/app/controllers/rails_error_dashboard/errors_controller.rb +97 -4
  5. data/app/controllers/rails_error_dashboard/health_controller.rb +38 -0
  6. data/app/controllers/rails_error_dashboard/webhooks_controller.rb +10 -9
  7. data/app/helpers/rails_error_dashboard/application_helper.rb +2 -2
  8. data/app/helpers/rails_error_dashboard/backtrace_helper.rb +63 -16
  9. data/app/helpers/rails_error_dashboard/overview_helper.rb +4 -0
  10. data/app/jobs/rails_error_dashboard/baseline_alert_job.rb +20 -2
  11. data/app/jobs/rails_error_dashboard/cascade_detection_job.rb +30 -0
  12. data/app/jobs/rails_error_dashboard/concerns/plain_channel_message.rb +33 -6
  13. data/app/jobs/rails_error_dashboard/discord_error_notification_job.rb +1 -1
  14. data/app/jobs/rails_error_dashboard/pagerduty_error_notification_job.rb +1 -1
  15. data/app/jobs/rails_error_dashboard/retention_cleanup_job.rb +31 -0
  16. data/app/jobs/rails_error_dashboard/scheduled_digest_job.rb +6 -1
  17. data/app/jobs/rails_error_dashboard/slack_error_notification_job.rb +1 -1
  18. data/app/jobs/rails_error_dashboard/telegram_error_notification_job.rb +25 -0
  19. data/app/jobs/rails_error_dashboard/webhook_error_notification_job.rb +2 -18
  20. data/app/models/rails_error_dashboard/error_log.rb +54 -20
  21. data/app/models/rails_error_dashboard/error_occurrence.rb +1 -0
  22. data/app/models/rails_error_dashboard/error_occurrence_context.rb +14 -0
  23. data/app/models/rails_error_dashboard/missing_translation.rb +43 -0
  24. data/app/views/layouts/rails_error_dashboard.html.erb +14 -2
  25. data/app/views/rails_error_dashboard/errors/_captured_occurrences.html.erb +45 -0
  26. data/app/views/rails_error_dashboard/errors/_error_info.html.erb +12 -3
  27. data/app/views/rails_error_dashboard/errors/_occurrence_banner.html.erb +12 -0
  28. data/app/views/rails_error_dashboard/errors/_sidebar_metadata.html.erb +3 -1
  29. data/app/views/rails_error_dashboard/errors/_source_code.html.erb +3 -9
  30. data/app/views/rails_error_dashboard/errors/activestorage_health_summary.html.erb +28 -0
  31. data/app/views/rails_error_dashboard/errors/boot_errors.html.erb +130 -0
  32. data/app/views/rails_error_dashboard/errors/index.html.erb +1 -1
  33. data/app/views/rails_error_dashboard/errors/missing_translations.html.erb +143 -0
  34. data/app/views/rails_error_dashboard/errors/overview.html.erb +10 -7
  35. data/app/views/rails_error_dashboard/errors/platform_comparison.html.erb +16 -11
  36. data/app/views/rails_error_dashboard/errors/settings/_value_badge.html.erb +7 -4
  37. data/app/views/rails_error_dashboard/errors/settings.html.erb +23 -8
  38. data/app/views/rails_error_dashboard/errors/show.html.erb +7 -4
  39. data/config/locales/de.yml +87 -5
  40. data/config/locales/en.yml +99 -4
  41. data/config/locales/es.yml +88 -5
  42. data/config/locales/fr.yml +88 -5
  43. data/config/locales/it.yml +88 -5
  44. data/config/locales/ja.yml +86 -4
  45. data/config/locales/pl.yml +89 -5
  46. data/config/locales/pt-BR.yml +88 -5
  47. data/config/locales/ru.yml +89 -5
  48. data/config/locales/uk.yml +89 -5
  49. data/config/locales/zh-CN.yml +86 -4
  50. data/config/routes.rb +6 -0
  51. data/db/migrate/20251223000000_create_rails_error_dashboard_complete_schema.rb +10 -2
  52. data/db/migrate/20260106094256_backfill_application_for_existing_errors.rb +3 -3
  53. data/db/migrate/20261010000001_create_missing_translations.rb +50 -0
  54. data/db/migrate/20261015000001_create_rails_error_dashboard_error_occurrence_contexts.rb +35 -0
  55. data/db/migrate/20261015000002_add_capture_source_to_error_occurrences_and_error_logs.rb +46 -0
  56. data/lib/generators/rails_error_dashboard/install/install_generator.rb +9 -4
  57. data/lib/generators/rails_error_dashboard/install/templates/initializer.rb +41 -5
  58. data/lib/rails_error_dashboard/commands/assign_error.rb +24 -1
  59. data/lib/rails_error_dashboard/commands/batch_mute_errors.rb +11 -2
  60. data/lib/rails_error_dashboard/commands/batch_resolve_errors.rb +16 -3
  61. data/lib/rails_error_dashboard/commands/batch_unmute_errors.rb +7 -2
  62. data/lib/rails_error_dashboard/commands/find_or_increment_error.rb +6 -0
  63. data/lib/rails_error_dashboard/commands/flush_missing_translations.rb +101 -0
  64. data/lib/rails_error_dashboard/commands/flush_rack_attack_events.rb +2 -4
  65. data/lib/rails_error_dashboard/commands/flush_storm_counts.rb +7 -6
  66. data/lib/rails_error_dashboard/commands/flush_swallowed_exceptions.rb +2 -4
  67. data/lib/rails_error_dashboard/commands/log_error.rb +20 -8
  68. data/lib/rails_error_dashboard/commands/record_occurrence_context.rb +60 -0
  69. data/lib/rails_error_dashboard/commands/resolve_error.rb +2 -15
  70. data/lib/rails_error_dashboard/commands/update_error_status.rb +7 -0
  71. data/lib/rails_error_dashboard/configuration.rb +61 -4
  72. data/lib/rails_error_dashboard/engine.rb +42 -1
  73. data/lib/rails_error_dashboard/error_reporter.rb +4 -2
  74. data/lib/rails_error_dashboard/manual_error_reporter.rb +11 -1
  75. data/lib/rails_error_dashboard/middleware/error_catcher.rb +18 -4
  76. data/lib/rails_error_dashboard/middleware/rate_limiter.rb +7 -1
  77. data/lib/rails_error_dashboard/plugin.rb +19 -3
  78. data/lib/rails_error_dashboard/plugin_registry.rb +61 -12
  79. data/lib/rails_error_dashboard/queries/analytics_stats.rb +0 -12
  80. data/lib/rails_error_dashboard/queries/boot_errors.rb +76 -0
  81. data/lib/rails_error_dashboard/queries/co_occurring_errors.rb +59 -33
  82. data/lib/rails_error_dashboard/queries/dashboard_stats.rb +3 -11
  83. data/lib/rails_error_dashboard/queries/errors_list.rb +8 -16
  84. data/lib/rails_error_dashboard/queries/health_status.rb +191 -0
  85. data/lib/rails_error_dashboard/queries/missing_translation_summary.rb +82 -0
  86. data/lib/rails_error_dashboard/queries/platform_comparison.rb +14 -6
  87. data/lib/rails_error_dashboard/services/active_storage_probe.rb +91 -0
  88. data/lib/rails_error_dashboard/services/backtrace_parser.rb +13 -7
  89. data/lib/rails_error_dashboard/services/backtrace_processor.rb +31 -7
  90. data/lib/rails_error_dashboard/services/baseline_alert_payload_builder.rb +30 -0
  91. data/lib/rails_error_dashboard/services/crash_capture.rb +90 -2
  92. data/lib/rails_error_dashboard/services/digest_builder.rb +9 -6
  93. data/lib/rails_error_dashboard/services/error_notification_dispatcher.rb +6 -0
  94. data/lib/rails_error_dashboard/services/github_link_generator.rb +25 -14
  95. data/lib/rails_error_dashboard/services/markdown_error_formatter.rb +15 -16
  96. data/lib/rails_error_dashboard/services/missing_translation_handler.rb +152 -0
  97. data/lib/rails_error_dashboard/services/missing_translation_tracker.rb +316 -0
  98. data/lib/rails_error_dashboard/services/repository_url.rb +18 -0
  99. data/lib/rails_error_dashboard/services/resolution_callbacks.rb +44 -0
  100. data/lib/rails_error_dashboard/services/severity_classifier.rb +52 -0
  101. data/lib/rails_error_dashboard/services/similarity_calculator.rb +3 -9
  102. data/lib/rails_error_dashboard/services/source_code_reader.rb +23 -3
  103. data/lib/rails_error_dashboard/services/storm_protection/count_buffer.rb +6 -3
  104. data/lib/rails_error_dashboard/services/storm_protection/gate.rb +6 -1
  105. data/lib/rails_error_dashboard/services/telegram_delivery.rb +134 -0
  106. data/lib/rails_error_dashboard/services/telegram_payload_builder.rb +105 -0
  107. data/lib/rails_error_dashboard/services/webhook_delivery.rb +63 -0
  108. data/lib/rails_error_dashboard/services/webhook_signer.rb +80 -0
  109. data/lib/rails_error_dashboard/value_objects/occurrence_snapshot.rb +23 -0
  110. data/lib/rails_error_dashboard/version.rb +1 -1
  111. data/lib/rails_error_dashboard.rb +28 -0
  112. data/lib/tasks/error_dashboard.rake +2 -3
  113. metadata +29 -2
@@ -36,6 +36,8 @@ uk:
36
36
  healthy: "✅ Справний"
37
37
  warning: "⚠️ Попередження"
38
38
  critical: "\U0001F534 Критичний"
39
+ not_rated: "Без оцінки"
40
+ not_rated_hint: "Оцінка порівнює платформи, тому потрібні помилки щонайменше на двох."
39
41
  device_type:
40
42
  mobile: Mobile
41
43
  tablet: Tablet
@@ -60,6 +62,8 @@ uk:
60
62
  llm: LLM
61
63
  n_plus_one: Запити N+1
62
64
  deprecations: Deprecations
65
+ missing_translations: "Переклади"
66
+ boot_errors: "Помилки запуску"
63
67
  swallowed: Придушені
64
68
  diagnostic_dumps: Діагностика
65
69
  storms: Шторми
@@ -246,6 +250,10 @@ uk:
246
250
  inactive_body: увімкніть, щоб бачити виконані рядки
247
251
  enable: Увімкнути
248
252
  disable: Вимкнути
253
+ occurrence:
254
+ title: "Подія #%{id}"
255
+ body: "Breadcrumbs, стан системи та змінні належать до цієї події. Усе інше показує останнє захоплення."
256
+ show_latest: "Показати останню"
249
257
  baselines:
250
258
  title: Статистика базового рівня
251
259
  anomaly_label: 'Аномалія:'
@@ -518,6 +526,14 @@ uk:
518
526
  few: "+%{count} ще"
519
527
  many: "+%{count} ще"
520
528
  view_errors: Переглянути помилки
529
+ captured_occurrences:
530
+ title: "Збережені події"
531
+ hint: "Breadcrumbs, стан системи та змінні зберігаються для останніх подій (occurrence_context_limit). Відкрийте подію, щоб їх побачити."
532
+ column_occurrence: "Подія"
533
+ column_occurred_at: "Час"
534
+ column_version: "Версія"
535
+ view: "Відкрити"
536
+ viewing: "Відкрито"
521
537
  co_occurring:
522
538
  title: Супутні помилки
523
539
  badge: Часові закономірності
@@ -773,6 +789,7 @@ uk:
773
789
  many: "%{count} кадрів"
774
790
  click_to_expand: "(натисніть, щоб розгорнути)"
775
791
  view_source: Переглянути код
792
+ client_report_note: "Ці фрейми надійшли зі звіту клієнта (ManualErrorReporter). Вони описують код, що виконувався на клієнті, тому RED не відкриває для них вихідні файли, не показує фрагменти й не запускає git blame."
776
793
  none: Backtrace недоступний
777
794
  row:
778
795
  new_badge: НОВА
@@ -866,6 +883,53 @@ uk:
866
883
  до error_log
867
884
  footer_tip: Придушені винятки піднімаються, а потім мовчки перехоплюються.
868
885
  Вони можуть приховувати справжні помилки.
886
+ missing_translations_page:
887
+ title: "Відсутні переклади"
888
+ empty_title: "Відсутні переклади не зафіксовано"
889
+ empty_body: "За останні %{days} дн. відсутніх ключів перекладу не зафіксовано."
890
+ how_title: "Як відстежуються відсутні переклади:"
891
+ how_enabled_html: "Відстеження має бути ввімкнено (%{option})"
892
+ how_hooked: "RED обгортає I18n.exception_handler і передає виклик обробнику, який уже був у застосунку; більше нічого в налаштуваннях I18n не змінюється"
893
+ how_recorded: "Кожен запит, що дійшов до обробника — як через I18n.t, так і через хелпер t() у view — рахується за локаллю та ключем разом із першим поміченим місцем виклику"
894
+ how_not_seen: "Запит із :default ніколи не промахується, а запит із raise_on_missing_translations кидає виняток до виклику будь-якого обробника, тому тут не враховуються ні той, ні інший"
895
+ how_buffered: "Промахи рахуються в пам'яті й записуються наприкінці запиту або завдання, тож вони з'являться з невеликою затримкою"
896
+ missing_keys: "Відсутні ключі"
897
+ total_misses: "Усього промахів"
898
+ locales_affected: "Зачеплені локалі"
899
+ all_title: "Відсутні ключі перекладу"
900
+ column_key: "Ключ"
901
+ column_locale: "Локаль"
902
+ column_source: "Уперше помічено в"
903
+ column_misses: "Промахи"
904
+ column_first_seen: "Уперше помічено"
905
+ overflow_notice: "%{count} промахів не вдалося віднести до ключа: буфер трекера (%{limit} ключів на потік) був заповнений."
906
+ footer_tip: "Додайте ключ до файлу локалі або передайте :default. Ключі, на які справді натрапили користувачі, йдуть першими."
907
+ docs: "Посібник Rails з i18n"
908
+
909
+ boot_errors_page:
910
+ title: "Помилки запуску"
911
+ empty_title: "Помилки запуску не зафіксовано"
912
+ empty_body: "За останні %{days} дн. жоден процес не впав під час запуску."
913
+ how_title: "Як перехоплюються помилки запуску:"
914
+ how_enabled_html: "Перехоплення аварій має бути ввімкнено (%{option})"
915
+ how_hooked: "RED реєструє свій хук at_exit одразу після ваших ініціалізаторів і до eager loading у Rails, тому аварія під час запуску видима, навіть якщо застосунок так і не запустився"
916
+ how_captured_html: "%{zeitwerk} із файлу, що визначає не ту константу, %{syntax} у моделі, ініціалізатор з винятком: усе, що вбиває процес до обробки першого запиту"
917
+ how_imported: "Аварія записується на диск, бо бази даних ще немає, і імпортується під час наступного успішного запуску"
918
+ how_never: "Процес, убитий SIGKILL або segfault, at_exit не побачить у жодному інструменті"
919
+ unresolved: "Невирішені"
920
+ boot_crashes: "Аварії під час запуску"
921
+ failed_boots: "Невдалі запуски"
922
+ all_title: "Аварії під час запуску за помилкою"
923
+ column_error: "Помилка"
924
+ column_file: "Файл"
925
+ column_constant: "Константа"
926
+ column_boots: "Запуски"
927
+ column_first_seen: "Уперше помічено"
928
+ column_status: "Статус"
929
+ status_open: "Відкрита"
930
+ status_resolved: "Вирішена"
931
+ footer_tip: "Аварія під час запуску означає, що процес не обробив жодного запиту. Виправте її до наступного розгортання; рядок закривається як будь-яка інша помилка."
932
+
869
933
  rack_attack_page:
870
934
  title: Події обмеження запитів
871
935
  missing_title: Rack Attack не встановлено
@@ -1104,6 +1168,11 @@ uk:
1104
1168
  other: "%{count} події"
1105
1169
  few: "%{count} події"
1106
1170
  many: "%{count} подій"
1171
+ occurrences:
1172
+ one: "%{count} подія"
1173
+ other: "%{count} події"
1174
+ few: "%{count} події"
1175
+ many: "%{count} подій"
1107
1176
  queries:
1108
1177
  one: "%{count} запит"
1109
1178
  other: "%{count} запита"
@@ -1149,7 +1218,7 @@ uk:
1149
1218
  unable_to_detect: Не вдалося визначити
1150
1219
  no_user_model: Модель User не виявлено
1151
1220
  unable_to_query_count: Не вдалося отримати кількість
1152
- manual_cleanup_required: Потрібне ручне очищення
1221
+ retention_enforced_by_job: "Видаляються RetentionCleanupJob, коли його запускає ваш планувальник"
1153
1222
  keep_forever: Зберігати назавжди
1154
1223
  no_automatic_deletion: Без автоматичного видалення
1155
1224
  run_command: 'Виконайте:'
@@ -1157,8 +1226,8 @@ uk:
1157
1226
  options:
1158
1227
  enable_middleware: Перехоплює необроблені винятки
1159
1228
  enable_error_subscriber: Підписується на події помилок Rails
1160
- retention_days: 'Лише ручне очищення (виконайте: rails error_dashboard:cleanup_resolved
1161
- DAYS=90)'
1229
+ retention_days: "Видаляти помилки, яких не було стільки днів. Виконує RetentionCleanupJob, який гем сам не планує"
1230
+ occurrence_context_limit: "Скільки останніх подій кожної помилки зберігають breadcrumbs, стан системи та змінні (0 = вимкнено)"
1162
1231
  max_backtrace_lines: Обмеження глибини стеку викликів
1163
1232
  sampling_rate: Відсоток захоплених помилок
1164
1233
  application_name: Назва цього застосунку (визначається автоматично, якщо не
@@ -1176,7 +1245,7 @@ uk:
1176
1245
  якщо не задано)
1177
1246
  async_logging: Обробка у фонових завданнях
1178
1247
  async_adapter: Адаптер черги завдань
1179
- enable_rate_limiting: Обмежувати запити до API
1248
+ enable_rate_limiting: "Обмежувати запити до панелі за IP і шляхом"
1180
1249
  rate_limit_per_minute: Запитів за хвилину з однієї IP-адреси
1181
1250
  enable_slack_notifications: Сповіщення в реальному часі
1182
1251
  slack_webhook_url: Кінцева точка webhook у Slack
@@ -1185,10 +1254,15 @@ uk:
1185
1254
  notification_email_from: Адреса відправника
1186
1255
  enable_discord_notifications: Сповіщення через webhook у Discord
1187
1256
  discord_webhook_url: Кінцева точка webhook у Discord
1257
+ enable_telegram_notifications: "Сповіщення Telegram через Bot API"
1258
+ telegram_bot_token: "Токен бота від @BotFather (задайте змінну середовища TELEGRAM_BOT_TOKEN)"
1259
+ telegram_chat_id: "Чат, група або канал, куди пише бот"
1260
+ telegram_api_base_url: "Сервер Bot API (змінюйте лише для проксі або власного сервера)"
1188
1261
  enable_pagerduty_notifications: Ескалація критичних помилок
1189
1262
  pagerduty_integration_key: Ключ API PagerDuty
1190
1263
  enable_webhook_notifications: Довільні кінцеві точки webhook
1191
1264
  webhook_urls: Власні кінцеві точки webhook
1265
+ webhook_signing_secret: Підписує вихідні webhook-запити HMAC-SHA256 (задайте змінну середовища WEBHOOK_SIGNING_SECRET)
1192
1266
  enable_scheduled_digests: Щоденні/щотижневі підсумкові листи. Плануйте через
1193
1267
  SolidQueue, Sidekiq або cron
1194
1268
  digest_frequency: Як часто надсилати підсумкові листи
@@ -1209,7 +1283,7 @@ uk:
1209
1283
  source_code_context_lines: Рядків до та після цільового рядка
1210
1284
  enable_git_blame: Показувати дані git blame
1211
1285
  source_code_cache_ttl: Тривалість кешування вихідного коду
1212
- only_show_app_code_source: Приховати gems/stdlib з міркувань безпеки
1286
+ only_show_app_code_source: "Приховувати код гемів і vendor у переглядачі коду"
1213
1287
  git_branch_strategy: Стратегія визначення гілки
1214
1288
  enable_breadcrumbs: Захоплювати послідовність дій у запиті
1215
1289
  breadcrumb_buffer_size: Максимум breadcrumbs на запит
@@ -1240,6 +1314,7 @@ uk:
1240
1314
  enable_crash_capture: Хук at_exit для необроблених аварійних завершень
1241
1315
  enable_coverage_tracking: Діагностичний режим — увімкніть через панель, щоб
1242
1316
  бачити виконані рядки у перегляді коду (Ruby 3.2+)
1317
+ enable_missing_translation_tracking: "Рахує промахи I18n застосунку за локаллю та ключем (обгортає I18n.exception_handler)"
1243
1318
  enable_rack_attack_tracking: Записувати події throttle/blocklist в окрему
1244
1319
  таблицю
1245
1320
  enable_actioncable_tracking: Відстежувати події каналів WebSocket як breadcrumbs
@@ -1299,6 +1374,8 @@ uk:
1299
1374
  breadcrumbs: Breadcrumbs
1300
1375
  system_health: Стан системи
1301
1376
  swallowed_exceptions: Виявлення придушених винятків
1377
+ missing_translations: "Відстеження відсутніх перекладів"
1378
+ boot_errors: "Перехоплення помилок запуску"
1302
1379
  rack_attack: Відстеження Rack Attack
1303
1380
  actioncable: Відстеження ActionCable
1304
1381
  activestorage: Відстеження ActiveStorage
@@ -1712,6 +1789,13 @@ uk:
1712
1789
  guide_link: Посібник з ActionCable
1713
1790
  activestorage:
1714
1791
  title: Стан ActiveStorage
1792
+ probe:
1793
+ title: "Доступність сервісу"
1794
+ reachable: "Доступний"
1795
+ unreachable: "Недоступний"
1796
+ service: "Сервіс: %{name}"
1797
+ latency: "%{ms} мс туди й назад"
1798
+ how: "Одна перевірка існування ключа, який ніколи не зберігається, під час завантаження цієї сторінки. GET /health виконує ту саму перевірку."
1715
1799
  empty:
1716
1800
  title: Подій ActiveStorage не знайдено
1717
1801
  message: За останні %{days} днів у breadcrumbs помилок не виявлено операцій
@@ -36,6 +36,8 @@ zh-CN:
36
36
  healthy: "✅ 正常"
37
37
  warning: "⚠️ 警告"
38
38
  critical: "\U0001F534 严重"
39
+ not_rated: "未评级"
40
+ not_rated_hint: "评级是在平台之间比较的,因此至少需要两个平台上的错误。"
39
41
  device_type:
40
42
  mobile: Mobile
41
43
  tablet: Tablet
@@ -60,6 +62,8 @@ zh-CN:
60
62
  llm: LLM
61
63
  n_plus_one: N+1 查询
62
64
  deprecations: Deprecations
65
+ missing_translations: "翻译"
66
+ boot_errors: "启动错误"
63
67
  swallowed: 被吞异常
64
68
  diagnostic_dumps: 诊断
65
69
  storms: 风暴
@@ -229,6 +233,10 @@ zh-CN:
229
233
  inactive_body: 启用后可查看已执行的行
230
234
  enable: 启用
231
235
  disable: 停用
236
+ occurrence:
237
+ title: "发生 #%{id}"
238
+ body: "Breadcrumbs、系统状态和变量来自这次发生。其余内容显示最新一次捕获。"
239
+ show_latest: "查看最新"
232
240
  baselines:
233
241
  title: 基线统计
234
242
  anomaly_label: 异常:
@@ -449,6 +457,14 @@ zh-CN:
449
457
  more:
450
458
  other: 另有 %{count} 项
451
459
  view_errors: 查看错误
460
+ captured_occurrences:
461
+ title: "已捕获的发生记录"
462
+ hint: "仅为最近的发生保留 Breadcrumbs、系统状态和变量(occurrence_context_limit)。打开某一条即可查看。"
463
+ column_occurrence: "发生"
464
+ column_occurred_at: "发生时间"
465
+ column_version: "版本"
466
+ view: "查看"
467
+ viewing: "正在查看"
452
468
  co_occurring:
453
469
  title: 同时出现的错误
454
470
  badge: 时间规律
@@ -677,6 +693,7 @@ zh-CN:
677
693
  other: "%{count} 个栈帧"
678
694
  click_to_expand: "(点击展开)"
679
695
  view_source: 查看源码
696
+ client_report_note: "这些帧来自客户端报告(ManualErrorReporter)。它们描述的是在客户端运行的代码,因此 RED 不会为其打开源文件、显示代码片段或运行 git blame。"
680
697
  none: 无可用的 backtrace
681
698
  row:
682
699
  new_badge: 新
@@ -753,6 +770,53 @@ zh-CN:
753
770
  column_ratio: 比例
754
771
  view_unrescued: 查看未被捕获、已进入 error_log 的 %{exception} 错误
755
772
  footer_tip: 被吞异常先被抛出,随后被静默捕获,可能掩盖真正的缺陷。
773
+ missing_translations_page:
774
+ title: "缺失的翻译"
775
+ empty_title: "未记录到缺失的翻译"
776
+ empty_body: "最近 %{days} 天内未记录到缺失的翻译键。"
777
+ how_title: "缺失翻译的跟踪方式:"
778
+ how_enabled_html: "必须启用跟踪(%{option})"
779
+ how_hooked: "RED 会包装 I18n.exception_handler,并把调用交给应用原有的处理器;I18n 配置的其他部分不会改变"
780
+ how_recorded: "每一次到达处理器的查找——无论来自 I18n.t 还是视图助手 t()——都会按语言区域和键计数,并记录首次观测到的调用位置"
781
+ how_not_seen: "提供了 :default 的查找永远不会缺失;使用 raise_on_missing_translations 的查找会在任何处理器运行前抛出异常,因此两者都不会在此计数"
782
+ how_buffered: "缺失在内存中计数,并在请求或任务结束时写入,因此显示会有短暂延迟"
783
+ missing_keys: "缺失的键"
784
+ total_misses: "缺失总数"
785
+ locales_affected: "受影响的语言区域"
786
+ all_title: "缺失的翻译键"
787
+ column_key: "键"
788
+ column_locale: "语言区域"
789
+ column_source: "首次出现于"
790
+ column_misses: "缺失次数"
791
+ column_first_seen: "首次出现"
792
+ overflow_notice: "由于跟踪器的缓冲区(每线程 %{limit} 个键)已满,有 %{count} 次缺失无法归到具体的键。"
793
+ footer_tip: "把该键加入语言文件,或传入 :default。用户实际遇到的键排在最前。"
794
+ docs: "Rails i18n 指南"
795
+
796
+ boot_errors_page:
797
+ title: "启动错误"
798
+ empty_title: "未记录到启动错误"
799
+ empty_body: "最近 %{days} 天内没有进程在启动时崩溃。"
800
+ how_title: "启动错误的捕获方式:"
801
+ how_enabled_html: "必须启用崩溃捕获(%{option})"
802
+ how_hooked: "RED 在你的 initializer 运行之后、Rails 预加载之前注册 at_exit 钩子,因此即使应用从未完成启动,启动期间的崩溃也会被捕获"
803
+ how_captured_html: "文件定义了错误常量导致的 %{zeitwerk}、模型中的 %{syntax}、抛出异常的 initializer:任何在处理请求之前就终止进程的问题"
804
+ how_imported: "此时还没有数据库,所以崩溃先写入磁盘,并在下一次成功启动时导入"
805
+ how_never: "被 SIGKILL 或段错误终止的进程,任何工具的 at_exit 都看不到"
806
+ unresolved: "未解决"
807
+ boot_crashes: "启动崩溃"
808
+ failed_boots: "失败的启动"
809
+ all_title: "按错误分组的启动崩溃"
810
+ column_error: "错误"
811
+ column_file: "文件"
812
+ column_constant: "常量"
813
+ column_boots: "启动次数"
814
+ column_first_seen: "首次出现"
815
+ column_status: "状态"
816
+ status_open: "未处理"
817
+ status_resolved: "已解决"
818
+ footer_tip: "启动崩溃意味着进程从未处理过请求。请在下次部署前修复;该行会像其他错误一样被标记为已解决。"
819
+
756
820
  rack_attack_page:
757
821
  title: 限流事件
758
822
  missing_title: 尚未安装 Rack Attack
@@ -937,6 +1001,8 @@ zh-CN:
937
1001
  other: "%{count} 秒"
938
1002
  events:
939
1003
  other: "%{count} 个事件"
1004
+ occurrences:
1005
+ other: "%{count} 次发生"
940
1006
  queries:
941
1007
  other: "%{count} 条查询"
942
1008
  std_devs: "%{count}σ"
@@ -967,7 +1033,7 @@ zh-CN:
967
1033
  unable_to_detect: 无法检测
968
1034
  no_user_model: 未检测到 User 模型
969
1035
  unable_to_query_count: 无法查询数量
970
- manual_cleanup_required: 需要手动清理
1036
+ retention_enforced_by_job: "由 RetentionCleanupJob 在您的调度器运行它时删除"
971
1037
  keep_forever: 永久保留
972
1038
  no_automatic_deletion: 不自动删除
973
1039
  run_command: 执行:
@@ -975,7 +1041,8 @@ zh-CN:
975
1041
  options:
976
1042
  enable_middleware: 捕获未处理的异常
977
1043
  enable_error_subscriber: 订阅 Rails 错误事件
978
- retention_days: 仅手动清理(执行:rails error_dashboard:cleanup_resolved DAYS=90)
1044
+ retention_days: "删除这么多天内未再出现的错误。由 RetentionCleanupJob 执行,gem 本身不会安排它运行"
1045
+ occurrence_context_limit: "每个错误保留 Breadcrumbs、系统状态和变量的最近发生次数(0 = 关闭)"
979
1046
  max_backtrace_lines: 堆栈跟踪深度上限
980
1047
  sampling_rate: 错误捕获的采样比例
981
1048
  application_name: 本应用的名称(未设置时自动检测)
@@ -989,7 +1056,7 @@ zh-CN:
989
1056
  total_users_for_impact: 用于计算影响百分比(未设置时自动检测)
990
1057
  async_logging: 在后台任务中处理
991
1058
  async_adapter: 任务队列适配器
992
- enable_rate_limiting: 对 API 请求限流
1059
+ enable_rate_limiting: "按 IP 和路径限制仪表盘请求"
993
1060
  rate_limit_per_minute: 每个 IP 每分钟的请求数
994
1061
  enable_slack_notifications: 实时通知
995
1062
  slack_webhook_url: Slack webhook 地址
@@ -998,10 +1065,15 @@ zh-CN:
998
1065
  notification_email_from: 发件人地址
999
1066
  enable_discord_notifications: 通过 Discord webhook 通知
1000
1067
  discord_webhook_url: Discord webhook 地址
1068
+ enable_telegram_notifications: "Telegram Bot API 通知"
1069
+ telegram_bot_token: "来自 @BotFather 的机器人令牌(设置环境变量 TELEGRAM_BOT_TOKEN)"
1070
+ telegram_chat_id: "机器人发送消息的聊天、群组或频道"
1071
+ telegram_api_base_url: "Bot API 服务器(仅在使用代理或自建服务器时更改)"
1001
1072
  enable_pagerduty_notifications: 严重错误升级
1002
1073
  pagerduty_integration_key: PagerDuty API 密钥
1003
1074
  enable_webhook_notifications: 通用 webhook 地址
1004
1075
  webhook_urls: 自定义 webhook 地址
1076
+ webhook_signing_secret: 使用 HMAC-SHA256 签名发出的 webhook(设置环境变量 WEBHOOK_SIGNING_SECRET)
1005
1077
  enable_scheduled_digests: 每日/每周摘要邮件。可通过 SolidQueue、Sidekiq 或 cron 调度
1006
1078
  digest_frequency: 摘要邮件的发送频率
1007
1079
  digest_recipients: 邮件地址(默认使用通知收件人)
@@ -1019,7 +1091,7 @@ zh-CN:
1019
1091
  source_code_context_lines: 目标行前后显示的行数
1020
1092
  enable_git_blame: 显示 git blame 信息
1021
1093
  source_code_cache_ttl: 源代码缓存时长
1022
- only_show_app_code_source: 出于安全考虑隐藏 gems/stdlib
1094
+ only_show_app_code_source: "在源码查看器中隐藏 gem 和 vendor 代码"
1023
1095
  git_branch_strategy: 分支解析策略
1024
1096
  enable_breadcrumbs: 记录请求的活动轨迹
1025
1097
  breadcrumb_buffer_size: 每个请求最多记录的 breadcrumbs 数
@@ -1043,6 +1115,7 @@ zh-CN:
1043
1115
  enable_diagnostic_dump: 按需生成系统状态快照
1044
1116
  enable_crash_capture: 用 at_exit 钩子捕获未处理的崩溃
1045
1117
  enable_coverage_tracking: 诊断模式 — 在面板中启用后,可在源码视图中看到已执行的行(Ruby 3.2+)
1118
+ enable_missing_translation_tracking: "按语言区域和键统计应用的 I18n 缺失(包装 I18n.exception_handler)"
1046
1119
  enable_rack_attack_tracking: 将 throttle/blocklist 事件记录到独立数据表
1047
1120
  enable_actioncable_tracking: 将 WebSocket 频道事件记录为 breadcrumbs
1048
1121
  enable_activestorage_tracking: 将上传/下载/删除记录为 breadcrumbs
@@ -1088,6 +1161,8 @@ zh-CN:
1088
1161
  breadcrumbs: Breadcrumbs
1089
1162
  system_health: 系统状态
1090
1163
  swallowed_exceptions: 被吞异常检测
1164
+ missing_translations: "缺失翻译跟踪"
1165
+ boot_errors: "启动错误捕获"
1091
1166
  rack_attack: Rack Attack 跟踪
1092
1167
  actioncable: ActionCable 跟踪
1093
1168
  activestorage: ActiveStorage 跟踪
@@ -1450,6 +1525,13 @@ zh-CN:
1450
1525
  guide_link: ActionCable 指南
1451
1526
  activestorage:
1452
1527
  title: ActiveStorage 状态
1528
+ probe:
1529
+ title: "服务可达性"
1530
+ reachable: "可达"
1531
+ unreachable: "不可达"
1532
+ service: "服务:%{name}"
1533
+ latency: "往返 %{ms} ms"
1534
+ how: "加载本页时对一个永远不会存储的键做一次存在性检查。GET /health 执行同样的探测。"
1453
1535
  empty:
1454
1536
  title: 未找到 ActiveStorage 事件
1455
1537
  message: 最近 %{days} 天的错误 breadcrumbs 中未检测到 ActiveStorage 的服务操作。
data/config/routes.rb CHANGED
@@ -7,6 +7,10 @@ RailsErrorDashboard::Engine.routes.draw do
7
7
  # Settings page
8
8
  get "settings", to: "errors#settings", as: :settings
9
9
 
10
+ # Health check (JSON): error database, queue, storm breaker, gem version.
11
+ # 200 when the dashboard can do its job, 503 when the error DB is unreachable.
12
+ get "health", to: "health#show", as: :health
13
+
10
14
  # Per-user dashboard language, persisted in the session (P5-T1).
11
15
  # POST only: changing it is a state change, and a GET would let any link or
12
16
  # prefetch alter the user's language.
@@ -41,6 +45,8 @@ RailsErrorDashboard::Engine.routes.draw do
41
45
  get :database_health_summary
42
46
  get :swallowed_exceptions
43
47
  get :rack_attack_summary
48
+ get :missing_translations
49
+ get :boot_errors
44
50
  get :actioncable_health_summary
45
51
  get :activestorage_health_summary
46
52
  get :llm_health_summary
@@ -26,7 +26,7 @@ class CreateRailsErrorDashboardCompleteSchema < ActiveRecord::Migration[7.0]
26
26
  t.string :error_type, null: false
27
27
  t.text :message, null: false
28
28
  t.text :backtrace
29
- t.integer :user_id
29
+ t.column :user_id, user_id_type
30
30
  t.text :request_url
31
31
  t.text :request_params
32
32
  t.text :user_agent
@@ -157,7 +157,7 @@ class CreateRailsErrorDashboardCompleteSchema < ActiveRecord::Migration[7.0]
157
157
  create_table :rails_error_dashboard_error_occurrences do |t|
158
158
  t.bigint :error_log_id, null: false
159
159
  t.datetime :occurred_at, null: false
160
- t.integer :user_id
160
+ t.column :user_id, user_id_type
161
161
  t.string :request_id
162
162
  t.string :session_id
163
163
  t.timestamps
@@ -275,4 +275,12 @@ class CreateRailsErrorDashboardCompleteSchema < ActiveRecord::Migration[7.0]
275
275
  drop_table :rails_error_dashboard_error_logs, if_exists: true
276
276
  drop_table :rails_error_dashboard_applications, if_exists: true
277
277
  end
278
+
279
+ private
280
+
281
+ # Same type as the app's primary keys, as ActiveStorage does for record_id.
282
+ def user_id_type
283
+ config = Rails.configuration.generators
284
+ config.options[config.orm][:primary_key_type] || :integer
285
+ end
278
286
  end
@@ -3,9 +3,9 @@ class BackfillApplicationForExistingErrors < ActiveRecord::Migration[7.0]
3
3
  return if RailsErrorDashboard::ErrorLog.count.zero?
4
4
 
5
5
  # Create default application
6
- default_name = ENV['APPLICATION_NAME'] ||
7
- (defined?(Rails) && Rails.application.class.module_parent_name) ||
8
- 'Legacy Application'
6
+ # The name new errors are captured under, config.application_name first,
7
+ # so old and new errors land in the same Application
8
+ default_name = RailsErrorDashboard.resolved_application_name
9
9
 
10
10
  app = RailsErrorDashboard::Application.find_or_create_by!(name: default_name) do |a|
11
11
  a.description = 'Auto-created during migration for existing errors'
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ # One row per (locale, key, application) the host app failed to translate.
4
+ #
5
+ # A missing translation is not an error: Rails renders "translation missing"
6
+ # (or the humanized key) and the request succeeds, so it never reaches
7
+ # error_logs and nothing in the dashboard would otherwise show it. The
8
+ # tracker counts misses in memory and upserts them here at the end of the
9
+ # request or job that produced them -- a loop rendering a missing key a
10
+ # thousand times is one UPDATE, not a thousand INSERTs.
11
+ class CreateMissingTranslations < ActiveRecord::Migration[7.0]
12
+ def change
13
+ # Guard against the squashed schema migration having already created this
14
+ # table -- without it, every later migration is silently cancelled.
15
+ return if table_exists?(:rails_error_dashboard_missing_translations)
16
+
17
+ create_table :rails_error_dashboard_missing_translations do |t|
18
+ # BCP 47 tags ("pt-BR", "zh-Hans-CN", "en-US-x-twain") fit in 35.
19
+ t.string :locale, null: false, limit: 35
20
+ # The full dotted key as I18n normalised it ("users.show.greeting").
21
+ # Capped at 191 so the unique upsert index stays inside MySQL's
22
+ # 3072-byte utf8mb4 limit: (35 + 191) * 4 + application_id. The tracker
23
+ # truncates before the value reaches the index.
24
+ t.string :translation_key, null: false, limit: 191
25
+ # First host call site seen for this key ("app/views/users/show.html.erb:12").
26
+ # First-write-wins; not part of the row's identity.
27
+ t.string :source, limit: 250
28
+ t.bigint :miss_count, null: false, default: 0
29
+ t.datetime :first_seen_at, null: false
30
+ t.datetime :last_seen_at, null: false
31
+ # Nullable: a miss recorded before application scoping, or with no
32
+ # application_name configured, applies to the whole install.
33
+ t.bigint :application_id
34
+ t.timestamps
35
+ end
36
+
37
+ # Named explicitly: an auto-generated name would exceed PostgreSQL's
38
+ # 63-character limit and fail during the HOST app's deploy.
39
+ add_index :rails_error_dashboard_missing_translations,
40
+ [ :locale, :translation_key, :application_id ],
41
+ unique: true,
42
+ name: "index_red_missing_translations_upsert_key"
43
+
44
+ # The dashboard reads "misses seen in the last N days" and retention
45
+ # prunes by the same column.
46
+ add_index :rails_error_dashboard_missing_translations,
47
+ :last_seen_at,
48
+ name: "index_red_missing_translations_on_last_seen_at"
49
+ end
50
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The moment-of-failure context of individual occurrences: breadcrumbs, system
4
+ # health, local and instance variables, as the error row stores them for its
5
+ # latest capture. The error row keeps only the latest; this keeps the last
6
+ # config.occurrence_context_limit occurrences per error (default 25).
7
+ #
8
+ # A side table, not columns on error_occurrences: the co-occurrence queries
9
+ # load occurrence rows whole, and these payloads run to several KB.
10
+ class CreateRailsErrorDashboardErrorOccurrenceContexts < ActiveRecord::Migration[7.0]
11
+ def change
12
+ # Guard against the squashed schema migration having already created this
13
+ # table -- without it, every later migration is silently cancelled.
14
+ return if table_exists?(:rails_error_dashboard_error_occurrence_contexts)
15
+
16
+ create_table :rails_error_dashboard_error_occurrence_contexts do |t|
17
+ t.bigint :error_occurrence_id, null: false
18
+ # Denormalized from the occurrence, so trimming to the newest N per error
19
+ # and retention's delete-by-error need no join
20
+ t.bigint :error_log_id, null: false
21
+ t.text :breadcrumbs
22
+ t.text :system_health
23
+ t.text :local_variables
24
+ t.text :instance_variables
25
+ t.datetime :created_at, null: false
26
+ end
27
+
28
+ # Named explicitly: an auto-generated name would exceed PostgreSQL's
29
+ # 63-character limit and fail during the HOST app's deploy.
30
+ add_index :rails_error_dashboard_error_occurrence_contexts, :error_occurrence_id,
31
+ unique: true, name: "index_red_occurrence_contexts_on_occurrence"
32
+ add_index :rails_error_dashboard_error_occurrence_contexts, :error_log_id,
33
+ name: "index_red_occurrence_contexts_on_error_log"
34
+ end
35
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Where each capture came from: "server" for RED's own captures (middleware,
4
+ # the Rails.error subscriber, jobs, crash import), "client" for errors sent
5
+ # through ManualErrorReporter by a browser, mobile app or other client.
6
+ #
7
+ # error_occurrences.capture_source how this occurrence was captured
8
+ # error_logs.backtrace_source where the backtrace the error page shows
9
+ # came from (the group's backtrace can be
10
+ # replaced by a later capture's)
11
+ #
12
+ # A client's backtrace describes code that ran on the client, so the features
13
+ # that read this app's source files for a frame (View Source, git blame, the
14
+ # snippets in Copy for LLM and AI help) skip it.
15
+ #
16
+ # Nullable, no default: NULL is a row from before this migration and reads as
17
+ # server, which is how those rows have always been treated. No index: nothing
18
+ # queries by it. Not part of the fingerprint: a client report and a server
19
+ # capture of the same error stay one group.
20
+ #
21
+ # up/down rather than `change`: the column_exists? guards that make `up` safe
22
+ # to re-run would make a reversed `change` skip the removal.
23
+ class AddCaptureSourceToErrorOccurrencesAndErrorLogs < ActiveRecord::Migration[7.0]
24
+ COLUMNS = {
25
+ rails_error_dashboard_error_occurrences: :capture_source,
26
+ rails_error_dashboard_error_logs: :backtrace_source
27
+ }.freeze
28
+
29
+ def up
30
+ COLUMNS.each do |table, column|
31
+ next unless table_exists?(table)
32
+ next if column_exists?(table, column)
33
+
34
+ add_column table, column, :string, limit: 10
35
+ end
36
+ end
37
+
38
+ def down
39
+ COLUMNS.each do |table, column|
40
+ next unless table_exists?(table)
41
+ next unless column_exists?(table, column)
42
+
43
+ remove_column table, column
44
+ end
45
+ end
46
+ end