railwatch 0.9.4 → 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 (452) hide show
  1. checksums.yaml +4 -4
  2. data/AGENTS.md +37 -33
  3. data/CHANGELOG.md +50 -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 +35 -66
  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/configuration.rb +21 -151
  21. data/lib/railwatch/engine.rb +9 -118
  22. data/lib/railwatch/patches.rb +0 -11
  23. data/lib/railwatch/reporter.rb +3 -19
  24. data/lib/railwatch/subscribers/process_info.rb +1 -2
  25. data/lib/railwatch/version.rb +1 -1
  26. data/lib/railwatch.rb +4 -62
  27. data/lib/tasks/railwatch_tasks.rake +3 -325
  28. data/llms.txt +23 -25
  29. metadata +3 -453
  30. data/app/channels/railwatch/environment_channel.rb +0 -62
  31. data/app/controllers/concerns/railwatch/telemetry_identity.rb +0 -25
  32. data/app/controllers/railwatch/alerts_controller.rb +0 -48
  33. data/app/controllers/railwatch/anomaly_rules_controller.rb +0 -31
  34. data/app/controllers/railwatch/attachments_controller.rb +0 -22
  35. data/app/controllers/railwatch/broadcasts_controller.rb +0 -15
  36. data/app/controllers/railwatch/cache_events_controller.rb +0 -30
  37. data/app/controllers/railwatch/commands_controller.rb +0 -16
  38. data/app/controllers/railwatch/comments_controller.rb +0 -11
  39. data/app/controllers/railwatch/dashboard_controller.rb +0 -70
  40. data/app/controllers/railwatch/deploys_controller.rb +0 -34
  41. data/app/controllers/railwatch/deprecations_controller.rb +0 -54
  42. data/app/controllers/railwatch/environment_scoped.rb +0 -81
  43. data/app/controllers/railwatch/exceptions_controller.rb +0 -52
  44. data/app/controllers/railwatch/executions_controller.rb +0 -13
  45. data/app/controllers/railwatch/issues_controller.rb +0 -258
  46. data/app/controllers/railwatch/jobs_controller.rb +0 -63
  47. data/app/controllers/railwatch/llm_calls_controller.rb +0 -124
  48. data/app/controllers/railwatch/logs_controller.rb +0 -40
  49. data/app/controllers/railwatch/mails_controller.rb +0 -19
  50. data/app/controllers/railwatch/notifications_controller.rb +0 -11
  51. data/app/controllers/railwatch/outgoing_requests_controller.rb +0 -19
  52. data/app/controllers/railwatch/overview_controller.rb +0 -38
  53. data/app/controllers/railwatch/people_controller.rb +0 -25
  54. data/app/controllers/railwatch/processes_controller.rb +0 -33
  55. data/app/controllers/railwatch/profiles_controller.rb +0 -65
  56. data/app/controllers/railwatch/queries_controller.rb +0 -59
  57. data/app/controllers/railwatch/releases_controller.rb +0 -75
  58. data/app/controllers/railwatch/requests_controller.rb +0 -37
  59. data/app/controllers/railwatch/saved_views_controller.rb +0 -53
  60. data/app/controllers/railwatch/scheduled_tasks_controller.rb +0 -45
  61. data/app/controllers/railwatch/spans_controller.rb +0 -47
  62. data/app/controllers/railwatch/storage_ops_controller.rb +0 -15
  63. data/app/controllers/railwatch/tenants_controller.rb +0 -44
  64. data/app/controllers/railwatch/thresholds_controller.rb +0 -47
  65. data/app/controllers/railwatch/traces_controller.rb +0 -72
  66. data/app/controllers/railwatch/transactions_controller.rb +0 -21
  67. data/app/controllers/railwatch/view_renders_controller.rb +0 -28
  68. data/app/controllers/railwatch/visits_controller.rb +0 -53
  69. data/app/frontend/components/app-content.tsx +0 -26
  70. data/app/frontend/components/app-logo-icon.tsx +0 -23
  71. data/app/frontend/components/app-logo.tsx +0 -14
  72. data/app/frontend/components/app-shell.tsx +0 -38
  73. data/app/frontend/components/app-sidebar-header.tsx +0 -54
  74. data/app/frontend/components/app-sidebar.tsx +0 -94
  75. data/app/frontend/components/app-wordmark.tsx +0 -29
  76. data/app/frontend/components/breadcrumbs.tsx +0 -46
  77. data/app/frontend/components/icon.tsx +0 -16
  78. data/app/frontend/components/input-error.tsx +0 -18
  79. data/app/frontend/components/nav-footer.tsx +0 -46
  80. data/app/frontend/components/nav-main.tsx +0 -41
  81. data/app/frontend/components/nav-user.tsx +0 -50
  82. data/app/frontend/components/railwatch/chart-hover.tsx +0 -34
  83. data/app/frontend/components/railwatch/chart-panel.tsx +0 -170
  84. data/app/frontend/components/railwatch/code.tsx +0 -28
  85. data/app/frontend/components/railwatch/command-palette.tsx +0 -132
  86. data/app/frontend/components/railwatch/copy-block.tsx +0 -52
  87. data/app/frontend/components/railwatch/copy-id.tsx +0 -49
  88. data/app/frontend/components/railwatch/cursor-load-more.tsx +0 -39
  89. data/app/frontend/components/railwatch/data-table.tsx +0 -195
  90. data/app/frontend/components/railwatch/digit-roll.tsx +0 -312
  91. data/app/frontend/components/railwatch/empty-state.tsx +0 -159
  92. data/app/frontend/components/railwatch/env-nav.tsx +0 -130
  93. data/app/frontend/components/railwatch/env-switcher.tsx +0 -145
  94. data/app/frontend/components/railwatch/filter-bar.tsx +0 -153
  95. data/app/frontend/components/railwatch/flamegraph.tsx +0 -324
  96. data/app/frontend/components/railwatch/frames.tsx +0 -295
  97. data/app/frontend/components/railwatch/json-viewer.tsx +0 -159
  98. data/app/frontend/components/railwatch/live-dot.tsx +0 -78
  99. data/app/frontend/components/railwatch/live-toggle.tsx +0 -76
  100. data/app/frontend/components/railwatch/metric-chart.tsx +0 -112
  101. data/app/frontend/components/railwatch/mobile-page-switcher.tsx +0 -89
  102. data/app/frontend/components/railwatch/onboarding.tsx +0 -91
  103. data/app/frontend/components/railwatch/origin-identity.tsx +0 -84
  104. data/app/frontend/components/railwatch/page-header.tsx +0 -42
  105. data/app/frontend/components/railwatch/percentile-picker.tsx +0 -18
  106. data/app/frontend/components/railwatch/relative-time.tsx +0 -18
  107. data/app/frontend/components/railwatch/saved-views.tsx +0 -232
  108. data/app/frontend/components/railwatch/segmented.tsx +0 -64
  109. data/app/frontend/components/railwatch/series-chart.tsx +0 -400
  110. data/app/frontend/components/railwatch/shortcuts-dialog.tsx +0 -89
  111. data/app/frontend/components/railwatch/signal-toast.tsx +0 -121
  112. data/app/frontend/components/railwatch/sort-header.tsx +0 -37
  113. data/app/frontend/components/railwatch/source-link.tsx +0 -57
  114. data/app/frontend/components/railwatch/sparkline-cell.tsx +0 -14
  115. data/app/frontend/components/railwatch/sql-block.tsx +0 -116
  116. data/app/frontend/components/railwatch/stat.tsx +0 -185
  117. data/app/frontend/components/railwatch/status-badge.tsx +0 -79
  118. data/app/frontend/components/railwatch/step-picker.tsx +0 -57
  119. data/app/frontend/components/railwatch/timeline.tsx +0 -405
  120. data/app/frontend/components/railwatch/utilisation-bar.tsx +0 -44
  121. data/app/frontend/components/railwatch/window-picker.tsx +0 -147
  122. data/app/frontend/components/ui/avatar.tsx +0 -51
  123. data/app/frontend/components/ui/badge.tsx +0 -46
  124. data/app/frontend/components/ui/breadcrumb.tsx +0 -109
  125. data/app/frontend/components/ui/button.tsx +0 -60
  126. data/app/frontend/components/ui/card.tsx +0 -92
  127. data/app/frontend/components/ui/chart.tsx +0 -374
  128. data/app/frontend/components/ui/checkbox.tsx +0 -32
  129. data/app/frontend/components/ui/collapsible.tsx +0 -31
  130. data/app/frontend/components/ui/command.tsx +0 -182
  131. data/app/frontend/components/ui/dialog.tsx +0 -158
  132. data/app/frontend/components/ui/dropdown-menu.tsx +0 -255
  133. data/app/frontend/components/ui/hover-card.tsx +0 -42
  134. data/app/frontend/components/ui/input.tsx +0 -21
  135. data/app/frontend/components/ui/label.tsx +0 -24
  136. data/app/frontend/components/ui/popover.tsx +0 -87
  137. data/app/frontend/components/ui/select.tsx +0 -185
  138. data/app/frontend/components/ui/separator.tsx +0 -28
  139. data/app/frontend/components/ui/sheet.tsx +0 -137
  140. data/app/frontend/components/ui/sidebar.tsx +0 -726
  141. data/app/frontend/components/ui/skeleton.tsx +0 -13
  142. data/app/frontend/components/ui/sonner.tsx +0 -41
  143. data/app/frontend/components/ui/table.tsx +0 -114
  144. data/app/frontend/components/ui/tabs.tsx +0 -91
  145. data/app/frontend/components/ui/textarea.tsx +0 -18
  146. data/app/frontend/components/ui/tooltip.tsx +0 -61
  147. data/app/frontend/components/user-info.tsx +0 -32
  148. data/app/frontend/components/user-menu-content.tsx +0 -94
  149. data/app/frontend/entrypoints/application.css +0 -244
  150. data/app/frontend/entrypoints/inertia.tsx +0 -92
  151. data/app/frontend/fonts/CommitMono-Bold.woff2 +0 -0
  152. data/app/frontend/fonts/CommitMono-Regular.woff2 +0 -0
  153. data/app/frontend/fonts/Roboto-Black.woff2 +0 -0
  154. data/app/frontend/fonts/Roboto-Bold.woff2 +0 -0
  155. data/app/frontend/fonts/Roboto-Medium.woff2 +0 -0
  156. data/app/frontend/fonts/Roboto-Regular.woff2 +0 -0
  157. data/app/frontend/hooks/use-appearance.tsx +0 -67
  158. data/app/frontend/hooks/use-clipboard.ts +0 -32
  159. data/app/frontend/hooks/use-flash.tsx +0 -23
  160. data/app/frontend/hooks/use-hotkeys.ts +0 -63
  161. data/app/frontend/hooks/use-initials.tsx +0 -15
  162. data/app/frontend/hooks/use-live.ts +0 -205
  163. data/app/frontend/hooks/use-mobile-navigation.ts +0 -8
  164. data/app/frontend/hooks/use-mobile.ts +0 -21
  165. data/app/frontend/hooks/use-percentile.ts +0 -24
  166. data/app/frontend/hooks/use-reduced-motion.ts +0 -25
  167. data/app/frontend/hooks/use-row-nav.ts +0 -106
  168. data/app/frontend/hooks/use-step.ts +0 -27
  169. data/app/frontend/hooks/use-ticker.ts +0 -12
  170. data/app/frontend/hooks/use-window.ts +0 -58
  171. data/app/frontend/layouts/app/app-sidebar-layout.tsx +0 -107
  172. data/app/frontend/layouts/app-layout.tsx +0 -21
  173. data/app/frontend/layouts/env-layout.tsx +0 -53
  174. data/app/frontend/layouts/persistent-layout.tsx +0 -18
  175. data/app/frontend/lib/cable.ts +0 -10
  176. data/app/frontend/lib/env-pages.ts +0 -236
  177. data/app/frontend/lib/execution-path.ts +0 -55
  178. data/app/frontend/lib/explain.ts +0 -50
  179. data/app/frontend/lib/filter.ts +0 -25
  180. data/app/frontend/lib/flamegraph.ts +0 -155
  181. data/app/frontend/lib/format.ts +0 -71
  182. data/app/frontend/lib/keyboard.ts +0 -18
  183. data/app/frontend/lib/mount.ts +0 -23
  184. data/app/frontend/lib/source-link.ts +0 -217
  185. data/app/frontend/lib/utils.ts +0 -6
  186. data/app/frontend/pages/alerts/index.tsx +0 -171
  187. data/app/frontend/pages/broadcasts/index.tsx +0 -171
  188. data/app/frontend/pages/cache_events/index.tsx +0 -127
  189. data/app/frontend/pages/commands/index.tsx +0 -143
  190. data/app/frontend/pages/deploys/index.tsx +0 -94
  191. data/app/frontend/pages/deploys/show.tsx +0 -309
  192. data/app/frontend/pages/deprecations/index.tsx +0 -176
  193. data/app/frontend/pages/exceptions/index.tsx +0 -271
  194. data/app/frontend/pages/executions/show.tsx +0 -821
  195. data/app/frontend/pages/issues/index.tsx +0 -399
  196. data/app/frontend/pages/issues/show.tsx +0 -1367
  197. data/app/frontend/pages/jobs/index.tsx +0 -381
  198. data/app/frontend/pages/jobs/klass.tsx +0 -215
  199. data/app/frontend/pages/llm_calls/index.tsx +0 -664
  200. data/app/frontend/pages/logs/index.tsx +0 -303
  201. data/app/frontend/pages/mails/index.tsx +0 -218
  202. data/app/frontend/pages/notifications/index.tsx +0 -161
  203. data/app/frontend/pages/outgoing_requests/index.tsx +0 -309
  204. data/app/frontend/pages/overview/show.tsx +0 -333
  205. data/app/frontend/pages/people/index.tsx +0 -102
  206. data/app/frontend/pages/people/show.tsx +0 -149
  207. data/app/frontend/pages/processes/index.tsx +0 -335
  208. data/app/frontend/pages/profiles/index.tsx +0 -110
  209. data/app/frontend/pages/profiles/show.tsx +0 -105
  210. data/app/frontend/pages/queries/index.tsx +0 -492
  211. data/app/frontend/pages/queries/show.tsx +0 -276
  212. data/app/frontend/pages/releases/index.tsx +0 -190
  213. data/app/frontend/pages/releases/release-health.tsx +0 -68
  214. data/app/frontend/pages/releases/release-path.ts +0 -25
  215. data/app/frontend/pages/releases/show.tsx +0 -262
  216. data/app/frontend/pages/requests/index.tsx +0 -235
  217. data/app/frontend/pages/requests/route.tsx +0 -159
  218. data/app/frontend/pages/scheduled_tasks/index.tsx +0 -220
  219. data/app/frontend/pages/spans/index.tsx +0 -229
  220. data/app/frontend/pages/spans/show.tsx +0 -220
  221. data/app/frontend/pages/storage_ops/index.tsx +0 -150
  222. data/app/frontend/pages/tenants/index.tsx +0 -236
  223. data/app/frontend/pages/tenants/show.tsx +0 -477
  224. data/app/frontend/pages/tenants/tenant-path.ts +0 -26
  225. data/app/frontend/pages/thresholds/index.tsx +0 -414
  226. data/app/frontend/pages/traces/show.tsx +0 -208
  227. data/app/frontend/pages/transactions/index.tsx +0 -220
  228. data/app/frontend/pages/view_renders/index.tsx +0 -241
  229. data/app/frontend/pages/visits/index.tsx +0 -321
  230. data/app/frontend/public/icon.png +0 -0
  231. data/app/frontend/public/icon.svg +0 -5
  232. data/app/frontend/routes/index.d.ts +0 -2136
  233. data/app/frontend/routes/index.js +0 -2112
  234. data/app/frontend/types/globals.d.ts +0 -9
  235. data/app/frontend/types/index.ts +0 -288
  236. data/app/frontend/types/vite-env.d.ts +0 -9
  237. data/app/helpers/railwatch/assets_helper.rb +0 -52
  238. data/app/jobs/railwatch/anomaly_scan_job.rb +0 -11
  239. data/app/jobs/railwatch/application_job.rb +0 -7
  240. data/app/jobs/railwatch/auto_resolve_issues_job.rb +0 -19
  241. data/app/jobs/railwatch/check_scheduled_tasks_job.rb +0 -113
  242. data/app/jobs/railwatch/detect_anomalies_job.rb +0 -171
  243. data/app/jobs/railwatch/detect_performance_issues_job.rb +0 -95
  244. data/app/jobs/railwatch/group_exceptions_job.rb +0 -91
  245. data/app/jobs/railwatch/optimize_telemetry_job.rb +0 -23
  246. data/app/jobs/railwatch/performance_scan_job.rb +0 -11
  247. data/app/jobs/railwatch/prune_telemetry_job.rb +0 -129
  248. data/app/jobs/railwatch/release_health_rollup_job.rb +0 -64
  249. data/app/jobs/railwatch/rollup_catchup_job.rb +0 -21
  250. data/app/jobs/railwatch/rollup_job.rb +0 -132
  251. data/app/jobs/railwatch/scheduled_task_scan_job.rb +0 -11
  252. data/app/models/concerns/railwatch/detection_snapshotting.rb +0 -20
  253. data/app/models/railwatch/alert.rb +0 -229
  254. data/app/models/railwatch/alert_rule.rb +0 -121
  255. data/app/models/railwatch/anomaly_rule.rb +0 -51
  256. data/app/models/railwatch/application.rb +0 -44
  257. data/app/models/railwatch/application_record.rb +0 -33
  258. data/app/models/railwatch/comment.rb +0 -36
  259. data/app/models/railwatch/deploy.rb +0 -67
  260. data/app/models/railwatch/environment.rb +0 -68
  261. data/app/models/railwatch/execution_presenter.rb +0 -185
  262. data/app/models/railwatch/filter_query.rb +0 -184
  263. data/app/models/railwatch/followup_receipt.rb +0 -27
  264. data/app/models/railwatch/ingest/batch.rb +0 -355
  265. data/app/models/railwatch/ingest/mapper.rb +0 -585
  266. data/app/models/railwatch/ingest/payload.rb +0 -96
  267. data/app/models/railwatch/ingest/rollup_absorber.rb +0 -170
  268. data/app/models/railwatch/ingest/writer.rb +0 -137
  269. data/app/models/railwatch/issue.rb +0 -277
  270. data/app/models/railwatch/issue_activity.rb +0 -23
  271. data/app/models/railwatch/issue_detection_presenter.rb +0 -309
  272. data/app/models/railwatch/issue_detection_snapshot.rb +0 -77
  273. data/app/models/railwatch/maintenance_task.rb +0 -53
  274. data/app/models/railwatch/saved_view.rb +0 -63
  275. data/app/models/railwatch/telemetry/aggregations.rb +0 -251
  276. data/app/models/railwatch/telemetry/attachment.rb +0 -31
  277. data/app/models/railwatch/telemetry/bounded_gzip.rb +0 -101
  278. data/app/models/railwatch/telemetry/broadcast.rb +0 -13
  279. data/app/models/railwatch/telemetry/cache_event.rb +0 -16
  280. data/app/models/railwatch/telemetry/child.rb +0 -53
  281. data/app/models/railwatch/telemetry/cursor_page.rb +0 -99
  282. data/app/models/railwatch/telemetry/deprecation.rb +0 -13
  283. data/app/models/railwatch/telemetry/enqueued_job.rb +0 -13
  284. data/app/models/railwatch/telemetry/exception.rb +0 -28
  285. data/app/models/railwatch/telemetry/execution.rb +0 -152
  286. data/app/models/railwatch/telemetry/export_delivery.rb +0 -32
  287. data/app/models/railwatch/telemetry/export_destination.rb +0 -66
  288. data/app/models/railwatch/telemetry/health_sample.rb +0 -72
  289. data/app/models/railwatch/telemetry/ingest_batch.rb +0 -31
  290. data/app/models/railwatch/telemetry/llm_call.rb +0 -37
  291. data/app/models/railwatch/telemetry/log.rb +0 -91
  292. data/app/models/railwatch/telemetry/mail.rb +0 -13
  293. data/app/models/railwatch/telemetry/n_plus_one.rb +0 -92
  294. data/app/models/railwatch/telemetry/notification.rb +0 -13
  295. data/app/models/railwatch/telemetry/outgoing_request.rb +0 -13
  296. data/app/models/railwatch/telemetry/person.rb +0 -50
  297. data/app/models/railwatch/telemetry/process.rb +0 -10
  298. data/app/models/railwatch/telemetry/profile.rb +0 -37
  299. data/app/models/railwatch/telemetry/query.rb +0 -57
  300. data/app/models/railwatch/telemetry/query_shape.rb +0 -45
  301. data/app/models/railwatch/telemetry/release_health.rb +0 -69
  302. data/app/models/railwatch/telemetry/rollup.rb +0 -157
  303. data/app/models/railwatch/telemetry/session.rb +0 -24
  304. data/app/models/railwatch/telemetry/span.rb +0 -19
  305. data/app/models/railwatch/telemetry/storage_op.rb +0 -13
  306. data/app/models/railwatch/telemetry/tenant.rb +0 -240
  307. data/app/models/railwatch/telemetry/transaction.rb +0 -13
  308. data/app/models/railwatch/telemetry/view_render.rb +0 -13
  309. data/app/models/railwatch/telemetry/visit.rb +0 -24
  310. data/app/models/railwatch/telemetry_record.rb +0 -107
  311. data/app/models/railwatch/threshold.rb +0 -66
  312. data/app/models/railwatch/user.rb +0 -47
  313. data/app/models/railwatch/viewer.rb +0 -13
  314. data/app/models/railwatch/window.rb +0 -66
  315. data/app/views/layouts/railwatch/dashboard.html.erb +0 -25
  316. data/db/railwatch_migrate/20260916000000_create_railwatch_tables.rb +0 -151
  317. data/db/railwatch_migrate/20260917000000_create_railwatch_maintenance_tasks.rb +0 -18
  318. data/db/railwatch_migrate/20260917120000_create_railwatch_followup_receipts.rb +0 -20
  319. data/db/railwatch_migrate/20260918120000_widen_host_user_ids.rb +0 -71
  320. data/db/railwatch_telemetry_migrate/20260903000000_enable_incremental_vacuum.rb +0 -62
  321. data/db/railwatch_telemetry_migrate/20260903000001_create_telemetry.rb +0 -481
  322. data/db/railwatch_telemetry_migrate/20260903000002_rename_tenant_to_app_tenant.rb +0 -14
  323. data/db/railwatch_telemetry_migrate/20260903000003_add_statement_count_to_transactions.rb +0 -9
  324. data/db/railwatch_telemetry_migrate/20260903000004_add_role_and_channel.rb +0 -10
  325. data/db/railwatch_telemetry_migrate/20260903000005_add_locals_to_exceptions.rb +0 -9
  326. data/db/railwatch_telemetry_migrate/20260903000006_add_spans_health_vitals_and_fts.rb +0 -82
  327. data/db/railwatch_telemetry_migrate/20260903000007_rename_span_attributes_to_payload.rb +0 -10
  328. data/db/railwatch_telemetry_migrate/20260903000008_add_profiles_and_attachments.rb +0 -60
  329. data/db/railwatch_telemetry_migrate/20260903000009_add_truncated_to_attachments.rb +0 -9
  330. data/db/railwatch_telemetry_migrate/20260903000010_create_sessions_and_release_health.rb +0 -51
  331. data/db/railwatch_telemetry_migrate/20260903000011_add_fingerprint_to_exceptions.rb +0 -11
  332. data/db/railwatch_telemetry_migrate/20260904000012_add_failed_to_broadcasts.rb +0 -10
  333. data/db/railwatch_telemetry_migrate/20260904010000_add_filter_cursor_indexes.rb +0 -37
  334. data/db/railwatch_telemetry_migrate/20260904020000_add_n_plus_ones_execution_id_index.rb +0 -11
  335. data/db/railwatch_telemetry_migrate/20260904120000_create_query_shapes.rb +0 -15
  336. data/db/railwatch_telemetry_migrate/20260906120000_add_backpressure_factor_to_ingest_batches.rb +0 -9
  337. data/db/railwatch_telemetry_migrate/20260907000000_rename_lantern_version_on_processes.rb +0 -10
  338. data/db/railwatch_telemetry_migrate/20260913000000_rename_nightrail_version_on_processes.rb +0 -9
  339. data/db/railwatch_telemetry_migrate/20260914000000_drop_orphan_durable_ingest_tables.rb +0 -18
  340. data/db/railwatch_telemetry_migrate/20260914010000_drop_orphan_durable_ingest_columns.rb +0 -25
  341. data/db/railwatch_telemetry_migrate/20260915000000_create_llm_calls.rb +0 -55
  342. data/db/railwatch_telemetry_migrate/20260915120000_add_detail_to_llm_calls.rb +0 -21
  343. data/db/railwatch_telemetry_migrate/20260915200000_add_explained_index_to_queries.rb +0 -16
  344. data/db/railwatch_telemetry_migrate/20260915210000_add_slowest_index_to_queries.rb +0 -18
  345. data/db/railwatch_telemetry_migrate/20260917010000_add_batch_ledger_to_ingest_batches.rb +0 -16
  346. data/db/railwatch_telemetry_migrate/20260919000100_create_export_queue.rb +0 -146
  347. data/db/railwatch_telemetry_migrate/20260922000000_add_covering_indexes_for_dashboard_aggregates.rb +0 -34
  348. data/db/railwatch_telemetry_migrate/20260923000000_add_people_index.rb +0 -15
  349. data/db/railwatch_telemetry_migrate/20260925000000_add_tenant_summary_index.rb +0 -17
  350. data/db/railwatch_telemetry_migrate/20261008000000_add_in_transaction_to_outgoing_requests.rb +0 -10
  351. data/docs/embedded.md +0 -503
  352. data/lib/puma/plugin/railwatch.rb +0 -215
  353. data/lib/railwatch/dashboard_assets.rb +0 -45
  354. data/lib/railwatch/embedded.rb +0 -50
  355. data/lib/railwatch/export/client.rb +0 -124
  356. data/lib/railwatch/export/lease.rb +0 -56
  357. data/lib/railwatch/export/outbox.rb +0 -361
  358. data/lib/railwatch/export/policy.rb +0 -79
  359. data/lib/railwatch/export/sender.rb +0 -130
  360. data/lib/railwatch/ingest_request_body_limit.rb +0 -10
  361. data/lib/railwatch/maintenance.rb +0 -194
  362. data/lib/railwatch/patches/migration_busy_timeout.rb +0 -73
  363. data/lib/railwatch/transport/local.rb +0 -78
  364. data/lib/railwatch/transport/socket.rb +0 -183
  365. data/lib/railwatch/writer.rb +0 -370
  366. data/public/railwatch/assets/CommitMono-Bold-D6h61ieg.woff2 +0 -0
  367. data/public/railwatch/assets/CommitMono-Regular-zr8w7Obm.woff2 +0 -0
  368. data/public/railwatch/assets/Roboto-Black-auA4GeOK.woff2 +0 -0
  369. data/public/railwatch/assets/Roboto-Bold-CJLnO8j1.woff2 +0 -0
  370. data/public/railwatch/assets/Roboto-Medium-Cm2bwKpj.woff2 +0 -0
  371. data/public/railwatch/assets/Roboto-Regular-Chaq1-PV.woff2 +0 -0
  372. data/public/railwatch/assets/app-layout-by7G3sa1.js +0 -41
  373. data/public/railwatch/assets/application-Cc5v_Vk9.css +0 -1
  374. data/public/railwatch/assets/arrow-up-DCZ7emux.js +0 -1
  375. data/public/railwatch/assets/braces-CykT5_5u.js +0 -1
  376. data/public/railwatch/assets/card-C-PPSA0g.js +0 -1
  377. data/public/railwatch/assets/chart-Cov79YC5.js +0 -39
  378. data/public/railwatch/assets/chart-hover-DdK4E-2h.js +0 -1
  379. data/public/railwatch/assets/chart-panel-BPaYRXJB.js +0 -1
  380. data/public/railwatch/assets/code-BYjC4YZ_.js +0 -1
  381. data/public/railwatch/assets/copy-id-LPfoj9cC.js +0 -1
  382. data/public/railwatch/assets/cursor-load-more-DSdhgpGl.js +0 -1
  383. data/public/railwatch/assets/data-table-CQ_YRWQs.js +0 -1
  384. data/public/railwatch/assets/env-layout-CdMWbUkm.js +0 -1
  385. data/public/railwatch/assets/execution-path-7MYt-Qbn.js +0 -1
  386. data/public/railwatch/assets/filter-bar-CrzpQ_vD.js +0 -1
  387. data/public/railwatch/assets/flamegraph-BIFEdTAg.js +0 -2
  388. data/public/railwatch/assets/format-B9SDkrWj.js +0 -1
  389. data/public/railwatch/assets/frames-C5t-x4f0.js +0 -1
  390. data/public/railwatch/assets/index--NQ7us-L.js +0 -1
  391. data/public/railwatch/assets/index-2kgYe_99.js +0 -1
  392. data/public/railwatch/assets/index-4YEPHxtO.js +0 -1
  393. data/public/railwatch/assets/index-AOCC1GDJ.js +0 -1
  394. data/public/railwatch/assets/index-BEo-u0eO.js +0 -1
  395. data/public/railwatch/assets/index-BJ1V3w2N.js +0 -1
  396. data/public/railwatch/assets/index-BPoHsBKb.js +0 -1
  397. data/public/railwatch/assets/index-BZzyGUM3.js +0 -1
  398. data/public/railwatch/assets/index-BgVq7yKL.js +0 -1
  399. data/public/railwatch/assets/index-BqFGEZvW.js +0 -1
  400. data/public/railwatch/assets/index-ByuHJ1CH.js +0 -1
  401. data/public/railwatch/assets/index-C2j50AL2.js +0 -1
  402. data/public/railwatch/assets/index-CLEdTkvW.js +0 -1
  403. data/public/railwatch/assets/index-CWgq8qBu.js +0 -1
  404. data/public/railwatch/assets/index-CXQ7_oVj.js +0 -2
  405. data/public/railwatch/assets/index-CmyYrjS2.js +0 -1
  406. data/public/railwatch/assets/index-Cn9_HjDo.js +0 -1
  407. data/public/railwatch/assets/index-CsMAm9UK.js +0 -1
  408. data/public/railwatch/assets/index-D0Kk_oGY.js +0 -1
  409. data/public/railwatch/assets/index-DC9ZptJh.js +0 -1
  410. data/public/railwatch/assets/index-DNzg7jmV.js +0 -1
  411. data/public/railwatch/assets/index-DQ8uJO5f.js +0 -1
  412. data/public/railwatch/assets/index-Deq14BxB.js +0 -1
  413. data/public/railwatch/assets/index-IaV3Blzp.js +0 -1
  414. data/public/railwatch/assets/index-TYNWcJQs.js +0 -1
  415. data/public/railwatch/assets/index-WonH2fcZ.js +0 -1
  416. data/public/railwatch/assets/index-WxWUaH_d.js +0 -1
  417. data/public/railwatch/assets/index-mUNrl4ky.js +0 -1
  418. data/public/railwatch/assets/inertia-DfNRARuA.js +0 -97
  419. data/public/railwatch/assets/json-viewer-DMsF5glp.js +0 -1
  420. data/public/railwatch/assets/klass-CmHYk5rz.js +0 -1
  421. data/public/railwatch/assets/origin-identity-1R3bqa3R.js +0 -1
  422. data/public/railwatch/assets/percentile-picker-73z5PAps.js +0 -1
  423. data/public/railwatch/assets/relative-time-Dsc_cCdX.js +0 -1
  424. data/public/railwatch/assets/release-health-BV5OaOWd.js +0 -1
  425. data/public/railwatch/assets/route-CWh58C7K.js +0 -1
  426. data/public/railwatch/assets/segmented-DB4zh-Am.js +0 -1
  427. data/public/railwatch/assets/select-DBKjFlOX.js +0 -1
  428. data/public/railwatch/assets/series-chart-BaiDG795.js +0 -1
  429. data/public/railwatch/assets/show-B4ICCFGh.js +0 -1
  430. data/public/railwatch/assets/show-B5lOU9Gv.js +0 -1
  431. data/public/railwatch/assets/show-BYPB74t4.js +0 -2
  432. data/public/railwatch/assets/show-C7A0m2M8.js +0 -1
  433. data/public/railwatch/assets/show-DT5X1zqE.js +0 -1
  434. data/public/railwatch/assets/show-D_NBdXsn.js +0 -2
  435. data/public/railwatch/assets/show-DgPCnRg8.js +0 -1
  436. data/public/railwatch/assets/show-Yl69HF0x.js +0 -1
  437. data/public/railwatch/assets/show-_QxuUQCm.js +0 -3
  438. data/public/railwatch/assets/show-g__jnN36.js +0 -1
  439. data/public/railwatch/assets/show-l4ztfjl9.js +0 -1
  440. data/public/railwatch/assets/sort-header-BcG8ulEH.js +0 -1
  441. data/public/railwatch/assets/source-link-B2183i2-.js +0 -1
  442. data/public/railwatch/assets/sparkline-cell-B47ipscB.js +0 -1
  443. data/public/railwatch/assets/stat-DT2hhsqS.js +0 -1
  444. data/public/railwatch/assets/status-badge-CmbRKlY7.js +0 -1
  445. data/public/railwatch/assets/tenant-path-C4rNGauE.js +0 -1
  446. data/public/railwatch/assets/use-clipboard-oTNeP4ax.js +0 -1
  447. data/public/railwatch/assets/use-live-CLZZj245.js +0 -1
  448. data/public/railwatch/icon.png +0 -0
  449. data/public/railwatch/icon.svg +0 -5
  450. data/public/railwatch/manifest.json +0 -1245
  451. data/public/railwatch/rails-vite.json +0 -1
  452. /data/{app/frontend/lib → lib/generators/railwatch/install/templates}/railwatch.ts +0 -0
