railwatch 0.9.3 → 1.0.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 (454) hide show
  1. checksums.yaml +4 -4
  2. data/AGENTS.md +37 -33
  3. data/CHANGELOG.md +66 -0
  4. data/README.md +51 -60
  5. data/config/routes.rb +0 -59
  6. data/docs/ai-and-mcp.md +9 -8
  7. data/docs/configuration.md +57 -71
  8. data/docs/faq.md +17 -25
  9. data/docs/getting-started.md +105 -117
  10. data/docs/replacing-nightwatch.md +8 -7
  11. data/docs/replacing-sentry.md +12 -16
  12. data/docs/security.md +14 -20
  13. data/docs/self-hosting.md +24 -22
  14. data/docs/testing.md +6 -14
  15. data/docs/troubleshooting.md +12 -46
  16. data/lib/generators/railwatch/install/install_generator.rb +12 -240
  17. data/lib/generators/railwatch/install/templates/initializer.rb.tt +10 -40
  18. data/lib/generators/railwatch/install/templates/post-deploy +0 -7
  19. data/lib/railwatch/authentication.rb +6 -5
  20. data/lib/railwatch/buffer.rb +9 -0
  21. data/lib/railwatch/configuration.rb +21 -151
  22. data/lib/railwatch/engine.rb +9 -118
  23. data/lib/railwatch/patches.rb +0 -11
  24. data/lib/railwatch/reporter.rb +92 -21
  25. data/lib/railwatch/sampler.rb +10 -2
  26. data/lib/railwatch/subscribers/process_info.rb +1 -2
  27. data/lib/railwatch/version.rb +1 -1
  28. data/lib/railwatch.rb +8 -64
  29. data/lib/tasks/railwatch_tasks.rake +3 -325
  30. data/llms.txt +23 -25
  31. metadata +3 -453
  32. data/app/channels/railwatch/environment_channel.rb +0 -62
  33. data/app/controllers/concerns/railwatch/telemetry_identity.rb +0 -25
  34. data/app/controllers/railwatch/alerts_controller.rb +0 -48
  35. data/app/controllers/railwatch/anomaly_rules_controller.rb +0 -31
  36. data/app/controllers/railwatch/attachments_controller.rb +0 -22
  37. data/app/controllers/railwatch/broadcasts_controller.rb +0 -15
  38. data/app/controllers/railwatch/cache_events_controller.rb +0 -30
  39. data/app/controllers/railwatch/commands_controller.rb +0 -16
  40. data/app/controllers/railwatch/comments_controller.rb +0 -11
  41. data/app/controllers/railwatch/dashboard_controller.rb +0 -70
  42. data/app/controllers/railwatch/deploys_controller.rb +0 -34
  43. data/app/controllers/railwatch/deprecations_controller.rb +0 -54
  44. data/app/controllers/railwatch/environment_scoped.rb +0 -81
  45. data/app/controllers/railwatch/exceptions_controller.rb +0 -52
  46. data/app/controllers/railwatch/executions_controller.rb +0 -13
  47. data/app/controllers/railwatch/issues_controller.rb +0 -258
  48. data/app/controllers/railwatch/jobs_controller.rb +0 -63
  49. data/app/controllers/railwatch/llm_calls_controller.rb +0 -124
  50. data/app/controllers/railwatch/logs_controller.rb +0 -40
  51. data/app/controllers/railwatch/mails_controller.rb +0 -19
  52. data/app/controllers/railwatch/notifications_controller.rb +0 -11
  53. data/app/controllers/railwatch/outgoing_requests_controller.rb +0 -19
  54. data/app/controllers/railwatch/overview_controller.rb +0 -38
  55. data/app/controllers/railwatch/people_controller.rb +0 -25
  56. data/app/controllers/railwatch/processes_controller.rb +0 -33
  57. data/app/controllers/railwatch/profiles_controller.rb +0 -65
  58. data/app/controllers/railwatch/queries_controller.rb +0 -59
  59. data/app/controllers/railwatch/releases_controller.rb +0 -75
  60. data/app/controllers/railwatch/requests_controller.rb +0 -37
  61. data/app/controllers/railwatch/saved_views_controller.rb +0 -53
  62. data/app/controllers/railwatch/scheduled_tasks_controller.rb +0 -45
  63. data/app/controllers/railwatch/spans_controller.rb +0 -47
  64. data/app/controllers/railwatch/storage_ops_controller.rb +0 -15
  65. data/app/controllers/railwatch/tenants_controller.rb +0 -44
  66. data/app/controllers/railwatch/thresholds_controller.rb +0 -47
  67. data/app/controllers/railwatch/traces_controller.rb +0 -72
  68. data/app/controllers/railwatch/transactions_controller.rb +0 -21
  69. data/app/controllers/railwatch/view_renders_controller.rb +0 -28
  70. data/app/controllers/railwatch/visits_controller.rb +0 -53
  71. data/app/frontend/components/app-content.tsx +0 -26
  72. data/app/frontend/components/app-logo-icon.tsx +0 -23
  73. data/app/frontend/components/app-logo.tsx +0 -14
  74. data/app/frontend/components/app-shell.tsx +0 -38
  75. data/app/frontend/components/app-sidebar-header.tsx +0 -54
  76. data/app/frontend/components/app-sidebar.tsx +0 -94
  77. data/app/frontend/components/app-wordmark.tsx +0 -29
  78. data/app/frontend/components/breadcrumbs.tsx +0 -46
  79. data/app/frontend/components/icon.tsx +0 -16
  80. data/app/frontend/components/input-error.tsx +0 -18
  81. data/app/frontend/components/nav-footer.tsx +0 -46
  82. data/app/frontend/components/nav-main.tsx +0 -41
  83. data/app/frontend/components/nav-user.tsx +0 -50
  84. data/app/frontend/components/railwatch/chart-hover.tsx +0 -34
  85. data/app/frontend/components/railwatch/chart-panel.tsx +0 -170
  86. data/app/frontend/components/railwatch/code.tsx +0 -28
  87. data/app/frontend/components/railwatch/command-palette.tsx +0 -132
  88. data/app/frontend/components/railwatch/copy-block.tsx +0 -52
  89. data/app/frontend/components/railwatch/copy-id.tsx +0 -49
  90. data/app/frontend/components/railwatch/cursor-load-more.tsx +0 -39
  91. data/app/frontend/components/railwatch/data-table.tsx +0 -195
  92. data/app/frontend/components/railwatch/digit-roll.tsx +0 -312
  93. data/app/frontend/components/railwatch/empty-state.tsx +0 -159
  94. data/app/frontend/components/railwatch/env-nav.tsx +0 -130
  95. data/app/frontend/components/railwatch/env-switcher.tsx +0 -145
  96. data/app/frontend/components/railwatch/filter-bar.tsx +0 -153
  97. data/app/frontend/components/railwatch/flamegraph.tsx +0 -324
  98. data/app/frontend/components/railwatch/frames.tsx +0 -295
  99. data/app/frontend/components/railwatch/json-viewer.tsx +0 -159
  100. data/app/frontend/components/railwatch/live-dot.tsx +0 -78
  101. data/app/frontend/components/railwatch/live-toggle.tsx +0 -76
  102. data/app/frontend/components/railwatch/metric-chart.tsx +0 -112
  103. data/app/frontend/components/railwatch/mobile-page-switcher.tsx +0 -89
  104. data/app/frontend/components/railwatch/onboarding.tsx +0 -91
  105. data/app/frontend/components/railwatch/origin-identity.tsx +0 -84
  106. data/app/frontend/components/railwatch/page-header.tsx +0 -42
  107. data/app/frontend/components/railwatch/percentile-picker.tsx +0 -18
  108. data/app/frontend/components/railwatch/relative-time.tsx +0 -18
  109. data/app/frontend/components/railwatch/saved-views.tsx +0 -232
  110. data/app/frontend/components/railwatch/segmented.tsx +0 -64
  111. data/app/frontend/components/railwatch/series-chart.tsx +0 -400
  112. data/app/frontend/components/railwatch/shortcuts-dialog.tsx +0 -89
  113. data/app/frontend/components/railwatch/signal-toast.tsx +0 -121
  114. data/app/frontend/components/railwatch/sort-header.tsx +0 -37
  115. data/app/frontend/components/railwatch/source-link.tsx +0 -57
  116. data/app/frontend/components/railwatch/sparkline-cell.tsx +0 -14
  117. data/app/frontend/components/railwatch/sql-block.tsx +0 -116
  118. data/app/frontend/components/railwatch/stat.tsx +0 -185
  119. data/app/frontend/components/railwatch/status-badge.tsx +0 -79
  120. data/app/frontend/components/railwatch/step-picker.tsx +0 -57
  121. data/app/frontend/components/railwatch/timeline.tsx +0 -405
  122. data/app/frontend/components/railwatch/utilisation-bar.tsx +0 -44
  123. data/app/frontend/components/railwatch/window-picker.tsx +0 -147
  124. data/app/frontend/components/ui/avatar.tsx +0 -51
  125. data/app/frontend/components/ui/badge.tsx +0 -46
  126. data/app/frontend/components/ui/breadcrumb.tsx +0 -109
  127. data/app/frontend/components/ui/button.tsx +0 -60
  128. data/app/frontend/components/ui/card.tsx +0 -92
  129. data/app/frontend/components/ui/chart.tsx +0 -374
  130. data/app/frontend/components/ui/checkbox.tsx +0 -32
  131. data/app/frontend/components/ui/collapsible.tsx +0 -31
  132. data/app/frontend/components/ui/command.tsx +0 -182
  133. data/app/frontend/components/ui/dialog.tsx +0 -158
  134. data/app/frontend/components/ui/dropdown-menu.tsx +0 -255
  135. data/app/frontend/components/ui/hover-card.tsx +0 -42
  136. data/app/frontend/components/ui/input.tsx +0 -21
  137. data/app/frontend/components/ui/label.tsx +0 -24
  138. data/app/frontend/components/ui/popover.tsx +0 -87
  139. data/app/frontend/components/ui/select.tsx +0 -185
  140. data/app/frontend/components/ui/separator.tsx +0 -28
  141. data/app/frontend/components/ui/sheet.tsx +0 -137
  142. data/app/frontend/components/ui/sidebar.tsx +0 -726
  143. data/app/frontend/components/ui/skeleton.tsx +0 -13
  144. data/app/frontend/components/ui/sonner.tsx +0 -41
  145. data/app/frontend/components/ui/table.tsx +0 -114
  146. data/app/frontend/components/ui/tabs.tsx +0 -91
  147. data/app/frontend/components/ui/textarea.tsx +0 -18
  148. data/app/frontend/components/ui/tooltip.tsx +0 -61
  149. data/app/frontend/components/user-info.tsx +0 -32
  150. data/app/frontend/components/user-menu-content.tsx +0 -94
  151. data/app/frontend/entrypoints/application.css +0 -244
  152. data/app/frontend/entrypoints/inertia.tsx +0 -92
  153. data/app/frontend/fonts/CommitMono-Bold.woff2 +0 -0
  154. data/app/frontend/fonts/CommitMono-Regular.woff2 +0 -0
  155. data/app/frontend/fonts/Roboto-Black.woff2 +0 -0
  156. data/app/frontend/fonts/Roboto-Bold.woff2 +0 -0
  157. data/app/frontend/fonts/Roboto-Medium.woff2 +0 -0
  158. data/app/frontend/fonts/Roboto-Regular.woff2 +0 -0
  159. data/app/frontend/hooks/use-appearance.tsx +0 -67
  160. data/app/frontend/hooks/use-clipboard.ts +0 -32
  161. data/app/frontend/hooks/use-flash.tsx +0 -23
  162. data/app/frontend/hooks/use-hotkeys.ts +0 -63
  163. data/app/frontend/hooks/use-initials.tsx +0 -15
  164. data/app/frontend/hooks/use-live.ts +0 -205
  165. data/app/frontend/hooks/use-mobile-navigation.ts +0 -8
  166. data/app/frontend/hooks/use-mobile.ts +0 -21
  167. data/app/frontend/hooks/use-percentile.ts +0 -24
  168. data/app/frontend/hooks/use-reduced-motion.ts +0 -25
  169. data/app/frontend/hooks/use-row-nav.ts +0 -106
  170. data/app/frontend/hooks/use-step.ts +0 -27
  171. data/app/frontend/hooks/use-ticker.ts +0 -12
  172. data/app/frontend/hooks/use-window.ts +0 -58
  173. data/app/frontend/layouts/app/app-sidebar-layout.tsx +0 -107
  174. data/app/frontend/layouts/app-layout.tsx +0 -21
  175. data/app/frontend/layouts/env-layout.tsx +0 -53
  176. data/app/frontend/layouts/persistent-layout.tsx +0 -18
  177. data/app/frontend/lib/cable.ts +0 -10
  178. data/app/frontend/lib/env-pages.ts +0 -236
  179. data/app/frontend/lib/execution-path.ts +0 -55
  180. data/app/frontend/lib/explain.ts +0 -50
  181. data/app/frontend/lib/filter.ts +0 -25
  182. data/app/frontend/lib/flamegraph.ts +0 -155
  183. data/app/frontend/lib/format.ts +0 -71
  184. data/app/frontend/lib/keyboard.ts +0 -18
  185. data/app/frontend/lib/mount.ts +0 -23
  186. data/app/frontend/lib/source-link.ts +0 -217
  187. data/app/frontend/lib/utils.ts +0 -6
  188. data/app/frontend/pages/alerts/index.tsx +0 -171
  189. data/app/frontend/pages/broadcasts/index.tsx +0 -171
  190. data/app/frontend/pages/cache_events/index.tsx +0 -127
  191. data/app/frontend/pages/commands/index.tsx +0 -143
  192. data/app/frontend/pages/deploys/index.tsx +0 -94
  193. data/app/frontend/pages/deploys/show.tsx +0 -309
  194. data/app/frontend/pages/deprecations/index.tsx +0 -176
  195. data/app/frontend/pages/exceptions/index.tsx +0 -271
  196. data/app/frontend/pages/executions/show.tsx +0 -821
  197. data/app/frontend/pages/issues/index.tsx +0 -399
  198. data/app/frontend/pages/issues/show.tsx +0 -1367
  199. data/app/frontend/pages/jobs/index.tsx +0 -381
  200. data/app/frontend/pages/jobs/klass.tsx +0 -215
  201. data/app/frontend/pages/llm_calls/index.tsx +0 -664
  202. data/app/frontend/pages/logs/index.tsx +0 -303
  203. data/app/frontend/pages/mails/index.tsx +0 -218
  204. data/app/frontend/pages/notifications/index.tsx +0 -161
  205. data/app/frontend/pages/outgoing_requests/index.tsx +0 -309
  206. data/app/frontend/pages/overview/show.tsx +0 -333
  207. data/app/frontend/pages/people/index.tsx +0 -102
  208. data/app/frontend/pages/people/show.tsx +0 -149
  209. data/app/frontend/pages/processes/index.tsx +0 -335
  210. data/app/frontend/pages/profiles/index.tsx +0 -110
  211. data/app/frontend/pages/profiles/show.tsx +0 -105
  212. data/app/frontend/pages/queries/index.tsx +0 -492
  213. data/app/frontend/pages/queries/show.tsx +0 -276
  214. data/app/frontend/pages/releases/index.tsx +0 -190
  215. data/app/frontend/pages/releases/release-health.tsx +0 -68
  216. data/app/frontend/pages/releases/release-path.ts +0 -25
  217. data/app/frontend/pages/releases/show.tsx +0 -262
  218. data/app/frontend/pages/requests/index.tsx +0 -235
  219. data/app/frontend/pages/requests/route.tsx +0 -159
  220. data/app/frontend/pages/scheduled_tasks/index.tsx +0 -220
  221. data/app/frontend/pages/spans/index.tsx +0 -229
  222. data/app/frontend/pages/spans/show.tsx +0 -220
  223. data/app/frontend/pages/storage_ops/index.tsx +0 -150
  224. data/app/frontend/pages/tenants/index.tsx +0 -236
  225. data/app/frontend/pages/tenants/show.tsx +0 -477
  226. data/app/frontend/pages/tenants/tenant-path.ts +0 -26
  227. data/app/frontend/pages/thresholds/index.tsx +0 -414
  228. data/app/frontend/pages/traces/show.tsx +0 -208
  229. data/app/frontend/pages/transactions/index.tsx +0 -220
  230. data/app/frontend/pages/view_renders/index.tsx +0 -241
  231. data/app/frontend/pages/visits/index.tsx +0 -321
  232. data/app/frontend/public/icon.png +0 -0
  233. data/app/frontend/public/icon.svg +0 -5
  234. data/app/frontend/routes/index.d.ts +0 -2136
  235. data/app/frontend/routes/index.js +0 -2112
  236. data/app/frontend/types/globals.d.ts +0 -9
  237. data/app/frontend/types/index.ts +0 -288
  238. data/app/frontend/types/vite-env.d.ts +0 -9
  239. data/app/helpers/railwatch/assets_helper.rb +0 -52
  240. data/app/jobs/railwatch/anomaly_scan_job.rb +0 -11
  241. data/app/jobs/railwatch/application_job.rb +0 -7
  242. data/app/jobs/railwatch/auto_resolve_issues_job.rb +0 -19
  243. data/app/jobs/railwatch/check_scheduled_tasks_job.rb +0 -113
  244. data/app/jobs/railwatch/detect_anomalies_job.rb +0 -171
  245. data/app/jobs/railwatch/detect_performance_issues_job.rb +0 -95
  246. data/app/jobs/railwatch/group_exceptions_job.rb +0 -91
  247. data/app/jobs/railwatch/optimize_telemetry_job.rb +0 -23
  248. data/app/jobs/railwatch/performance_scan_job.rb +0 -11
  249. data/app/jobs/railwatch/prune_telemetry_job.rb +0 -129
  250. data/app/jobs/railwatch/release_health_rollup_job.rb +0 -64
  251. data/app/jobs/railwatch/rollup_catchup_job.rb +0 -21
  252. data/app/jobs/railwatch/rollup_job.rb +0 -132
  253. data/app/jobs/railwatch/scheduled_task_scan_job.rb +0 -11
  254. data/app/models/concerns/railwatch/detection_snapshotting.rb +0 -20
  255. data/app/models/railwatch/alert.rb +0 -229
  256. data/app/models/railwatch/alert_rule.rb +0 -121
  257. data/app/models/railwatch/anomaly_rule.rb +0 -51
  258. data/app/models/railwatch/application.rb +0 -44
  259. data/app/models/railwatch/application_record.rb +0 -33
  260. data/app/models/railwatch/comment.rb +0 -36
  261. data/app/models/railwatch/deploy.rb +0 -67
  262. data/app/models/railwatch/environment.rb +0 -68
  263. data/app/models/railwatch/execution_presenter.rb +0 -185
  264. data/app/models/railwatch/filter_query.rb +0 -184
  265. data/app/models/railwatch/followup_receipt.rb +0 -27
  266. data/app/models/railwatch/ingest/batch.rb +0 -355
  267. data/app/models/railwatch/ingest/mapper.rb +0 -585
  268. data/app/models/railwatch/ingest/payload.rb +0 -96
  269. data/app/models/railwatch/ingest/rollup_absorber.rb +0 -170
  270. data/app/models/railwatch/ingest/writer.rb +0 -137
  271. data/app/models/railwatch/issue.rb +0 -277
  272. data/app/models/railwatch/issue_activity.rb +0 -23
  273. data/app/models/railwatch/issue_detection_presenter.rb +0 -309
  274. data/app/models/railwatch/issue_detection_snapshot.rb +0 -77
  275. data/app/models/railwatch/maintenance_task.rb +0 -53
  276. data/app/models/railwatch/saved_view.rb +0 -63
  277. data/app/models/railwatch/telemetry/aggregations.rb +0 -251
  278. data/app/models/railwatch/telemetry/attachment.rb +0 -31
  279. data/app/models/railwatch/telemetry/bounded_gzip.rb +0 -101
  280. data/app/models/railwatch/telemetry/broadcast.rb +0 -13
  281. data/app/models/railwatch/telemetry/cache_event.rb +0 -16
  282. data/app/models/railwatch/telemetry/child.rb +0 -53
  283. data/app/models/railwatch/telemetry/cursor_page.rb +0 -99
  284. data/app/models/railwatch/telemetry/deprecation.rb +0 -13
  285. data/app/models/railwatch/telemetry/enqueued_job.rb +0 -13
  286. data/app/models/railwatch/telemetry/exception.rb +0 -28
  287. data/app/models/railwatch/telemetry/execution.rb +0 -152
  288. data/app/models/railwatch/telemetry/export_delivery.rb +0 -32
  289. data/app/models/railwatch/telemetry/export_destination.rb +0 -66
  290. data/app/models/railwatch/telemetry/health_sample.rb +0 -72
  291. data/app/models/railwatch/telemetry/ingest_batch.rb +0 -31
  292. data/app/models/railwatch/telemetry/llm_call.rb +0 -37
  293. data/app/models/railwatch/telemetry/log.rb +0 -91
  294. data/app/models/railwatch/telemetry/mail.rb +0 -13
  295. data/app/models/railwatch/telemetry/n_plus_one.rb +0 -92
  296. data/app/models/railwatch/telemetry/notification.rb +0 -13
  297. data/app/models/railwatch/telemetry/outgoing_request.rb +0 -13
  298. data/app/models/railwatch/telemetry/person.rb +0 -50
  299. data/app/models/railwatch/telemetry/process.rb +0 -10
  300. data/app/models/railwatch/telemetry/profile.rb +0 -37
  301. data/app/models/railwatch/telemetry/query.rb +0 -57
  302. data/app/models/railwatch/telemetry/query_shape.rb +0 -45
  303. data/app/models/railwatch/telemetry/release_health.rb +0 -69
  304. data/app/models/railwatch/telemetry/rollup.rb +0 -157
  305. data/app/models/railwatch/telemetry/session.rb +0 -24
  306. data/app/models/railwatch/telemetry/span.rb +0 -19
  307. data/app/models/railwatch/telemetry/storage_op.rb +0 -13
  308. data/app/models/railwatch/telemetry/tenant.rb +0 -240
  309. data/app/models/railwatch/telemetry/transaction.rb +0 -13
  310. data/app/models/railwatch/telemetry/view_render.rb +0 -13
  311. data/app/models/railwatch/telemetry/visit.rb +0 -24
  312. data/app/models/railwatch/telemetry_record.rb +0 -107
  313. data/app/models/railwatch/threshold.rb +0 -66
  314. data/app/models/railwatch/user.rb +0 -47
  315. data/app/models/railwatch/viewer.rb +0 -13
  316. data/app/models/railwatch/window.rb +0 -66
  317. data/app/views/layouts/railwatch/dashboard.html.erb +0 -25
  318. data/db/railwatch_migrate/20260916000000_create_railwatch_tables.rb +0 -151
  319. data/db/railwatch_migrate/20260917000000_create_railwatch_maintenance_tasks.rb +0 -18
  320. data/db/railwatch_migrate/20260917120000_create_railwatch_followup_receipts.rb +0 -20
  321. data/db/railwatch_migrate/20260918120000_widen_host_user_ids.rb +0 -71
  322. data/db/railwatch_telemetry_migrate/20260903000000_enable_incremental_vacuum.rb +0 -62
  323. data/db/railwatch_telemetry_migrate/20260903000001_create_telemetry.rb +0 -481
  324. data/db/railwatch_telemetry_migrate/20260903000002_rename_tenant_to_app_tenant.rb +0 -14
  325. data/db/railwatch_telemetry_migrate/20260903000003_add_statement_count_to_transactions.rb +0 -9
  326. data/db/railwatch_telemetry_migrate/20260903000004_add_role_and_channel.rb +0 -10
  327. data/db/railwatch_telemetry_migrate/20260903000005_add_locals_to_exceptions.rb +0 -9
  328. data/db/railwatch_telemetry_migrate/20260903000006_add_spans_health_vitals_and_fts.rb +0 -82
  329. data/db/railwatch_telemetry_migrate/20260903000007_rename_span_attributes_to_payload.rb +0 -10
  330. data/db/railwatch_telemetry_migrate/20260903000008_add_profiles_and_attachments.rb +0 -60
  331. data/db/railwatch_telemetry_migrate/20260903000009_add_truncated_to_attachments.rb +0 -9
  332. data/db/railwatch_telemetry_migrate/20260903000010_create_sessions_and_release_health.rb +0 -51
  333. data/db/railwatch_telemetry_migrate/20260903000011_add_fingerprint_to_exceptions.rb +0 -11
  334. data/db/railwatch_telemetry_migrate/20260904000012_add_failed_to_broadcasts.rb +0 -10
  335. data/db/railwatch_telemetry_migrate/20260904010000_add_filter_cursor_indexes.rb +0 -37
  336. data/db/railwatch_telemetry_migrate/20260904020000_add_n_plus_ones_execution_id_index.rb +0 -11
  337. data/db/railwatch_telemetry_migrate/20260904120000_create_query_shapes.rb +0 -15
  338. data/db/railwatch_telemetry_migrate/20260906120000_add_backpressure_factor_to_ingest_batches.rb +0 -9
  339. data/db/railwatch_telemetry_migrate/20260907000000_rename_lantern_version_on_processes.rb +0 -10
  340. data/db/railwatch_telemetry_migrate/20260913000000_rename_nightrail_version_on_processes.rb +0 -9
  341. data/db/railwatch_telemetry_migrate/20260914000000_drop_orphan_durable_ingest_tables.rb +0 -18
  342. data/db/railwatch_telemetry_migrate/20260914010000_drop_orphan_durable_ingest_columns.rb +0 -25
  343. data/db/railwatch_telemetry_migrate/20260915000000_create_llm_calls.rb +0 -55
  344. data/db/railwatch_telemetry_migrate/20260915120000_add_detail_to_llm_calls.rb +0 -21
  345. data/db/railwatch_telemetry_migrate/20260915200000_add_explained_index_to_queries.rb +0 -16
  346. data/db/railwatch_telemetry_migrate/20260915210000_add_slowest_index_to_queries.rb +0 -18
  347. data/db/railwatch_telemetry_migrate/20260917010000_add_batch_ledger_to_ingest_batches.rb +0 -16
  348. data/db/railwatch_telemetry_migrate/20260919000100_create_export_queue.rb +0 -146
  349. data/db/railwatch_telemetry_migrate/20260922000000_add_covering_indexes_for_dashboard_aggregates.rb +0 -34
  350. data/db/railwatch_telemetry_migrate/20260923000000_add_people_index.rb +0 -15
  351. data/db/railwatch_telemetry_migrate/20260925000000_add_tenant_summary_index.rb +0 -17
  352. data/db/railwatch_telemetry_migrate/20261008000000_add_in_transaction_to_outgoing_requests.rb +0 -10
  353. data/docs/embedded.md +0 -503
  354. data/lib/puma/plugin/railwatch.rb +0 -215
  355. data/lib/railwatch/dashboard_assets.rb +0 -45
  356. data/lib/railwatch/embedded.rb +0 -50
  357. data/lib/railwatch/export/client.rb +0 -124
  358. data/lib/railwatch/export/lease.rb +0 -56
  359. data/lib/railwatch/export/outbox.rb +0 -361
  360. data/lib/railwatch/export/policy.rb +0 -79
  361. data/lib/railwatch/export/sender.rb +0 -130
  362. data/lib/railwatch/ingest_request_body_limit.rb +0 -10
  363. data/lib/railwatch/maintenance.rb +0 -194
  364. data/lib/railwatch/patches/migration_busy_timeout.rb +0 -73
  365. data/lib/railwatch/transport/local.rb +0 -78
  366. data/lib/railwatch/transport/socket.rb +0 -183
  367. data/lib/railwatch/writer.rb +0 -370
  368. data/public/railwatch/assets/CommitMono-Bold-D6h61ieg.woff2 +0 -0
  369. data/public/railwatch/assets/CommitMono-Regular-zr8w7Obm.woff2 +0 -0
  370. data/public/railwatch/assets/Roboto-Black-auA4GeOK.woff2 +0 -0
  371. data/public/railwatch/assets/Roboto-Bold-CJLnO8j1.woff2 +0 -0
  372. data/public/railwatch/assets/Roboto-Medium-Cm2bwKpj.woff2 +0 -0
  373. data/public/railwatch/assets/Roboto-Regular-Chaq1-PV.woff2 +0 -0
  374. data/public/railwatch/assets/app-layout-by7G3sa1.js +0 -41
  375. data/public/railwatch/assets/application-Cc5v_Vk9.css +0 -1
  376. data/public/railwatch/assets/arrow-up-DCZ7emux.js +0 -1
  377. data/public/railwatch/assets/braces-CykT5_5u.js +0 -1
  378. data/public/railwatch/assets/card-C-PPSA0g.js +0 -1
  379. data/public/railwatch/assets/chart-Cov79YC5.js +0 -39
  380. data/public/railwatch/assets/chart-hover-DdK4E-2h.js +0 -1
  381. data/public/railwatch/assets/chart-panel-BPaYRXJB.js +0 -1
  382. data/public/railwatch/assets/code-BYjC4YZ_.js +0 -1
  383. data/public/railwatch/assets/copy-id-LPfoj9cC.js +0 -1
  384. data/public/railwatch/assets/cursor-load-more-DSdhgpGl.js +0 -1
  385. data/public/railwatch/assets/data-table-CQ_YRWQs.js +0 -1
  386. data/public/railwatch/assets/env-layout-CdMWbUkm.js +0 -1
  387. data/public/railwatch/assets/execution-path-7MYt-Qbn.js +0 -1
  388. data/public/railwatch/assets/filter-bar-CrzpQ_vD.js +0 -1
  389. data/public/railwatch/assets/flamegraph-BIFEdTAg.js +0 -2
  390. data/public/railwatch/assets/format-B9SDkrWj.js +0 -1
  391. data/public/railwatch/assets/frames-C5t-x4f0.js +0 -1
  392. data/public/railwatch/assets/index--NQ7us-L.js +0 -1
  393. data/public/railwatch/assets/index-2kgYe_99.js +0 -1
  394. data/public/railwatch/assets/index-4YEPHxtO.js +0 -1
  395. data/public/railwatch/assets/index-AOCC1GDJ.js +0 -1
  396. data/public/railwatch/assets/index-BEo-u0eO.js +0 -1
  397. data/public/railwatch/assets/index-BJ1V3w2N.js +0 -1
  398. data/public/railwatch/assets/index-BPoHsBKb.js +0 -1
  399. data/public/railwatch/assets/index-BZzyGUM3.js +0 -1
  400. data/public/railwatch/assets/index-BgVq7yKL.js +0 -1
  401. data/public/railwatch/assets/index-BqFGEZvW.js +0 -1
  402. data/public/railwatch/assets/index-ByuHJ1CH.js +0 -1
  403. data/public/railwatch/assets/index-C2j50AL2.js +0 -1
  404. data/public/railwatch/assets/index-CLEdTkvW.js +0 -1
  405. data/public/railwatch/assets/index-CWgq8qBu.js +0 -1
  406. data/public/railwatch/assets/index-CXQ7_oVj.js +0 -2
  407. data/public/railwatch/assets/index-CmyYrjS2.js +0 -1
  408. data/public/railwatch/assets/index-Cn9_HjDo.js +0 -1
  409. data/public/railwatch/assets/index-CsMAm9UK.js +0 -1
  410. data/public/railwatch/assets/index-D0Kk_oGY.js +0 -1
  411. data/public/railwatch/assets/index-DC9ZptJh.js +0 -1
  412. data/public/railwatch/assets/index-DNzg7jmV.js +0 -1
  413. data/public/railwatch/assets/index-DQ8uJO5f.js +0 -1
  414. data/public/railwatch/assets/index-Deq14BxB.js +0 -1
  415. data/public/railwatch/assets/index-IaV3Blzp.js +0 -1
  416. data/public/railwatch/assets/index-TYNWcJQs.js +0 -1
  417. data/public/railwatch/assets/index-WonH2fcZ.js +0 -1
  418. data/public/railwatch/assets/index-WxWUaH_d.js +0 -1
  419. data/public/railwatch/assets/index-mUNrl4ky.js +0 -1
  420. data/public/railwatch/assets/inertia-DfNRARuA.js +0 -97
  421. data/public/railwatch/assets/json-viewer-DMsF5glp.js +0 -1
  422. data/public/railwatch/assets/klass-CmHYk5rz.js +0 -1
  423. data/public/railwatch/assets/origin-identity-1R3bqa3R.js +0 -1
  424. data/public/railwatch/assets/percentile-picker-73z5PAps.js +0 -1
  425. data/public/railwatch/assets/relative-time-Dsc_cCdX.js +0 -1
  426. data/public/railwatch/assets/release-health-BV5OaOWd.js +0 -1
  427. data/public/railwatch/assets/route-CWh58C7K.js +0 -1
  428. data/public/railwatch/assets/segmented-DB4zh-Am.js +0 -1
  429. data/public/railwatch/assets/select-DBKjFlOX.js +0 -1
  430. data/public/railwatch/assets/series-chart-BaiDG795.js +0 -1
  431. data/public/railwatch/assets/show-B4ICCFGh.js +0 -1
  432. data/public/railwatch/assets/show-B5lOU9Gv.js +0 -1
  433. data/public/railwatch/assets/show-BYPB74t4.js +0 -2
  434. data/public/railwatch/assets/show-C7A0m2M8.js +0 -1
  435. data/public/railwatch/assets/show-DT5X1zqE.js +0 -1
  436. data/public/railwatch/assets/show-D_NBdXsn.js +0 -2
  437. data/public/railwatch/assets/show-DgPCnRg8.js +0 -1
  438. data/public/railwatch/assets/show-Yl69HF0x.js +0 -1
  439. data/public/railwatch/assets/show-_QxuUQCm.js +0 -3
  440. data/public/railwatch/assets/show-g__jnN36.js +0 -1
  441. data/public/railwatch/assets/show-l4ztfjl9.js +0 -1
  442. data/public/railwatch/assets/sort-header-BcG8ulEH.js +0 -1
  443. data/public/railwatch/assets/source-link-B2183i2-.js +0 -1
  444. data/public/railwatch/assets/sparkline-cell-B47ipscB.js +0 -1
  445. data/public/railwatch/assets/stat-DT2hhsqS.js +0 -1
  446. data/public/railwatch/assets/status-badge-CmbRKlY7.js +0 -1
  447. data/public/railwatch/assets/tenant-path-C4rNGauE.js +0 -1
  448. data/public/railwatch/assets/use-clipboard-oTNeP4ax.js +0 -1
  449. data/public/railwatch/assets/use-live-CLZZj245.js +0 -1
  450. data/public/railwatch/icon.png +0 -0
  451. data/public/railwatch/icon.svg +0 -5
  452. data/public/railwatch/manifest.json +0 -1245
  453. data/public/railwatch/rails-vite.json +0 -1
  454. /data/{app/frontend/lib → lib/generators/railwatch/install/templates}/railwatch.ts +0 -0
