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.
- checksums.yaml +4 -4
- data/README.md +19 -8
- data/app/controllers/rails_error_dashboard/application_controller.rb +28 -4
- data/app/controllers/rails_error_dashboard/errors_controller.rb +97 -4
- data/app/controllers/rails_error_dashboard/health_controller.rb +38 -0
- data/app/controllers/rails_error_dashboard/webhooks_controller.rb +10 -9
- data/app/helpers/rails_error_dashboard/application_helper.rb +2 -2
- data/app/helpers/rails_error_dashboard/backtrace_helper.rb +63 -16
- data/app/helpers/rails_error_dashboard/overview_helper.rb +4 -0
- data/app/jobs/rails_error_dashboard/baseline_alert_job.rb +20 -2
- data/app/jobs/rails_error_dashboard/cascade_detection_job.rb +30 -0
- data/app/jobs/rails_error_dashboard/concerns/plain_channel_message.rb +33 -6
- data/app/jobs/rails_error_dashboard/discord_error_notification_job.rb +1 -1
- data/app/jobs/rails_error_dashboard/pagerduty_error_notification_job.rb +1 -1
- data/app/jobs/rails_error_dashboard/retention_cleanup_job.rb +31 -0
- data/app/jobs/rails_error_dashboard/scheduled_digest_job.rb +6 -1
- data/app/jobs/rails_error_dashboard/slack_error_notification_job.rb +1 -1
- data/app/jobs/rails_error_dashboard/telegram_error_notification_job.rb +25 -0
- data/app/jobs/rails_error_dashboard/webhook_error_notification_job.rb +2 -18
- data/app/models/rails_error_dashboard/error_log.rb +54 -20
- data/app/models/rails_error_dashboard/error_occurrence.rb +1 -0
- data/app/models/rails_error_dashboard/error_occurrence_context.rb +14 -0
- data/app/models/rails_error_dashboard/missing_translation.rb +43 -0
- data/app/views/layouts/rails_error_dashboard.html.erb +14 -2
- data/app/views/rails_error_dashboard/errors/_captured_occurrences.html.erb +45 -0
- data/app/views/rails_error_dashboard/errors/_error_info.html.erb +12 -3
- data/app/views/rails_error_dashboard/errors/_occurrence_banner.html.erb +12 -0
- data/app/views/rails_error_dashboard/errors/_sidebar_metadata.html.erb +3 -1
- data/app/views/rails_error_dashboard/errors/_source_code.html.erb +3 -9
- data/app/views/rails_error_dashboard/errors/activestorage_health_summary.html.erb +28 -0
- data/app/views/rails_error_dashboard/errors/boot_errors.html.erb +130 -0
- data/app/views/rails_error_dashboard/errors/index.html.erb +1 -1
- data/app/views/rails_error_dashboard/errors/missing_translations.html.erb +143 -0
- data/app/views/rails_error_dashboard/errors/overview.html.erb +10 -7
- data/app/views/rails_error_dashboard/errors/platform_comparison.html.erb +16 -11
- data/app/views/rails_error_dashboard/errors/settings/_value_badge.html.erb +7 -4
- data/app/views/rails_error_dashboard/errors/settings.html.erb +23 -8
- data/app/views/rails_error_dashboard/errors/show.html.erb +7 -4
- data/config/locales/de.yml +87 -5
- data/config/locales/en.yml +99 -4
- data/config/locales/es.yml +88 -5
- data/config/locales/fr.yml +88 -5
- data/config/locales/it.yml +88 -5
- data/config/locales/ja.yml +86 -4
- data/config/locales/pl.yml +89 -5
- data/config/locales/pt-BR.yml +88 -5
- data/config/locales/ru.yml +89 -5
- data/config/locales/uk.yml +89 -5
- data/config/locales/zh-CN.yml +86 -4
- data/config/routes.rb +6 -0
- data/db/migrate/20251223000000_create_rails_error_dashboard_complete_schema.rb +10 -2
- data/db/migrate/20260106094256_backfill_application_for_existing_errors.rb +3 -3
- data/db/migrate/20261010000001_create_missing_translations.rb +50 -0
- data/db/migrate/20261015000001_create_rails_error_dashboard_error_occurrence_contexts.rb +35 -0
- data/db/migrate/20261015000002_add_capture_source_to_error_occurrences_and_error_logs.rb +46 -0
- data/lib/generators/rails_error_dashboard/install/install_generator.rb +9 -4
- data/lib/generators/rails_error_dashboard/install/templates/initializer.rb +41 -5
- data/lib/rails_error_dashboard/commands/assign_error.rb +24 -1
- data/lib/rails_error_dashboard/commands/batch_mute_errors.rb +11 -2
- data/lib/rails_error_dashboard/commands/batch_resolve_errors.rb +16 -3
- data/lib/rails_error_dashboard/commands/batch_unmute_errors.rb +7 -2
- data/lib/rails_error_dashboard/commands/find_or_increment_error.rb +6 -0
- data/lib/rails_error_dashboard/commands/flush_missing_translations.rb +101 -0
- data/lib/rails_error_dashboard/commands/flush_rack_attack_events.rb +2 -4
- data/lib/rails_error_dashboard/commands/flush_storm_counts.rb +7 -6
- data/lib/rails_error_dashboard/commands/flush_swallowed_exceptions.rb +2 -4
- data/lib/rails_error_dashboard/commands/log_error.rb +20 -8
- data/lib/rails_error_dashboard/commands/record_occurrence_context.rb +60 -0
- data/lib/rails_error_dashboard/commands/resolve_error.rb +2 -15
- data/lib/rails_error_dashboard/commands/update_error_status.rb +7 -0
- data/lib/rails_error_dashboard/configuration.rb +61 -4
- data/lib/rails_error_dashboard/engine.rb +42 -1
- data/lib/rails_error_dashboard/error_reporter.rb +4 -2
- data/lib/rails_error_dashboard/manual_error_reporter.rb +11 -1
- data/lib/rails_error_dashboard/middleware/error_catcher.rb +18 -4
- data/lib/rails_error_dashboard/middleware/rate_limiter.rb +7 -1
- data/lib/rails_error_dashboard/plugin.rb +19 -3
- data/lib/rails_error_dashboard/plugin_registry.rb +61 -12
- data/lib/rails_error_dashboard/queries/analytics_stats.rb +0 -12
- data/lib/rails_error_dashboard/queries/boot_errors.rb +76 -0
- data/lib/rails_error_dashboard/queries/co_occurring_errors.rb +59 -33
- data/lib/rails_error_dashboard/queries/dashboard_stats.rb +3 -11
- data/lib/rails_error_dashboard/queries/errors_list.rb +8 -16
- data/lib/rails_error_dashboard/queries/health_status.rb +191 -0
- data/lib/rails_error_dashboard/queries/missing_translation_summary.rb +82 -0
- data/lib/rails_error_dashboard/queries/platform_comparison.rb +14 -6
- data/lib/rails_error_dashboard/services/active_storage_probe.rb +91 -0
- data/lib/rails_error_dashboard/services/backtrace_parser.rb +13 -7
- data/lib/rails_error_dashboard/services/backtrace_processor.rb +31 -7
- data/lib/rails_error_dashboard/services/baseline_alert_payload_builder.rb +30 -0
- data/lib/rails_error_dashboard/services/crash_capture.rb +90 -2
- data/lib/rails_error_dashboard/services/digest_builder.rb +9 -6
- data/lib/rails_error_dashboard/services/error_notification_dispatcher.rb +6 -0
- data/lib/rails_error_dashboard/services/github_link_generator.rb +25 -14
- data/lib/rails_error_dashboard/services/markdown_error_formatter.rb +15 -16
- data/lib/rails_error_dashboard/services/missing_translation_handler.rb +152 -0
- data/lib/rails_error_dashboard/services/missing_translation_tracker.rb +316 -0
- data/lib/rails_error_dashboard/services/repository_url.rb +18 -0
- data/lib/rails_error_dashboard/services/resolution_callbacks.rb +44 -0
- data/lib/rails_error_dashboard/services/severity_classifier.rb +52 -0
- data/lib/rails_error_dashboard/services/similarity_calculator.rb +3 -9
- data/lib/rails_error_dashboard/services/source_code_reader.rb +23 -3
- data/lib/rails_error_dashboard/services/storm_protection/count_buffer.rb +6 -3
- data/lib/rails_error_dashboard/services/storm_protection/gate.rb +6 -1
- data/lib/rails_error_dashboard/services/telegram_delivery.rb +134 -0
- data/lib/rails_error_dashboard/services/telegram_payload_builder.rb +105 -0
- data/lib/rails_error_dashboard/services/webhook_delivery.rb +63 -0
- data/lib/rails_error_dashboard/services/webhook_signer.rb +80 -0
- data/lib/rails_error_dashboard/value_objects/occurrence_snapshot.rb +23 -0
- data/lib/rails_error_dashboard/version.rb +1 -1
- data/lib/rails_error_dashboard.rb +28 -0
- data/lib/tasks/error_dashboard.rake +2 -3
- metadata +29 -2
data/config/locales/uk.yml
CHANGED
|
@@ -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
|
-
|
|
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:
|
|
1161
|
-
|
|
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: Обмежувати запити до
|
|
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:
|
|
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 помилок не виявлено операцій
|
data/config/locales/zh-CN.yml
CHANGED
|
@@ -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
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
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.
|
|
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.
|
|
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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|