@@ -1,821 +0,0 @@
1
- import { Link, usePage } from "@inertiajs/react"
2
- import { Check, ClipboardCopy, Copy } from "lucide-react"
3
- import { useState } from "react"
4
-
5
- import { DataTable } from "@/components/railwatch/data-table"
6
- import { Flamegraph } from "@/components/railwatch/flamegraph"
7
- import { ExceptionCard } from "@/components/railwatch/frames"
8
- import { JsonViewer } from "@/components/railwatch/json-viewer"
9
- import { OriginIdentity } from "@/components/railwatch/origin-identity"
10
- import { PageHeader } from "@/components/railwatch/page-header"
11
- import { SqlBlock } from "@/components/railwatch/sql-block"
12
- import { Stat, StatStrip } from "@/components/railwatch/stat"
13
- import { LevelBadge, StatusBadge } from "@/components/railwatch/status-badge"
14
- import { Timeline } from "@/components/railwatch/timeline"
15
- import { Badge } from "@/components/ui/badge"
16
- import { Button } from "@/components/ui/button"
17
- import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
18
- import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
19
- import { useClipboard } from "@/hooks/use-clipboard"
20
- import EnvLayout from "@/layouts/env-layout"
21
- import { executionPath } from "@/lib/execution-path"
22
- import { bytes, count, ms, when } from "@/lib/format"
23
- import * as R from "@/routes"
24
- import type { ExceptionDetail, SharedProps, TimelineEntry } from "@/types"
25
-
26
- interface Execution {
27
- execution_id: string
28
- kind: string
29
- name: string
30
- duration: number
31
- status: number | null
32
- outcome: string | null
33
- method: string | null
34
- route: string | null
35
- controller: string | null
36
- action: string | null
37
- queue: string | null
38
- attempt: number | null
39
- queue_latency: number | null
40
- queue_time: number | null
41
- job_id: string | null
42
- task_key: string | null
43
- inertia_component: string | null
44
- occurred_at: string
45
- deploy: string | null
46
- server: string | null
47
- user_ref: string | null
48
- tenant: string | null
49
- trace_id: string
50
- allocations: number | null
51
- peak_memory: number | null
52
- exception_preview: string | null
53
- profiled: boolean
54
- stages: Record<string, number>
55
- counters: Record<string, number>
56
- detail: Record<string, unknown>
57
- }
58
- interface Attachment {
59
- id: number
60
- name: string
61
- content_type: string | null
62
- bytes: number
63
- truncated: boolean
64
- viewable: boolean
65
- occurred_at: string
66
- }
67
- interface Props {
68
- execution: Execution
69
- timeline: TimelineEntry[]
70
- exceptions: ExceptionDetail[]
71
- logs: {
72
- id: number
73
- level: string
74
- message: string
75
- tags: string[]
76
- occurred_at: string
77
- stage: string | null
78
- }[]
79
- queries: {
80
- id: number
81
- sql: string
82
- duration: number
83
- source: string | null
84
- group_hash: string
85
- row_count: number | null
86
- stage: string | null
87
- offset: number
88
- }[]
89
- spans: {
90
- id: number
91
- name: string
92
- duration: number | null
93
- status: string | null
94
- attributes: Record<string, unknown>
95
- offset: number
96
- stage: string | null
97
- occurred_at: string
98
- }[]
99
- profile: {
100
- id: number
101
- profiler: string
102
- mode: string | null
103
- interval: number | null
104
- duration: number
105
- samples: number
106
- stacks_bytes: number | null
107
- collapsed: string
108
- truncated: boolean
109
- } | null
110
- attachments: Attachment[]
111
- enqueued_jobs: {
112
- id: number
113
- name: string
114
- queue: string
115
- job_id: string
116
- offset: number
117
- attempt_execution_id: string | null
118
- attempt_outcome: string | null
119
- }[]
120
- parent: { execution_id: string; kind: string; name: string } | null
121
- trace: {
122
- execution_id: string
123
- kind: string
124
- name: string
125
- duration: number
126
- status: number | null
127
- outcome: string | null
128
- occurred_at: string
129
- parent_id: string | null
130
- server: string | null
131
- current: boolean
132
- }[]
133
- trace_url: string | null
134
- issues: {
135
- id: number
136
- key: string
137
- title: string
138
- status: string
139
- group_hash: string
140
- }[]
141
- person: { ref: string; name: string | null; email: string | null } | null
142
- }
143
-
144
- const kindLabel: Record<string, string> = {
145
- request: "Request",
146
- job_attempt: "Job attempt",
147
- scheduled_task: "Scheduled task",
148
- command: "Command",
149
- channel_action: "Channel action",
150
- }
151
-
152
- // Tiny local copy-to-clipboard affordance for an inline id. CopyId doesn't
153
- // exist yet (owned elsewhere), so this stays private to this page.
154
- function CopyId({ value }: { value: string }) {
155
- const [, copy] = useClipboard()
156
- const [copied, setCopied] = useState(false)
157
- return (
158
- <button
159
- type="button"
160
- onClick={() => {
161
- void (async () => {
162
- const ok = await copy(value)
163
- if (ok) {
164
- setCopied(true)
165
- setTimeout(() => setCopied(false), 1200)
166
- }
167
- })()
168
- }}
169
- className="hover:text-foreground text-muted-foreground ml-1 inline-flex align-middle"
170
- title="Copy"
171
- >
172
- {copied ? <Check className="size-3" /> : <Copy className="size-3" />}
173
- </button>
174
- )
175
- }
176
-
177
- function parseContext(v: unknown): unknown {
178
- try {
179
- return JSON.parse(String(v)) as unknown
180
- } catch {
181
- return String(v)
182
- }
183
- }
184
-
185
- function markdownReport(p: Props, x: Execution) {
186
- const lines: string[] = []
187
- lines.push(`# ${x.name}`)
188
- lines.push("")
189
- lines.push(`- status: ${x.status ?? x.outcome ?? "-"}`)
190
- lines.push(`- duration: ${ms(x.duration)}`)
191
- lines.push(`- occurred_at: ${x.occurred_at}`)
192
- lines.push("")
193
- lines.push("## Stages")
194
- for (const [k, v] of Object.entries(x.stages)) lines.push(`- ${k}: ${ms(v)}`)
195
- lines.push("")
196
- lines.push("## Counters")
197
- for (const [k, v] of Object.entries(x.counters)) lines.push(`- ${k}: ${v}`)
198
- if (p.exceptions.length > 0) {
199
- lines.push("")
200
- lines.push("## Exceptions")
201
- for (const ex of p.exceptions) {
202
- lines.push(`### ${ex.class_name}: ${ex.message}`)
203
- for (const f of ex.frames.filter((fr) => fr.in_app)) {
204
- lines.push(`- ${f.file}:${f.line} in \`${f.function}\``)
205
- }
206
- }
207
- }
208
- const slow = [...p.queries]
209
- .sort((a, b) => b.duration - a.duration)
210
- .slice(0, 5)
211
- if (slow.length > 0) {
212
- lines.push("")
213
- lines.push("## Slow queries")
214
- for (const q of slow) lines.push(`- ${ms(q.duration, 2)} — \`${q.sql}\``)
215
- }
216
- return lines.join("\n")
217
- }
218
-
219
- export default function ExecutionShow(p: Props) {
220
- const { environment, range, window } = usePage<SharedProps>().props
221
- const a = environment!.application_id
222
- const e = environment!.id
223
- const x = p.execution
224
- const detail = x.detail as Record<string, never>
225
- const headers = (detail.headers ?? {}) as Record<string, string>
226
- const inertia = detail.inertia as Record<string, unknown> | undefined
227
- const [, copyMd] = useClipboard()
228
- const [mdCopied, setMdCopied] = useState(false)
229
- return (
230
- <EnvLayout
231
- title={x.name}
232
- crumbs={[
233
- { title: kindLabel[x.kind] ?? x.kind, href: "#" },
234
- { title: x.name, href: "#" },
235
- ]}
236
- >
237
- <PageHeader
238
- withWindow={false}
239
- title={
240
- <span className="flex flex-wrap items-center gap-2">
241
- <StatusBadge status={x.status} outcome={x.outcome} />
242
- <span className="font-mono">{x.name}</span>
243
- {x.profiled && (
244
- <Badge variant="secondary" className="font-mono">
245
- profiled
246
- </Badge>
247
- )}
248
- </span>
249
- }
250
- description={
251
- <span className="flex flex-wrap gap-x-3 text-xs">
252
- <span>{when(x.occurred_at)}</span>
253
- {x.server && <span>server {x.server}</span>}
254
- {x.deploy && <span>deploy {x.deploy.slice(0, 12)}</span>}
255
- {x.user_ref && (
256
- <span className="inline-flex items-center gap-1">
257
- origin user
258
- <OriginIdentity
259
- applicationId={a}
260
- environmentId={e}
261
- kind="user"
262
- range={range}
263
- window={window}
264
- user_ref={x.user_ref}
265
- tenant={x.tenant}
266
- person={
267
- p.person && {
268
- ref: p.person.ref,
269
- name: p.person.name ?? p.person.ref,
270
- }
271
- }
272
- />
273
- </span>
274
- )}
275
- {x.tenant && (
276
- <span className="inline-flex items-center gap-1">
277
- origin tenant
278
- <OriginIdentity
279
- applicationId={a}
280
- environmentId={e}
281
- kind="tenant"
282
- range={range}
283
- window={window}
284
- user_ref={x.user_ref}
285
- tenant={x.tenant}
286
- person={null}
287
- />
288
- </span>
289
- )}
290
- </span>
291
- }
292
- actions={
293
- <Button
294
- type="button"
295
- variant="outline"
296
- size="sm"
297
- onClick={() => {
298
- void (async () => {
299
- const ok = await copyMd(markdownReport(p, x))
300
- if (ok) {
301
- setMdCopied(true)
302
- setTimeout(() => setMdCopied(false), 1200)
303
- }
304
- })()
305
- }}
306
- >
307
- {mdCopied ? (
308
- <Check className="size-3.5" />
309
- ) : (
310
- <ClipboardCopy className="size-3.5" />
311
- )}
312
- Copy as Markdown
313
- </Button>
314
- }
315
- />
316
- {p.issues.length > 0 && (
317
- <div className="flex flex-wrap gap-2">
318
- {p.issues.map((i) => (
319
- <Link key={i.id} href={R.issuePath(i.id)}>
320
- <Badge variant="destructive" className="font-mono">
321
- {i.key}
322
- </Badge>
323
- </Link>
324
- ))}
325
- </div>
326
- )}
327
- <StatStrip>
328
- <Stat
329
- label="Duration"
330
- value={ms(x.duration)}
331
- hint={
332
- x.queue_latency != null
333
- ? `queued ${ms(x.queue_latency)}`
334
- : undefined
335
- }
336
- />
337
- <Stat
338
- label="Queries"
339
- value={x.counters.queries ?? 0}
340
- hint={`${x.counters.cached_queries ?? 0} cached · ${x.counters.hydrated_models ?? 0} models`}
341
- />
342
- {x.queue_time != null && (
343
- <Stat label="Queue time" value={ms(x.queue_time)} />
344
- )}
345
- <Stat label="Cache" value={x.counters.cache_events ?? 0} />
346
- <Stat
347
- label="Outgoing"
348
- value={x.counters.outgoing_requests ?? 0}
349
- hint={`${x.counters.jobs_enqueued ?? 0} jobs enqueued · ${x.counters.mail ?? 0} mail`}
350
- />
351
- <Stat
352
- label="Allocations"
353
- value={count(x.allocations)}
354
- hint={x.peak_memory ? `RSS ${bytes(x.peak_memory)}` : undefined}
355
- />
356
- <Stat
357
- label="Exceptions"
358
- value={x.counters.exceptions ?? 0}
359
- tone={x.counters.exceptions ? "destructive" : undefined}
360
- hint={
361
- x.counters.lazy_loads
362
- ? `${x.counters.lazy_loads} lazy loads`
363
- : undefined
364
- }
365
- />
366
- </StatStrip>
367
- {p.trace.length > 1 && (
368
- <Card>
369
- <CardHeader>
370
- <CardTitle className="flex flex-wrap items-center justify-between gap-2">
371
- Trace
372
- {p.trace_url && (
373
- <Link
374
- href={p.trace_url}
375
- className="text-primary text-xs font-normal hover:underline"
376
- >
377
- View distributed trace
378
- </Link>
379
- )}
380
- </CardTitle>
381
- </CardHeader>
382
- <CardContent>
383
- <div className="flex flex-wrap items-center gap-2 text-xs">
384
- {p.trace.map((t, i) => {
385
- const maxDur = Math.max(...p.trace.map((tt) => tt.duration), 1)
386
- return (
387
- <span
388
- key={t.execution_id}
389
- className="flex items-center gap-2"
390
- >
391
- {i > 0 && <span className="text-muted-foreground">→</span>}
392
- <Link
393
- href={executionPath({
394
- applicationId: a,
395
- environmentId: e,
396
- source: t.kind,
397
- executionId: t.execution_id,
398
- })!}
399
- className={
400
- t.current
401
- ? "bg-muted flex items-center gap-1.5 rounded px-2 py-1 font-semibold"
402
- : "hover:bg-muted flex items-center gap-1.5 rounded px-2 py-1"
403
- }
404
- >
405
- <StatusBadge status={t.status} outcome={t.outcome} />{" "}
406
- <span className="font-mono">{t.name}</span>
407
- <span className="bg-muted-foreground/25 relative h-1.5 w-10 overflow-hidden rounded-full">
408
- <span
409
- className="bg-primary absolute inset-y-0 left-0 rounded-full"
410
- style={{
411
- width: `${Math.max(4, (t.duration / maxDur) * 100)}%`,
412
- }}
413
- />
414
- </span>
415
- <span className="text-muted-foreground">
416
- {ms(t.duration)}
417
- </span>
418
- </Link>
419
- </span>
420
- )
421
- })}
422
- </div>
423
- </CardContent>
424
- </Card>
425
- )}
426
- {p.exceptions.map((ex) => (
427
- <Card key={ex.id} className="border-destructive/40">
428
- <CardHeader>
429
- <CardTitle>Exception</CardTitle>
430
- </CardHeader>
431
- <CardContent>
432
- <ExceptionCard exception={ex} deploy={p.execution.deploy} />
433
- </CardContent>
434
- </Card>
435
- ))}
436
- <Card>
437
- <CardHeader>
438
- <CardTitle>Timeline</CardTitle>
439
- </CardHeader>
440
- <CardContent>
441
- <Timeline entries={p.timeline} total={x.duration} stages={x.stages} />
442
- </CardContent>
443
- </Card>
444
- {p.spans.length > 0 && (
445
- <Card>
446
- <CardHeader>
447
- <CardTitle>Spans</CardTitle>
448
- </CardHeader>
449
- <CardContent>
450
- <DataTable
451
- rows={p.spans}
452
- rowKey={(s) => s.id}
453
- columns={[
454
- {
455
- key: "off",
456
- header: "+ms",
457
- align: "right",
458
- cell: (s) => s.offset.toFixed(1),
459
- },
460
- {
461
- key: "name",
462
- header: "Span",
463
- cell: (s) => (
464
- <span className="font-mono text-xs">{s.name}</span>
465
- ),
466
- },
467
- {
468
- key: "status",
469
- header: "Status",
470
- cell: (s) => <StatusBadge outcome={s.status} />,
471
- },
472
- {
473
- key: "attrs",
474
- header: "Attributes",
475
- cell: (s) => (
476
- <span className="flex flex-wrap gap-1">
477
- {Object.entries(s.attributes).map(([key, value]) => (
478
- <span
479
- key={key}
480
- className="bg-muted rounded-sm px-1.5 font-mono text-[10px]"
481
- >
482
- {key}={String(value)}
483
- </span>
484
- ))}
485
- </span>
486
- ),
487
- },
488
- {
489
- key: "dur",
490
- header: "Duration",
491
- align: "right",
492
- cell: (s) => ms(s.duration, 2),
493
- },
494
- ]}
495
- />
496
- </CardContent>
497
- </Card>
498
- )}
499
- {p.profile && (
500
- <Card>
501
- <CardHeader>
502
- <CardTitle className="flex flex-wrap items-center justify-between gap-2">
503
- Profile
504
- <Link
505
- href={R.applicationEnvironmentProfilePath(a, e, p.profile.id)}
506
- className="text-primary text-xs font-normal hover:underline"
507
- >
508
- Open full profile
509
- </Link>
510
- </CardTitle>
511
- </CardHeader>
512
- <CardContent className="space-y-2">
513
- <p className="text-muted-foreground font-mono text-[11px]">
514
- {p.profile.profiler}
515
- {p.profile.mode ? ` · ${p.profile.mode}` : ""} ·{" "}
516
- {count(p.profile.samples)} samples · {ms(p.profile.duration)}
517
- {p.profile.truncated && " · truncated for this page"}
518
- </p>
519
- <Flamegraph collapsed={p.profile.collapsed} />
520
- </CardContent>
521
- </Card>
522
- )}
523
- {p.attachments.length > 0 && (
524
- <Card>
525
- <CardHeader>
526
- <CardTitle>Attachments</CardTitle>
527
- </CardHeader>
528
- <CardContent>
529
- <DataTable
530
- rows={p.attachments}
531
- rowKey={(f) => f.id}
532
- columns={[
533
- {
534
- key: "name",
535
- header: "Name",
536
- className: "max-w-0",
537
- cell: (f) => (
538
- <span className="flex items-center gap-1.5">
539
- <span className="truncate font-mono text-xs">
540
- {f.name}
541
- </span>
542
- {f.truncated && (
543
- <Badge variant="secondary">truncated</Badge>
544
- )}
545
- </span>
546
- ),
547
- },
548
- {
549
- key: "type",
550
- hideOnMobile: true,
551
- header: "Type",
552
- cell: (f) => (
553
- <span className="text-muted-foreground font-mono text-xs">
554
- {f.content_type ?? "–"}
555
- </span>
556
- ),
557
- },
558
- {
559
- key: "size",
560
- header: "Size",
561
- align: "right",
562
- cell: (f) => bytes(f.bytes),
563
- },
564
- {
565
- key: "links",
566
- header: "",
567
- align: "right",
568
- cell: (f) => (
569
- <span className="flex justify-end gap-3">
570
- {f.viewable && (
571
- <a
572
- className="text-primary hover:underline"
573
- href={R.applicationEnvironmentAttachmentPath(
574
- a,
575
- e,
576
- f.id,
577
- { view: 1 },
578
- )}
579
- >
580
- View
581
- </a>
582
- )}
583
- <a
584
- className="text-primary hover:underline"
585
- href={R.applicationEnvironmentAttachmentPath(
586
- a,
587
- e,
588
- f.id,
589
- )}
590
- >
591
- Download
592
- </a>
593
- </span>
594
- ),
595
- },
596
- ]}
597
- />
598
- </CardContent>
599
- </Card>
600
- )}
601
- <Tabs defaultValue="queries">
602
- <TabsList>
603
- <TabsTrigger value="queries">
604
- Queries ({p.queries.length})
605
- </TabsTrigger>
606
- <TabsTrigger value="logs">Logs ({p.logs.length})</TabsTrigger>
607
- <TabsTrigger value="jobs">
608
- Enqueued jobs ({p.enqueued_jobs.length})
609
- </TabsTrigger>
610
- <TabsTrigger value="request">Details</TabsTrigger>
611
- </TabsList>
612
- <TabsContent value="queries">
613
- <DataTable
614
- rows={p.queries}
615
- rowKey={(q) => q.id}
616
- empty="No queries."
617
- columns={[
618
- {
619
- key: "off",
620
- header: "+ms",
621
- align: "right",
622
- cell: (q) => q.offset.toFixed(1),
623
- },
624
- {
625
- key: "sql",
626
- header: "SQL",
627
- cell: (q) => (
628
- <Link
629
- href={R.applicationEnvironmentQueryPath(a, e, q.group_hash)}
630
- >
631
- <SqlBlock sql={q.sql} className="hover:underline" />
632
- </Link>
633
- ),
634
- },
635
- {
636
- key: "rows",
637
- header: "Rows",
638
- align: "right",
639
- cell: (q) => q.row_count ?? "–",
640
- },
641
- {
642
- key: "src",
643
- header: "Source",
644
- cell: (q) => (
645
- <span className="text-muted-foreground font-mono text-xs">
646
- {q.source ?? ""}
647
- </span>
648
- ),
649
- },
650
- {
651
- key: "dur",
652
- header: "Duration",
653
- align: "right",
654
- cell: (q) => ms(q.duration, 2),
655
- },
656
- ]}
657
- />
658
- </TabsContent>
659
- <TabsContent value="logs">
660
- <DataTable
661
- rows={p.logs}
662
- rowKey={(l) => l.id}
663
- empty="No logs."
664
- columns={[
665
- {
666
- key: "lvl",
667
- header: "Level",
668
- cell: (l) => <LevelBadge level={l.level} />,
669
- },
670
- {
671
- key: "msg",
672
- header: "Message",
673
- cell: (l) => (
674
- <span className="font-mono text-xs whitespace-pre-wrap">
675
- {l.message}
676
- </span>
677
- ),
678
- },
679
- { key: "tags", header: "Tags", cell: (l) => l.tags.join(" ") },
680
- {
681
- key: "stage",
682
- header: "Stage",
683
- cell: (l) => (
684
- <span className="text-muted-foreground text-xs">
685
- {l.stage ?? ""}
686
- </span>
687
- ),
688
- },
689
- ]}
690
- />
691
- </TabsContent>
692
- <TabsContent value="jobs">
693
- <DataTable
694
- rows={p.enqueued_jobs}
695
- rowKey={(j) => j.id}
696
- empty="No jobs enqueued."
697
- columns={[
698
- {
699
- key: "name",
700
- header: "Job",
701
- cell: (j) => (
702
- <span className="font-mono text-xs">{j.name}</span>
703
- ),
704
- },
705
- { key: "queue", header: "Queue", cell: (j) => j.queue },
706
- {
707
- key: "id",
708
- header: "Job id",
709
- cell: (j) => (
710
- <span className="font-mono text-xs">{j.job_id}</span>
711
- ),
712
- },
713
- {
714
- key: "attempt",
715
- header: "Attempt",
716
- cell: (j) =>
717
- j.attempt_execution_id ? (
718
- <Link
719
- className="hover:underline"
720
- href={R.applicationEnvironmentJobPath(
721
- a,
722
- e,
723
- j.attempt_execution_id,
724
- )}
725
- >
726
- <StatusBadge outcome={j.attempt_outcome} />
727
- </Link>
728
- ) : (
729
- <span className="text-muted-foreground text-xs">
730
- not seen yet
731
- </span>
732
- ),
733
- },
734
- ]}
735
- />
736
- </TabsContent>
737
- <TabsContent value="request">
738
- <div className="grid gap-4 lg:grid-cols-2">
739
- <Card>
740
- <CardHeader>
741
- <CardTitle>Execution</CardTitle>
742
- </CardHeader>
743
- <CardContent>
744
- <dl className="grid gap-y-1 text-xs sm:grid-cols-[10rem_1fr]">
745
- {Object.entries({
746
- kind: x.kind,
747
- trace_id: x.trace_id,
748
- execution_id: x.execution_id,
749
- controller: x.controller && `${x.controller}#${x.action}`,
750
- route: x.route,
751
- url: detail.url,
752
- ip: detail.ip,
753
- format: detail.format,
754
- queue: x.queue,
755
- attempt: x.attempt,
756
- job_id: x.job_id,
757
- task_key: x.task_key,
758
- schedule: detail.schedule,
759
- inertia_component: x.inertia_component,
760
- redirect_to: detail.redirect_to,
761
- halted_callback: detail.halted_callback,
762
- view_runtime: detail.view_runtime,
763
- db_runtime: detail.db_runtime,
764
- user_agent: detail.user_agent,
765
- arguments: Array.isArray(detail.arguments_preview)
766
- ? (detail.arguments_preview as string[]).join(", ")
767
- : undefined,
768
- })
769
- .filter(
770
- ([, v]) => v !== null && v !== undefined && v !== "",
771
- )
772
- .map(([k, v]) => (
773
- <div key={k} className="contents">
774
- <dt className="text-muted-foreground">{k}</dt>
775
- <dd className="font-mono break-all">
776
- {String(v)}
777
- {(k === "trace_id" || k === "execution_id") && (
778
- <CopyId value={String(v)} />
779
- )}
780
- </dd>
781
- </div>
782
- ))}
783
- </dl>
784
- {inertia && (
785
- <div className="mt-3">
786
- <JsonViewer data={inertia} />
787
- </div>
788
- )}
789
- {detail.context && detail.context !== "{}" && (
790
- <div className="mt-3">
791
- <JsonViewer data={parseContext(detail.context)} />
792
- </div>
793
- )}
794
- </CardContent>
795
- </Card>
796
- <Card>
797
- <CardHeader>
798
- <CardTitle>Headers</CardTitle>
799
- </CardHeader>
800
- <CardContent>
801
- <dl className="grid gap-y-1 text-xs sm:grid-cols-[12rem_1fr]">
802
- {Object.entries(headers).map(([k, v]) => (
803
- <div key={k} className="contents">
804
- <dt className="text-muted-foreground font-mono">{k}</dt>
805
- <dd className="font-mono break-all">{v}</dd>
806
- </div>
807
- ))}
808
- </dl>
809
- {detail.payload != null && (
810
- <div className="mt-3">
811
- <JsonViewer data={detail.payload} />
812
- </div>
813
- )}
814
- </CardContent>
815
- </Card>
816
- </div>
817
- </TabsContent>
818
- </Tabs>
819
- </EnvLayout>
820
- )
821
- }