@@ -1,102 +0,0 @@
1
- import { router, usePage } from "@inertiajs/react"
2
- import { Users } from "lucide-react"
3
-
4
- import { DataTable } from "@/components/railwatch/data-table"
5
- import { EmptyState } from "@/components/railwatch/empty-state"
6
- import { PageHeader } from "@/components/railwatch/page-header"
7
- import EnvLayout from "@/layouts/env-layout"
8
- import { ago, count } from "@/lib/format"
9
- import * as R from "@/routes"
10
- import type { SharedProps } from "@/types"
11
-
12
- interface Person {
13
- ref: string
14
- requests: number
15
- errors: number
16
- last_seen_at: string
17
- name: string | null
18
- email: string | null
19
- tenant: string | null
20
- }
21
- interface Props {
22
- people: Person[]
23
- }
24
-
25
- export default function People(p: Props) {
26
- const { environment } = usePage<SharedProps>().props
27
- const a = environment!.application_id
28
- const e = environment!.id
29
- const personHref = (x: Person) =>
30
- R.applicationEnvironmentPersonPath(a, e, x.ref)
31
- return (
32
- <EnvLayout title="Users">
33
- <PageHeader
34
- title="Users"
35
- description="Your application's users as seen in telemetry, with their activity and errors."
36
- />
37
- <DataTable
38
- rows={p.people}
39
- rowKey={(x) => x.ref}
40
- empty={
41
- <EmptyState
42
- icon={Users}
43
- title="No authenticated activity in this window"
44
- description="People appear here once requests are tagged with an authenticated user."
45
- />
46
- }
47
- onRowClick={(x) => router.visit(personHref(x))}
48
- keyboardNav={{
49
- onOpen: (x, opts) =>
50
- opts?.newTab
51
- ? window.open(personHref(x), "_blank")
52
- : router.visit(personHref(x)),
53
- }}
54
- columns={[
55
- {
56
- key: "n",
57
- header: "User",
58
- cell: (x) => (
59
- <span className="text-xs">
60
- <span className="font-medium">{x.name ?? x.ref}</span>
61
- {x.email && (
62
- <span className="text-muted-foreground"> · {x.email}</span>
63
- )}
64
- </span>
65
- ),
66
- },
67
- {
68
- key: "t",
69
- hideOnMobile: true,
70
- header: "Tenant",
71
- cell: (x) => (
72
- <span className="font-mono text-xs">{x.tenant ?? ""}</span>
73
- ),
74
- },
75
- {
76
- key: "r",
77
- header: "Requests",
78
- align: "right",
79
- cell: (x) => count(x.requests),
80
- },
81
- {
82
- key: "e",
83
- hideOnMobile: true,
84
- header: "Errors",
85
- align: "right",
86
- cell: (x) => (
87
- <span className={x.errors ? "text-destructive" : ""}>
88
- {x.errors}
89
- </span>
90
- ),
91
- },
92
- {
93
- key: "l",
94
- header: "Last seen",
95
- align: "right",
96
- cell: (x) => ago(x.last_seen_at),
97
- },
98
- ]}
99
- />
100
- </EnvLayout>
101
- )
102
- }
@@ -1,149 +0,0 @@
1
- import { Link, router, usePage } from "@inertiajs/react"
2
-
3
- import { DataTable } from "@/components/railwatch/data-table"
4
- import { PageHeader } from "@/components/railwatch/page-header"
5
- import {
6
- IssueStatusBadge,
7
- StatusBadge,
8
- } from "@/components/railwatch/status-badge"
9
- import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
10
- import EnvLayout from "@/layouts/env-layout"
11
- import { ago, ms, when } from "@/lib/format"
12
- import * as R from "@/routes"
13
- import type { ExecutionRow, SharedProps } from "@/types"
14
-
15
- interface Props {
16
- person: {
17
- person_ref: string
18
- name: string
19
- email: string | null
20
- tenant: string | null
21
- first_seen_at: string
22
- last_seen_at: string
23
- } | null
24
- person_ref: string
25
- executions: ExecutionRow[]
26
- issues: {
27
- id: number
28
- key: string
29
- title: string
30
- status: string
31
- count: number
32
- }[]
33
- }
34
-
35
- export default function PersonShow(p: Props) {
36
- const { environment, window } = usePage<SharedProps>().props
37
- const a = environment!.application_id
38
- const e = environment!.id
39
- const name = p.person?.name ?? p.person_ref
40
- return (
41
- <EnvLayout
42
- title={name}
43
- crumbs={[
44
- {
45
- title: "Users",
46
- href: R.applicationEnvironmentPeoplePath(a, e, { window }),
47
- },
48
- { title: name, href: "#" },
49
- ]}
50
- >
51
- <PageHeader
52
- title={name}
53
- description={
54
- p.person
55
- ? `${p.person.email ?? ""} ${p.person.tenant ? `· tenant ${p.person.tenant}` : ""} · first seen ${ago(p.person.first_seen_at)} · last seen ${ago(p.person.last_seen_at)}`
56
- : "Not yet identified."
57
- }
58
- />
59
- {p.issues.length > 0 && (
60
- <Card>
61
- <CardHeader>
62
- <CardTitle>Issues affecting this user</CardTitle>
63
- </CardHeader>
64
- <CardContent>
65
- <DataTable
66
- rows={p.issues}
67
- rowKey={(i) => i.id}
68
- columns={[
69
- {
70
- key: "k",
71
- header: "Issue",
72
- cell: (i) => (
73
- <Link
74
- className="font-mono text-xs hover:underline"
75
- href={R.issuePath(i.id)}
76
- >
77
- {i.key}
78
- </Link>
79
- ),
80
- },
81
- {
82
- key: "t",
83
- header: "Title",
84
- cell: (i) => (
85
- <span className="line-clamp-1 text-xs">{i.title}</span>
86
- ),
87
- },
88
- {
89
- key: "s",
90
- header: "",
91
- cell: (i) => <IssueStatusBadge status={i.status} />,
92
- },
93
- {
94
- key: "n",
95
- header: "Hits",
96
- align: "right",
97
- cell: (i) => i.count,
98
- },
99
- ]}
100
- />
101
- </CardContent>
102
- </Card>
103
- )}
104
- <DataTable
105
- rows={p.executions}
106
- rowKey={(x) => x.execution_id}
107
- onRowClick={(x) =>
108
- x.execution_id &&
109
- router.visit(
110
- R.applicationEnvironmentRequestPath(a, e, x.execution_id),
111
- )
112
- }
113
- columns={[
114
- {
115
- key: "when",
116
- header: "When",
117
- cell: (x) => <span className="text-xs">{when(x.occurred_at)}</span>,
118
- },
119
- { key: "k", header: "Kind", cell: (x) => x.kind },
120
- {
121
- key: "n",
122
- header: "Name",
123
- cell: (x) => <span className="font-mono text-xs">{x.name}</span>,
124
- },
125
- {
126
- key: "st",
127
- header: "Status",
128
- cell: (x) => <StatusBadge status={x.status} outcome={x.outcome} />,
129
- },
130
- {
131
- key: "d",
132
- header: "Duration",
133
- align: "right",
134
- cell: (x) => ms(x.duration),
135
- },
136
- {
137
- key: "ex",
138
- header: "Exception",
139
- cell: (x) => (
140
- <span className="text-destructive line-clamp-1 text-xs">
141
- {x.exception_preview ?? ""}
142
- </span>
143
- ),
144
- },
145
- ]}
146
- />
147
- </EnvLayout>
148
- )
149
- }
@@ -1,335 +0,0 @@
1
- import { AlertTriangle, Server } from "lucide-react"
2
-
3
- import { ChartPanel } from "@/components/railwatch/chart-panel"
4
- import { DataTable } from "@/components/railwatch/data-table"
5
- import { EmptyState } from "@/components/railwatch/empty-state"
6
- import { MetricChart } from "@/components/railwatch/metric-chart"
7
- import { PageHeader } from "@/components/railwatch/page-header"
8
- import { Stat, StatStrip } from "@/components/railwatch/stat"
9
- import { UtilisationBar } from "@/components/railwatch/utilisation-bar"
10
- import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
11
- import EnvLayout from "@/layouts/env-layout"
12
- import { ago, bytes, count, ms, when } from "@/lib/format"
13
-
14
- interface Sample {
15
- id: number
16
- server: string
17
- role: string | null
18
- pid: number | null
19
- deploy: string | null
20
- sampled_at: string
21
- threads_busy: number | null
22
- threads_max: number | null
23
- backlog: number | null
24
- pool_busy: number | null
25
- pool_size: number | null
26
- pool_waiting: number | null
27
- queue_depth: number | null
28
- queue_latency: number | null
29
- memory: number | null
30
- }
31
- interface HealthPoint {
32
- t: string
33
- utilisation: number
34
- backlog: number
35
- queue_depth: number
36
- queue_latency: number
37
- }
38
- interface QueueDepth {
39
- queue: string
40
- depth: number
41
- }
42
- interface Proc {
43
- id: number
44
- booted_at: string
45
- pid: number
46
- role: string
47
- server: string
48
- deploy: string | null
49
- ruby_version: string
50
- rails_version: string
51
- railwatch_version: string
52
- boot_seconds: number | null
53
- detail: Record<string, string>
54
- }
55
- interface Props {
56
- samples: Sample[]
57
- series: HealthPoint[]
58
- queues: QueueDepth[]
59
- silent_servers: string[]
60
- processes: Proc[]
61
- }
62
-
63
- const percent = (value: number) => `${value.toFixed(0)}%`
64
-
65
- function sum(values: (number | null)[]) {
66
- return values.reduce<number>((n, v) => n + (v ?? 0), 0)
67
- }
68
-
69
- function max(values: (number | null)[]) {
70
- return values.reduce<number>((n, v) => Math.max(n, v ?? 0), 0)
71
- }
72
-
73
- export default function Processes(p: Props) {
74
- const utilisations = p.samples.flatMap((s) =>
75
- s.threads_max ? [((s.threads_busy ?? 0) / s.threads_max) * 100] : [],
76
- )
77
- const utilisation = utilisations.length
78
- ? utilisations.reduce((a, b) => a + b, 0) / utilisations.length
79
- : 0
80
- const backlog = sum(p.samples.map((s) => s.backlog))
81
- const queueDepth = max(p.samples.map((s) => s.queue_depth))
82
- const oldestJob = max(p.samples.map((s) => s.queue_latency))
83
- const reporting = p.samples.length > 0 || p.series.length > 0
84
-
85
- return (
86
- <EnvLayout title="Processes">
87
- <PageHeader
88
- title="Processes"
89
- description="What every web and worker process is doing right now: thread pools, socket backlog, queue depth, and the boots behind them."
90
- />
91
- {p.silent_servers.length > 0 && (
92
- <Card className="border-warning/40">
93
- <CardHeader>
94
- <CardTitle className="flex items-center gap-2">
95
- <AlertTriangle className="text-warning size-4" />
96
- {p.silent_servers.length === 1
97
- ? "1 expected host is silent"
98
- : `${p.silent_servers.length} expected hosts are silent`}
99
- </CardTitle>
100
- </CardHeader>
101
- <CardContent className="space-y-2">
102
- <div className="flex flex-wrap gap-1.5">
103
- {p.silent_servers.map((server) => (
104
- <span
105
- key={server}
106
- className="border-warning/40 bg-warning/10 text-warning rounded-sm border px-1.5 font-mono text-[11px]"
107
- >
108
- {server}
109
- </span>
110
- ))}
111
- </div>
112
- <p className="text-muted-foreground text-xs">
113
- No health sample or execution from these hosts in the last 10
114
- minutes. Expected hosts come from your Kamal post-deploy hook, or
115
- from the environment settings if you set them by hand.
116
- </p>
117
- </CardContent>
118
- </Card>
119
- )}
120
- {reporting ? (
121
- <>
122
- <StatStrip>
123
- <Stat label="Live processes" value={p.samples.length} />
124
- <Stat
125
- label="Thread utilisation"
126
- value={percent(utilisation)}
127
- tone={
128
- utilisation >= 90
129
- ? "destructive"
130
- : utilisation >= 70
131
- ? "warning"
132
- : undefined
133
- }
134
- hint="averaged across live processes"
135
- />
136
- <Stat label="Backlog" value={count(backlog)} />
137
- <Stat label="Queue depth" value={count(queueDepth)} />
138
- <Stat label="Oldest job" value={ms(oldestJob)} />
139
- </StatStrip>
140
- <DataTable
141
- rows={p.samples}
142
- rowKey={(s) => s.id}
143
- empty={
144
- <EmptyState
145
- title="No process reported in the last 10 minutes"
146
- description="Health samples are older than the live window."
147
- />
148
- }
149
- columns={[
150
- {
151
- key: "server",
152
- header: "Server",
153
- cell: (s) => (
154
- <span className="font-mono text-xs">{s.server}</span>
155
- ),
156
- },
157
- { key: "role", header: "Role", cell: (s) => s.role },
158
- {
159
- key: "pid",
160
- hideOnMobile: true,
161
- header: "PID",
162
- align: "right",
163
- cell: (s) => s.pid,
164
- },
165
- {
166
- key: "threads",
167
- header: "Threads",
168
- cell: (s) => (
169
- <UtilisationBar busy={s.threads_busy} max={s.threads_max} />
170
- ),
171
- },
172
- {
173
- key: "pool",
174
- hideOnMobile: true,
175
- header: "DB pool",
176
- cell: (s) => (
177
- <UtilisationBar busy={s.pool_busy} max={s.pool_size} />
178
- ),
179
- },
180
- {
181
- key: "mem",
182
- hideOnMobile: true,
183
- header: "Memory",
184
- align: "right",
185
- cell: (s) => bytes(s.memory),
186
- },
187
- {
188
- key: "seen",
189
- header: "Last sample",
190
- align: "right",
191
- cell: (s) => (
192
- <span title={when(s.sampled_at)}>{ago(s.sampled_at)}</span>
193
- ),
194
- },
195
- ]}
196
- />
197
- {p.queues.length > 0 && (
198
- <Card>
199
- <CardHeader>
200
- <CardTitle>Queue depth</CardTitle>
201
- </CardHeader>
202
- <CardContent>
203
- <DataTable
204
- rows={p.queues}
205
- rowKey={(q) => q.queue}
206
- columns={[
207
- { key: "q", header: "Queue", cell: (q) => q.queue },
208
- {
209
- key: "d",
210
- header: "Ready jobs",
211
- align: "right",
212
- cell: (q) => count(q.depth),
213
- },
214
- ]}
215
- />
216
- </CardContent>
217
- </Card>
218
- )}
219
- <div className="grid gap-4 lg:grid-cols-2">
220
- <ChartPanel label="Thread utilisation" value={percent(utilisation)}>
221
- <MetricChart
222
- label="Utilisation"
223
- format={percent}
224
- data={p.series.map((s) => ({ t: s.t, value: s.utilisation }))}
225
- />
226
- </ChartPanel>
227
- <ChartPanel label="Backlog" value={count(backlog)}>
228
- <MetricChart
229
- label="Backlog"
230
- color="var(--warning)"
231
- format={count}
232
- data={p.series.map((s) => ({ t: s.t, value: s.backlog }))}
233
- />
234
- </ChartPanel>
235
- <ChartPanel label="Queue depth" value={count(queueDepth)}>
236
- <MetricChart
237
- label="Ready jobs"
238
- format={count}
239
- data={p.series.map((s) => ({ t: s.t, value: s.queue_depth }))}
240
- />
241
- </ChartPanel>
242
- <ChartPanel label="Oldest job" value={ms(oldestJob)}>
243
- <MetricChart
244
- label="Oldest job"
245
- color="var(--danger)"
246
- format={(v) => ms(v)}
247
- data={p.series.map((s) => ({ t: s.t, value: s.queue_latency }))}
248
- />
249
- </ChartPanel>
250
- </div>
251
- </>
252
- ) : (
253
- <EmptyState
254
- icon={Server}
255
- title="No health samples yet"
256
- description="The gem reports thread pool, backlog and queue health every 15s from each web and worker process."
257
- />
258
- )}
259
- <h2 className="text-sm font-semibold">Process boots</h2>
260
- <DataTable
261
- rows={p.processes}
262
- rowKey={(x) => x.id}
263
- empty={
264
- <EmptyState
265
- icon={Server}
266
- title="No process has booted with Railwatch yet"
267
- description="Server processes report here once they boot with the gem installed."
268
- />
269
- }
270
- columns={[
271
- {
272
- key: "b",
273
- header: "Booted",
274
- cell: (x) => (
275
- <span className="text-xs" title={when(x.booted_at)}>
276
- {ago(x.booted_at)}
277
- </span>
278
- ),
279
- },
280
- {
281
- key: "s",
282
- header: "Server",
283
- cell: (x) => <span className="font-mono text-xs">{x.server}</span>,
284
- },
285
- { key: "r", header: "Role", cell: (x) => x.role },
286
- {
287
- key: "pid",
288
- hideOnMobile: true,
289
- header: "PID",
290
- align: "right",
291
- cell: (x) => x.pid,
292
- },
293
- {
294
- key: "d",
295
- header: "Deploy",
296
- cell: (x) => (
297
- <span className="font-mono text-xs">
298
- {x.deploy?.slice(0, 12)}
299
- </span>
300
- ),
301
- },
302
- {
303
- key: "v",
304
- hideOnMobile: true,
305
- header: "Ruby / Rails / Railwatch",
306
- cell: (x) => (
307
- <span className="font-mono text-xs">
308
- {x.ruby_version} / {x.rails_version} / {x.railwatch_version}
309
- </span>
310
- ),
311
- },
312
- {
313
- key: "ad",
314
- hideOnMobile: true,
315
- header: "DB / queue / cache",
316
- cell: (x) => (
317
- <span className="font-mono text-xs">
318
- {x.detail.database_adapter} / {x.detail.queue_adapter} /{" "}
319
- {x.detail.cache_store?.replace("ActiveSupport::Cache::", "")}
320
- </span>
321
- ),
322
- },
323
- {
324
- key: "boot",
325
- hideOnMobile: true,
326
- header: "Boot",
327
- align: "right",
328
- cell: (x) =>
329
- x.boot_seconds ? `${x.boot_seconds.toFixed(1)}s` : "–",
330
- },
331
- ]}
332
- />
333
- </EnvLayout>
334
- )
335
- }
@@ -1,110 +0,0 @@
1
- import { Link, usePage } from "@inertiajs/react"
2
- import { Flame } from "lucide-react"
3
-
4
- import { DataTable } from "@/components/railwatch/data-table"
5
- import { EmptyState } from "@/components/railwatch/empty-state"
6
- import { PageHeader } from "@/components/railwatch/page-header"
7
- import { RelativeTime } from "@/components/railwatch/relative-time"
8
- import { Stat, StatStrip } from "@/components/railwatch/stat"
9
- import EnvLayout from "@/layouts/env-layout"
10
- import { count, ms, pct } from "@/lib/format"
11
- import * as R from "@/routes"
12
- import type { SharedProps } from "@/types"
13
-
14
- interface ProfileGroup {
15
- group_hash: string
16
- name: string | null
17
- profile_id: number
18
- count: number
19
- avg_duration: number
20
- max_samples: number | null
21
- last_seen_at: string
22
- }
23
- interface Props {
24
- profiles: ProfileGroup[]
25
- summary: {
26
- profiles: number
27
- executions: number
28
- profiled: number
29
- avg_samples: number
30
- }
31
- }
32
-
33
- export default function Profiles(p: Props) {
34
- const { environment, window } = usePage<SharedProps>().props
35
- const a = environment!.application_id
36
- const e = environment!.id
37
- const s = p.summary
38
-
39
- return (
40
- <EnvLayout title="Profiles">
41
- <PageHeader
42
- title="Profiles"
43
- description="Sampled stack profiles, grouped by the execution they profiled."
44
- />
45
- <StatStrip>
46
- <Stat label="Profiles" value={count(s.profiles)} />
47
- <Stat
48
- label="Executions profiled"
49
- value={pct(s.profiled, s.executions)}
50
- hint={`${count(s.profiled)} of ${count(s.executions)}`}
51
- />
52
- <Stat label="Avg samples" value={count(s.avg_samples)} />
53
- </StatStrip>
54
- <DataTable
55
- rows={p.profiles}
56
- rowKey={(g) => g.group_hash}
57
- empty={
58
- <EmptyState
59
- icon={Flame}
60
- title="No profiles in this window"
61
- description="Set RAILWATCH_PROFILE_SAMPLE_RATE in the app to profile slow executions."
62
- />
63
- }
64
- columns={[
65
- {
66
- key: "name",
67
- header: "Execution",
68
- className: "max-w-0",
69
- cell: (g) => (
70
- <Link
71
- href={R.applicationEnvironmentProfilePath(a, e, g.profile_id, {
72
- window,
73
- })}
74
- className="block truncate font-mono text-xs hover:underline"
75
- >
76
- {g.name ?? g.group_hash}
77
- </Link>
78
- ),
79
- },
80
- {
81
- key: "n",
82
- header: "Profiles",
83
- align: "right",
84
- cell: (g) => count(g.count),
85
- },
86
- {
87
- key: "avg",
88
- header: "Avg duration",
89
- align: "right",
90
- cell: (g) => ms(g.avg_duration),
91
- },
92
- {
93
- key: "samples",
94
- hideOnMobile: true,
95
- header: "Max samples",
96
- align: "right",
97
- cell: (g) => count(g.max_samples),
98
- },
99
- {
100
- key: "last",
101
- hideOnMobile: true,
102
- header: "Last seen",
103
- align: "right",
104
- cell: (g) => <RelativeTime iso={g.last_seen_at} />,
105
- },
106
- ]}
107
- />
108
- </EnvLayout>
109
- )
110
- }