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.
- checksums.yaml +4 -4
- data/AGENTS.md +37 -33
- data/CHANGELOG.md +66 -0
- data/README.md +51 -60
- data/config/routes.rb +0 -59
- data/docs/ai-and-mcp.md +9 -8
- data/docs/configuration.md +57 -71
- data/docs/faq.md +17 -25
- data/docs/getting-started.md +105 -117
- data/docs/replacing-nightwatch.md +8 -7
- data/docs/replacing-sentry.md +12 -16
- data/docs/security.md +14 -20
- data/docs/self-hosting.md +24 -22
- data/docs/testing.md +6 -14
- data/docs/troubleshooting.md +12 -46
- data/lib/generators/railwatch/install/install_generator.rb +12 -240
- data/lib/generators/railwatch/install/templates/initializer.rb.tt +10 -40
- data/lib/generators/railwatch/install/templates/post-deploy +0 -7
- data/lib/railwatch/authentication.rb +6 -5
- data/lib/railwatch/buffer.rb +9 -0
- data/lib/railwatch/configuration.rb +21 -151
- data/lib/railwatch/engine.rb +9 -118
- data/lib/railwatch/patches.rb +0 -11
- data/lib/railwatch/reporter.rb +92 -21
- data/lib/railwatch/sampler.rb +10 -2
- data/lib/railwatch/subscribers/process_info.rb +1 -2
- data/lib/railwatch/version.rb +1 -1
- data/lib/railwatch.rb +8 -64
- data/lib/tasks/railwatch_tasks.rake +3 -325
- data/llms.txt +23 -25
- metadata +3 -453
- data/app/channels/railwatch/environment_channel.rb +0 -62
- data/app/controllers/concerns/railwatch/telemetry_identity.rb +0 -25
- data/app/controllers/railwatch/alerts_controller.rb +0 -48
- data/app/controllers/railwatch/anomaly_rules_controller.rb +0 -31
- data/app/controllers/railwatch/attachments_controller.rb +0 -22
- data/app/controllers/railwatch/broadcasts_controller.rb +0 -15
- data/app/controllers/railwatch/cache_events_controller.rb +0 -30
- data/app/controllers/railwatch/commands_controller.rb +0 -16
- data/app/controllers/railwatch/comments_controller.rb +0 -11
- data/app/controllers/railwatch/dashboard_controller.rb +0 -70
- data/app/controllers/railwatch/deploys_controller.rb +0 -34
- data/app/controllers/railwatch/deprecations_controller.rb +0 -54
- data/app/controllers/railwatch/environment_scoped.rb +0 -81
- data/app/controllers/railwatch/exceptions_controller.rb +0 -52
- data/app/controllers/railwatch/executions_controller.rb +0 -13
- data/app/controllers/railwatch/issues_controller.rb +0 -258
- data/app/controllers/railwatch/jobs_controller.rb +0 -63
- data/app/controllers/railwatch/llm_calls_controller.rb +0 -124
- data/app/controllers/railwatch/logs_controller.rb +0 -40
- data/app/controllers/railwatch/mails_controller.rb +0 -19
- data/app/controllers/railwatch/notifications_controller.rb +0 -11
- data/app/controllers/railwatch/outgoing_requests_controller.rb +0 -19
- data/app/controllers/railwatch/overview_controller.rb +0 -38
- data/app/controllers/railwatch/people_controller.rb +0 -25
- data/app/controllers/railwatch/processes_controller.rb +0 -33
- data/app/controllers/railwatch/profiles_controller.rb +0 -65
- data/app/controllers/railwatch/queries_controller.rb +0 -59
- data/app/controllers/railwatch/releases_controller.rb +0 -75
- data/app/controllers/railwatch/requests_controller.rb +0 -37
- data/app/controllers/railwatch/saved_views_controller.rb +0 -53
- data/app/controllers/railwatch/scheduled_tasks_controller.rb +0 -45
- data/app/controllers/railwatch/spans_controller.rb +0 -47
- data/app/controllers/railwatch/storage_ops_controller.rb +0 -15
- data/app/controllers/railwatch/tenants_controller.rb +0 -44
- data/app/controllers/railwatch/thresholds_controller.rb +0 -47
- data/app/controllers/railwatch/traces_controller.rb +0 -72
- data/app/controllers/railwatch/transactions_controller.rb +0 -21
- data/app/controllers/railwatch/view_renders_controller.rb +0 -28
- data/app/controllers/railwatch/visits_controller.rb +0 -53
- data/app/frontend/components/app-content.tsx +0 -26
- data/app/frontend/components/app-logo-icon.tsx +0 -23
- data/app/frontend/components/app-logo.tsx +0 -14
- data/app/frontend/components/app-shell.tsx +0 -38
- data/app/frontend/components/app-sidebar-header.tsx +0 -54
- data/app/frontend/components/app-sidebar.tsx +0 -94
- data/app/frontend/components/app-wordmark.tsx +0 -29
- data/app/frontend/components/breadcrumbs.tsx +0 -46
- data/app/frontend/components/icon.tsx +0 -16
- data/app/frontend/components/input-error.tsx +0 -18
- data/app/frontend/components/nav-footer.tsx +0 -46
- data/app/frontend/components/nav-main.tsx +0 -41
- data/app/frontend/components/nav-user.tsx +0 -50
- data/app/frontend/components/railwatch/chart-hover.tsx +0 -34
- data/app/frontend/components/railwatch/chart-panel.tsx +0 -170
- data/app/frontend/components/railwatch/code.tsx +0 -28
- data/app/frontend/components/railwatch/command-palette.tsx +0 -132
- data/app/frontend/components/railwatch/copy-block.tsx +0 -52
- data/app/frontend/components/railwatch/copy-id.tsx +0 -49
- data/app/frontend/components/railwatch/cursor-load-more.tsx +0 -39
- data/app/frontend/components/railwatch/data-table.tsx +0 -195
- data/app/frontend/components/railwatch/digit-roll.tsx +0 -312
- data/app/frontend/components/railwatch/empty-state.tsx +0 -159
- data/app/frontend/components/railwatch/env-nav.tsx +0 -130
- data/app/frontend/components/railwatch/env-switcher.tsx +0 -145
- data/app/frontend/components/railwatch/filter-bar.tsx +0 -153
- data/app/frontend/components/railwatch/flamegraph.tsx +0 -324
- data/app/frontend/components/railwatch/frames.tsx +0 -295
- data/app/frontend/components/railwatch/json-viewer.tsx +0 -159
- data/app/frontend/components/railwatch/live-dot.tsx +0 -78
- data/app/frontend/components/railwatch/live-toggle.tsx +0 -76
- data/app/frontend/components/railwatch/metric-chart.tsx +0 -112
- data/app/frontend/components/railwatch/mobile-page-switcher.tsx +0 -89
- data/app/frontend/components/railwatch/onboarding.tsx +0 -91
- data/app/frontend/components/railwatch/origin-identity.tsx +0 -84
- data/app/frontend/components/railwatch/page-header.tsx +0 -42
- data/app/frontend/components/railwatch/percentile-picker.tsx +0 -18
- data/app/frontend/components/railwatch/relative-time.tsx +0 -18
- data/app/frontend/components/railwatch/saved-views.tsx +0 -232
- data/app/frontend/components/railwatch/segmented.tsx +0 -64
- data/app/frontend/components/railwatch/series-chart.tsx +0 -400
- data/app/frontend/components/railwatch/shortcuts-dialog.tsx +0 -89
- data/app/frontend/components/railwatch/signal-toast.tsx +0 -121
- data/app/frontend/components/railwatch/sort-header.tsx +0 -37
- data/app/frontend/components/railwatch/source-link.tsx +0 -57
- data/app/frontend/components/railwatch/sparkline-cell.tsx +0 -14
- data/app/frontend/components/railwatch/sql-block.tsx +0 -116
- data/app/frontend/components/railwatch/stat.tsx +0 -185
- data/app/frontend/components/railwatch/status-badge.tsx +0 -79
- data/app/frontend/components/railwatch/step-picker.tsx +0 -57
- data/app/frontend/components/railwatch/timeline.tsx +0 -405
- data/app/frontend/components/railwatch/utilisation-bar.tsx +0 -44
- data/app/frontend/components/railwatch/window-picker.tsx +0 -147
- data/app/frontend/components/ui/avatar.tsx +0 -51
- data/app/frontend/components/ui/badge.tsx +0 -46
- data/app/frontend/components/ui/breadcrumb.tsx +0 -109
- data/app/frontend/components/ui/button.tsx +0 -60
- data/app/frontend/components/ui/card.tsx +0 -92
- data/app/frontend/components/ui/chart.tsx +0 -374
- data/app/frontend/components/ui/checkbox.tsx +0 -32
- data/app/frontend/components/ui/collapsible.tsx +0 -31
- data/app/frontend/components/ui/command.tsx +0 -182
- data/app/frontend/components/ui/dialog.tsx +0 -158
- data/app/frontend/components/ui/dropdown-menu.tsx +0 -255
- data/app/frontend/components/ui/hover-card.tsx +0 -42
- data/app/frontend/components/ui/input.tsx +0 -21
- data/app/frontend/components/ui/label.tsx +0 -24
- data/app/frontend/components/ui/popover.tsx +0 -87
- data/app/frontend/components/ui/select.tsx +0 -185
- data/app/frontend/components/ui/separator.tsx +0 -28
- data/app/frontend/components/ui/sheet.tsx +0 -137
- data/app/frontend/components/ui/sidebar.tsx +0 -726
- data/app/frontend/components/ui/skeleton.tsx +0 -13
- data/app/frontend/components/ui/sonner.tsx +0 -41
- data/app/frontend/components/ui/table.tsx +0 -114
- data/app/frontend/components/ui/tabs.tsx +0 -91
- data/app/frontend/components/ui/textarea.tsx +0 -18
- data/app/frontend/components/ui/tooltip.tsx +0 -61
- data/app/frontend/components/user-info.tsx +0 -32
- data/app/frontend/components/user-menu-content.tsx +0 -94
- data/app/frontend/entrypoints/application.css +0 -244
- data/app/frontend/entrypoints/inertia.tsx +0 -92
- data/app/frontend/fonts/CommitMono-Bold.woff2 +0 -0
- data/app/frontend/fonts/CommitMono-Regular.woff2 +0 -0
- data/app/frontend/fonts/Roboto-Black.woff2 +0 -0
- data/app/frontend/fonts/Roboto-Bold.woff2 +0 -0
- data/app/frontend/fonts/Roboto-Medium.woff2 +0 -0
- data/app/frontend/fonts/Roboto-Regular.woff2 +0 -0
- data/app/frontend/hooks/use-appearance.tsx +0 -67
- data/app/frontend/hooks/use-clipboard.ts +0 -32
- data/app/frontend/hooks/use-flash.tsx +0 -23
- data/app/frontend/hooks/use-hotkeys.ts +0 -63
- data/app/frontend/hooks/use-initials.tsx +0 -15
- data/app/frontend/hooks/use-live.ts +0 -205
- data/app/frontend/hooks/use-mobile-navigation.ts +0 -8
- data/app/frontend/hooks/use-mobile.ts +0 -21
- data/app/frontend/hooks/use-percentile.ts +0 -24
- data/app/frontend/hooks/use-reduced-motion.ts +0 -25
- data/app/frontend/hooks/use-row-nav.ts +0 -106
- data/app/frontend/hooks/use-step.ts +0 -27
- data/app/frontend/hooks/use-ticker.ts +0 -12
- data/app/frontend/hooks/use-window.ts +0 -58
- data/app/frontend/layouts/app/app-sidebar-layout.tsx +0 -107
- data/app/frontend/layouts/app-layout.tsx +0 -21
- data/app/frontend/layouts/env-layout.tsx +0 -53
- data/app/frontend/layouts/persistent-layout.tsx +0 -18
- data/app/frontend/lib/cable.ts +0 -10
- data/app/frontend/lib/env-pages.ts +0 -236
- data/app/frontend/lib/execution-path.ts +0 -55
- data/app/frontend/lib/explain.ts +0 -50
- data/app/frontend/lib/filter.ts +0 -25
- data/app/frontend/lib/flamegraph.ts +0 -155
- data/app/frontend/lib/format.ts +0 -71
- data/app/frontend/lib/keyboard.ts +0 -18
- data/app/frontend/lib/mount.ts +0 -23
- data/app/frontend/lib/source-link.ts +0 -217
- data/app/frontend/lib/utils.ts +0 -6
- data/app/frontend/pages/alerts/index.tsx +0 -171
- data/app/frontend/pages/broadcasts/index.tsx +0 -171
- data/app/frontend/pages/cache_events/index.tsx +0 -127
- data/app/frontend/pages/commands/index.tsx +0 -143
- data/app/frontend/pages/deploys/index.tsx +0 -94
- data/app/frontend/pages/deploys/show.tsx +0 -309
- data/app/frontend/pages/deprecations/index.tsx +0 -176
- data/app/frontend/pages/exceptions/index.tsx +0 -271
- data/app/frontend/pages/executions/show.tsx +0 -821
- data/app/frontend/pages/issues/index.tsx +0 -399
- data/app/frontend/pages/issues/show.tsx +0 -1367
- data/app/frontend/pages/jobs/index.tsx +0 -381
- data/app/frontend/pages/jobs/klass.tsx +0 -215
- data/app/frontend/pages/llm_calls/index.tsx +0 -664
- data/app/frontend/pages/logs/index.tsx +0 -303
- data/app/frontend/pages/mails/index.tsx +0 -218
- data/app/frontend/pages/notifications/index.tsx +0 -161
- data/app/frontend/pages/outgoing_requests/index.tsx +0 -309
- data/app/frontend/pages/overview/show.tsx +0 -333
- data/app/frontend/pages/people/index.tsx +0 -102
- data/app/frontend/pages/people/show.tsx +0 -149
- data/app/frontend/pages/processes/index.tsx +0 -335
- data/app/frontend/pages/profiles/index.tsx +0 -110
- data/app/frontend/pages/profiles/show.tsx +0 -105
- data/app/frontend/pages/queries/index.tsx +0 -492
- data/app/frontend/pages/queries/show.tsx +0 -276
- data/app/frontend/pages/releases/index.tsx +0 -190
- data/app/frontend/pages/releases/release-health.tsx +0 -68
- data/app/frontend/pages/releases/release-path.ts +0 -25
- data/app/frontend/pages/releases/show.tsx +0 -262
- data/app/frontend/pages/requests/index.tsx +0 -235
- data/app/frontend/pages/requests/route.tsx +0 -159
- data/app/frontend/pages/scheduled_tasks/index.tsx +0 -220
- data/app/frontend/pages/spans/index.tsx +0 -229
- data/app/frontend/pages/spans/show.tsx +0 -220
- data/app/frontend/pages/storage_ops/index.tsx +0 -150
- data/app/frontend/pages/tenants/index.tsx +0 -236
- data/app/frontend/pages/tenants/show.tsx +0 -477
- data/app/frontend/pages/tenants/tenant-path.ts +0 -26
- data/app/frontend/pages/thresholds/index.tsx +0 -414
- data/app/frontend/pages/traces/show.tsx +0 -208
- data/app/frontend/pages/transactions/index.tsx +0 -220
- data/app/frontend/pages/view_renders/index.tsx +0 -241
- data/app/frontend/pages/visits/index.tsx +0 -321
- data/app/frontend/public/icon.png +0 -0
- data/app/frontend/public/icon.svg +0 -5
- data/app/frontend/routes/index.d.ts +0 -2136
- data/app/frontend/routes/index.js +0 -2112
- data/app/frontend/types/globals.d.ts +0 -9
- data/app/frontend/types/index.ts +0 -288
- data/app/frontend/types/vite-env.d.ts +0 -9
- data/app/helpers/railwatch/assets_helper.rb +0 -52
- data/app/jobs/railwatch/anomaly_scan_job.rb +0 -11
- data/app/jobs/railwatch/application_job.rb +0 -7
- data/app/jobs/railwatch/auto_resolve_issues_job.rb +0 -19
- data/app/jobs/railwatch/check_scheduled_tasks_job.rb +0 -113
- data/app/jobs/railwatch/detect_anomalies_job.rb +0 -171
- data/app/jobs/railwatch/detect_performance_issues_job.rb +0 -95
- data/app/jobs/railwatch/group_exceptions_job.rb +0 -91
- data/app/jobs/railwatch/optimize_telemetry_job.rb +0 -23
- data/app/jobs/railwatch/performance_scan_job.rb +0 -11
- data/app/jobs/railwatch/prune_telemetry_job.rb +0 -129
- data/app/jobs/railwatch/release_health_rollup_job.rb +0 -64
- data/app/jobs/railwatch/rollup_catchup_job.rb +0 -21
- data/app/jobs/railwatch/rollup_job.rb +0 -132
- data/app/jobs/railwatch/scheduled_task_scan_job.rb +0 -11
- data/app/models/concerns/railwatch/detection_snapshotting.rb +0 -20
- data/app/models/railwatch/alert.rb +0 -229
- data/app/models/railwatch/alert_rule.rb +0 -121
- data/app/models/railwatch/anomaly_rule.rb +0 -51
- data/app/models/railwatch/application.rb +0 -44
- data/app/models/railwatch/application_record.rb +0 -33
- data/app/models/railwatch/comment.rb +0 -36
- data/app/models/railwatch/deploy.rb +0 -67
- data/app/models/railwatch/environment.rb +0 -68
- data/app/models/railwatch/execution_presenter.rb +0 -185
- data/app/models/railwatch/filter_query.rb +0 -184
- data/app/models/railwatch/followup_receipt.rb +0 -27
- data/app/models/railwatch/ingest/batch.rb +0 -355
- data/app/models/railwatch/ingest/mapper.rb +0 -585
- data/app/models/railwatch/ingest/payload.rb +0 -96
- data/app/models/railwatch/ingest/rollup_absorber.rb +0 -170
- data/app/models/railwatch/ingest/writer.rb +0 -137
- data/app/models/railwatch/issue.rb +0 -277
- data/app/models/railwatch/issue_activity.rb +0 -23
- data/app/models/railwatch/issue_detection_presenter.rb +0 -309
- data/app/models/railwatch/issue_detection_snapshot.rb +0 -77
- data/app/models/railwatch/maintenance_task.rb +0 -53
- data/app/models/railwatch/saved_view.rb +0 -63
- data/app/models/railwatch/telemetry/aggregations.rb +0 -251
- data/app/models/railwatch/telemetry/attachment.rb +0 -31
- data/app/models/railwatch/telemetry/bounded_gzip.rb +0 -101
- data/app/models/railwatch/telemetry/broadcast.rb +0 -13
- data/app/models/railwatch/telemetry/cache_event.rb +0 -16
- data/app/models/railwatch/telemetry/child.rb +0 -53
- data/app/models/railwatch/telemetry/cursor_page.rb +0 -99
- data/app/models/railwatch/telemetry/deprecation.rb +0 -13
- data/app/models/railwatch/telemetry/enqueued_job.rb +0 -13
- data/app/models/railwatch/telemetry/exception.rb +0 -28
- data/app/models/railwatch/telemetry/execution.rb +0 -152
- data/app/models/railwatch/telemetry/export_delivery.rb +0 -32
- data/app/models/railwatch/telemetry/export_destination.rb +0 -66
- data/app/models/railwatch/telemetry/health_sample.rb +0 -72
- data/app/models/railwatch/telemetry/ingest_batch.rb +0 -31
- data/app/models/railwatch/telemetry/llm_call.rb +0 -37
- data/app/models/railwatch/telemetry/log.rb +0 -91
- data/app/models/railwatch/telemetry/mail.rb +0 -13
- data/app/models/railwatch/telemetry/n_plus_one.rb +0 -92
- data/app/models/railwatch/telemetry/notification.rb +0 -13
- data/app/models/railwatch/telemetry/outgoing_request.rb +0 -13
- data/app/models/railwatch/telemetry/person.rb +0 -50
- data/app/models/railwatch/telemetry/process.rb +0 -10
- data/app/models/railwatch/telemetry/profile.rb +0 -37
- data/app/models/railwatch/telemetry/query.rb +0 -57
- data/app/models/railwatch/telemetry/query_shape.rb +0 -45
- data/app/models/railwatch/telemetry/release_health.rb +0 -69
- data/app/models/railwatch/telemetry/rollup.rb +0 -157
- data/app/models/railwatch/telemetry/session.rb +0 -24
- data/app/models/railwatch/telemetry/span.rb +0 -19
- data/app/models/railwatch/telemetry/storage_op.rb +0 -13
- data/app/models/railwatch/telemetry/tenant.rb +0 -240
- data/app/models/railwatch/telemetry/transaction.rb +0 -13
- data/app/models/railwatch/telemetry/view_render.rb +0 -13
- data/app/models/railwatch/telemetry/visit.rb +0 -24
- data/app/models/railwatch/telemetry_record.rb +0 -107
- data/app/models/railwatch/threshold.rb +0 -66
- data/app/models/railwatch/user.rb +0 -47
- data/app/models/railwatch/viewer.rb +0 -13
- data/app/models/railwatch/window.rb +0 -66
- data/app/views/layouts/railwatch/dashboard.html.erb +0 -25
- data/db/railwatch_migrate/20260916000000_create_railwatch_tables.rb +0 -151
- data/db/railwatch_migrate/20260917000000_create_railwatch_maintenance_tasks.rb +0 -18
- data/db/railwatch_migrate/20260917120000_create_railwatch_followup_receipts.rb +0 -20
- data/db/railwatch_migrate/20260918120000_widen_host_user_ids.rb +0 -71
- data/db/railwatch_telemetry_migrate/20260903000000_enable_incremental_vacuum.rb +0 -62
- data/db/railwatch_telemetry_migrate/20260903000001_create_telemetry.rb +0 -481
- data/db/railwatch_telemetry_migrate/20260903000002_rename_tenant_to_app_tenant.rb +0 -14
- data/db/railwatch_telemetry_migrate/20260903000003_add_statement_count_to_transactions.rb +0 -9
- data/db/railwatch_telemetry_migrate/20260903000004_add_role_and_channel.rb +0 -10
- data/db/railwatch_telemetry_migrate/20260903000005_add_locals_to_exceptions.rb +0 -9
- data/db/railwatch_telemetry_migrate/20260903000006_add_spans_health_vitals_and_fts.rb +0 -82
- data/db/railwatch_telemetry_migrate/20260903000007_rename_span_attributes_to_payload.rb +0 -10
- data/db/railwatch_telemetry_migrate/20260903000008_add_profiles_and_attachments.rb +0 -60
- data/db/railwatch_telemetry_migrate/20260903000009_add_truncated_to_attachments.rb +0 -9
- data/db/railwatch_telemetry_migrate/20260903000010_create_sessions_and_release_health.rb +0 -51
- data/db/railwatch_telemetry_migrate/20260903000011_add_fingerprint_to_exceptions.rb +0 -11
- data/db/railwatch_telemetry_migrate/20260904000012_add_failed_to_broadcasts.rb +0 -10
- data/db/railwatch_telemetry_migrate/20260904010000_add_filter_cursor_indexes.rb +0 -37
- data/db/railwatch_telemetry_migrate/20260904020000_add_n_plus_ones_execution_id_index.rb +0 -11
- data/db/railwatch_telemetry_migrate/20260904120000_create_query_shapes.rb +0 -15
- data/db/railwatch_telemetry_migrate/20260906120000_add_backpressure_factor_to_ingest_batches.rb +0 -9
- data/db/railwatch_telemetry_migrate/20260907000000_rename_lantern_version_on_processes.rb +0 -10
- data/db/railwatch_telemetry_migrate/20260913000000_rename_nightrail_version_on_processes.rb +0 -9
- data/db/railwatch_telemetry_migrate/20260914000000_drop_orphan_durable_ingest_tables.rb +0 -18
- data/db/railwatch_telemetry_migrate/20260914010000_drop_orphan_durable_ingest_columns.rb +0 -25
- data/db/railwatch_telemetry_migrate/20260915000000_create_llm_calls.rb +0 -55
- data/db/railwatch_telemetry_migrate/20260915120000_add_detail_to_llm_calls.rb +0 -21
- data/db/railwatch_telemetry_migrate/20260915200000_add_explained_index_to_queries.rb +0 -16
- data/db/railwatch_telemetry_migrate/20260915210000_add_slowest_index_to_queries.rb +0 -18
- data/db/railwatch_telemetry_migrate/20260917010000_add_batch_ledger_to_ingest_batches.rb +0 -16
- data/db/railwatch_telemetry_migrate/20260919000100_create_export_queue.rb +0 -146
- data/db/railwatch_telemetry_migrate/20260922000000_add_covering_indexes_for_dashboard_aggregates.rb +0 -34
- data/db/railwatch_telemetry_migrate/20260923000000_add_people_index.rb +0 -15
- data/db/railwatch_telemetry_migrate/20260925000000_add_tenant_summary_index.rb +0 -17
- data/db/railwatch_telemetry_migrate/20261008000000_add_in_transaction_to_outgoing_requests.rb +0 -10
- data/docs/embedded.md +0 -503
- data/lib/puma/plugin/railwatch.rb +0 -215
- data/lib/railwatch/dashboard_assets.rb +0 -45
- data/lib/railwatch/embedded.rb +0 -50
- data/lib/railwatch/export/client.rb +0 -124
- data/lib/railwatch/export/lease.rb +0 -56
- data/lib/railwatch/export/outbox.rb +0 -361
- data/lib/railwatch/export/policy.rb +0 -79
- data/lib/railwatch/export/sender.rb +0 -130
- data/lib/railwatch/ingest_request_body_limit.rb +0 -10
- data/lib/railwatch/maintenance.rb +0 -194
- data/lib/railwatch/patches/migration_busy_timeout.rb +0 -73
- data/lib/railwatch/transport/local.rb +0 -78
- data/lib/railwatch/transport/socket.rb +0 -183
- data/lib/railwatch/writer.rb +0 -370
- data/public/railwatch/assets/CommitMono-Bold-D6h61ieg.woff2 +0 -0
- data/public/railwatch/assets/CommitMono-Regular-zr8w7Obm.woff2 +0 -0
- data/public/railwatch/assets/Roboto-Black-auA4GeOK.woff2 +0 -0
- data/public/railwatch/assets/Roboto-Bold-CJLnO8j1.woff2 +0 -0
- data/public/railwatch/assets/Roboto-Medium-Cm2bwKpj.woff2 +0 -0
- data/public/railwatch/assets/Roboto-Regular-Chaq1-PV.woff2 +0 -0
- data/public/railwatch/assets/app-layout-by7G3sa1.js +0 -41
- data/public/railwatch/assets/application-Cc5v_Vk9.css +0 -1
- data/public/railwatch/assets/arrow-up-DCZ7emux.js +0 -1
- data/public/railwatch/assets/braces-CykT5_5u.js +0 -1
- data/public/railwatch/assets/card-C-PPSA0g.js +0 -1
- data/public/railwatch/assets/chart-Cov79YC5.js +0 -39
- data/public/railwatch/assets/chart-hover-DdK4E-2h.js +0 -1
- data/public/railwatch/assets/chart-panel-BPaYRXJB.js +0 -1
- data/public/railwatch/assets/code-BYjC4YZ_.js +0 -1
- data/public/railwatch/assets/copy-id-LPfoj9cC.js +0 -1
- data/public/railwatch/assets/cursor-load-more-DSdhgpGl.js +0 -1
- data/public/railwatch/assets/data-table-CQ_YRWQs.js +0 -1
- data/public/railwatch/assets/env-layout-CdMWbUkm.js +0 -1
- data/public/railwatch/assets/execution-path-7MYt-Qbn.js +0 -1
- data/public/railwatch/assets/filter-bar-CrzpQ_vD.js +0 -1
- data/public/railwatch/assets/flamegraph-BIFEdTAg.js +0 -2
- data/public/railwatch/assets/format-B9SDkrWj.js +0 -1
- data/public/railwatch/assets/frames-C5t-x4f0.js +0 -1
- data/public/railwatch/assets/index--NQ7us-L.js +0 -1
- data/public/railwatch/assets/index-2kgYe_99.js +0 -1
- data/public/railwatch/assets/index-4YEPHxtO.js +0 -1
- data/public/railwatch/assets/index-AOCC1GDJ.js +0 -1
- data/public/railwatch/assets/index-BEo-u0eO.js +0 -1
- data/public/railwatch/assets/index-BJ1V3w2N.js +0 -1
- data/public/railwatch/assets/index-BPoHsBKb.js +0 -1
- data/public/railwatch/assets/index-BZzyGUM3.js +0 -1
- data/public/railwatch/assets/index-BgVq7yKL.js +0 -1
- data/public/railwatch/assets/index-BqFGEZvW.js +0 -1
- data/public/railwatch/assets/index-ByuHJ1CH.js +0 -1
- data/public/railwatch/assets/index-C2j50AL2.js +0 -1
- data/public/railwatch/assets/index-CLEdTkvW.js +0 -1
- data/public/railwatch/assets/index-CWgq8qBu.js +0 -1
- data/public/railwatch/assets/index-CXQ7_oVj.js +0 -2
- data/public/railwatch/assets/index-CmyYrjS2.js +0 -1
- data/public/railwatch/assets/index-Cn9_HjDo.js +0 -1
- data/public/railwatch/assets/index-CsMAm9UK.js +0 -1
- data/public/railwatch/assets/index-D0Kk_oGY.js +0 -1
- data/public/railwatch/assets/index-DC9ZptJh.js +0 -1
- data/public/railwatch/assets/index-DNzg7jmV.js +0 -1
- data/public/railwatch/assets/index-DQ8uJO5f.js +0 -1
- data/public/railwatch/assets/index-Deq14BxB.js +0 -1
- data/public/railwatch/assets/index-IaV3Blzp.js +0 -1
- data/public/railwatch/assets/index-TYNWcJQs.js +0 -1
- data/public/railwatch/assets/index-WonH2fcZ.js +0 -1
- data/public/railwatch/assets/index-WxWUaH_d.js +0 -1
- data/public/railwatch/assets/index-mUNrl4ky.js +0 -1
- data/public/railwatch/assets/inertia-DfNRARuA.js +0 -97
- data/public/railwatch/assets/json-viewer-DMsF5glp.js +0 -1
- data/public/railwatch/assets/klass-CmHYk5rz.js +0 -1
- data/public/railwatch/assets/origin-identity-1R3bqa3R.js +0 -1
- data/public/railwatch/assets/percentile-picker-73z5PAps.js +0 -1
- data/public/railwatch/assets/relative-time-Dsc_cCdX.js +0 -1
- data/public/railwatch/assets/release-health-BV5OaOWd.js +0 -1
- data/public/railwatch/assets/route-CWh58C7K.js +0 -1
- data/public/railwatch/assets/segmented-DB4zh-Am.js +0 -1
- data/public/railwatch/assets/select-DBKjFlOX.js +0 -1
- data/public/railwatch/assets/series-chart-BaiDG795.js +0 -1
- data/public/railwatch/assets/show-B4ICCFGh.js +0 -1
- data/public/railwatch/assets/show-B5lOU9Gv.js +0 -1
- data/public/railwatch/assets/show-BYPB74t4.js +0 -2
- data/public/railwatch/assets/show-C7A0m2M8.js +0 -1
- data/public/railwatch/assets/show-DT5X1zqE.js +0 -1
- data/public/railwatch/assets/show-D_NBdXsn.js +0 -2
- data/public/railwatch/assets/show-DgPCnRg8.js +0 -1
- data/public/railwatch/assets/show-Yl69HF0x.js +0 -1
- data/public/railwatch/assets/show-_QxuUQCm.js +0 -3
- data/public/railwatch/assets/show-g__jnN36.js +0 -1
- data/public/railwatch/assets/show-l4ztfjl9.js +0 -1
- data/public/railwatch/assets/sort-header-BcG8ulEH.js +0 -1
- data/public/railwatch/assets/source-link-B2183i2-.js +0 -1
- data/public/railwatch/assets/sparkline-cell-B47ipscB.js +0 -1
- data/public/railwatch/assets/stat-DT2hhsqS.js +0 -1
- data/public/railwatch/assets/status-badge-CmbRKlY7.js +0 -1
- data/public/railwatch/assets/tenant-path-C4rNGauE.js +0 -1
- data/public/railwatch/assets/use-clipboard-oTNeP4ax.js +0 -1
- data/public/railwatch/assets/use-live-CLZZj245.js +0 -1
- data/public/railwatch/icon.png +0 -0
- data/public/railwatch/icon.svg +0 -5
- data/public/railwatch/manifest.json +0 -1245
- data/public/railwatch/rails-vite.json +0 -1
- /data/{app/frontend/lib → lib/generators/railwatch/install/templates}/railwatch.ts +0 -0
|
@@ -9,17 +9,15 @@ module Railwatch
|
|
|
9
9
|
source_root File.expand_path("templates", __dir__)
|
|
10
10
|
|
|
11
11
|
desc "Creates config/initializers/railwatch.rb, a Kamal post-deploy hook, the browser client, and wires the test helpers. " \
|
|
12
|
-
"
|
|
13
|
-
"
|
|
12
|
+
"Telemetry goes to Railwatch Cloud, or to a self-hosted Railwatch server with --url. " \
|
|
13
|
+
"To run the server inside this app instead, add the railwatch-server gem."
|
|
14
14
|
|
|
15
|
-
class_option :cloud, type: :boolean, default: false,
|
|
16
|
-
desc: "Send telemetry to Railwatch Cloud instead of keeping it in this app. Implied by --prompt-token, --token-stdin, --url and --kamal-secrets."
|
|
17
15
|
class_option :prompt_token, type: :boolean, default: false,
|
|
18
16
|
desc: "Prompt for the ingest token without echoing it."
|
|
19
17
|
class_option :token_stdin, type: :boolean, default: false,
|
|
20
18
|
desc: "Read the ingest token from one line on standard input."
|
|
21
19
|
class_option :url, type: :string,
|
|
22
|
-
desc: "Ingest URL, for a self-hosted Railwatch
|
|
20
|
+
desc: "Ingest URL, for a self-hosted Railwatch server. Defaults to https://railwatch.rebulk.com."
|
|
23
21
|
class_option :kamal_secrets, type: :boolean, default: false,
|
|
24
22
|
desc: "Wire RAILWATCH_TOKEN through .kamal/secrets and config/deploy.yml's `env: secret:` list."
|
|
25
23
|
class_option :doctor, type: :boolean, default: true,
|
|
@@ -59,59 +57,7 @@ module Railwatch
|
|
|
59
57
|
append_to_file "Gemfile", "#{contents.end_with?("\n") ? "" : "\n"}\n" \
|
|
60
58
|
"# #{Railwatch::JsonCompat::ISSUE}: Rails cannot decode with json 3 yet. Remove when it can.\n" \
|
|
61
59
|
"#{Railwatch::JsonCompat::PIN}\n"
|
|
62
|
-
|
|
63
|
-
say "#{Railwatch::JsonCompat.advice} Added the pin to your Gemfile; run `bundle install`, then " \
|
|
64
|
-
"`bin/rails db:prepare`.", :yellow
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
# Embedded mode stores telemetry in SQLite files whatever the app's own
|
|
68
|
-
# database is, so an app on PostgreSQL or MySQL needs the adapter gem
|
|
69
|
-
# added before those files can be created.
|
|
70
|
-
def ensure_sqlite3_gem
|
|
71
|
-
return unless local?
|
|
72
|
-
return if Gem.loaded_specs.key?("sqlite3")
|
|
73
|
-
return say("Embedded mode needs the sqlite3 gem for its two databases; add `gem \"sqlite3\"` and re-run.", :yellow) unless File.exist?("Gemfile")
|
|
74
|
-
|
|
75
|
-
contents = File.read("Gemfile")
|
|
76
|
-
unless contents.match?(/^\s*gem ["']sqlite3["']/)
|
|
77
|
-
append_to_file "Gemfile", %(#{contents.end_with?("\n") ? "" : "\n"}\n# Railwatch (embedded) keeps its telemetry in two SQLite files of its own.\ngem "sqlite3"\n)
|
|
78
|
-
end
|
|
79
|
-
@needs_bundle = true
|
|
80
|
-
say "Added `gem \"sqlite3\"` to the Gemfile: Railwatch's two databases are SQLite files whatever this app's " \
|
|
81
|
-
"own database is. Run `bundle install`, then `bin/rails db:prepare` to create them.", :yellow
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
# Two databases of its own, never the app's primary: `railwatch` for
|
|
86
|
-
# what people author (issues, comments, saved views, thresholds) and
|
|
87
|
-
# `railwatch_telemetry` for what the app reports, which is written
|
|
88
|
-
# continuously and pruned. Their migrations live in the gem; the
|
|
89
|
-
# database entries point migrations_paths at them, so db:prepare
|
|
90
|
-
# creates the tables now and migrates them after every gem update.
|
|
91
|
-
# Nothing is copied into the app.
|
|
92
|
-
def configure_local_databases
|
|
93
|
-
return unless local?
|
|
94
|
-
|
|
95
|
-
return say("No config/database.yml found; add railwatch and railwatch_telemetry databases yourself (docs/embedded.md).", :yellow) unless File.exist?("config/database.yml")
|
|
96
|
-
|
|
97
|
-
contents = File.read("config/database.yml")
|
|
98
|
-
updated = self.class.database_yml_with_railwatch(contents)
|
|
99
|
-
return say_status(:identical, "config/database.yml", :blue) if updated == contents
|
|
100
|
-
|
|
101
|
-
create_file "config/database.yml", updated, force: true
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
# The writer process: one per Puma master, forked by the gem's Puma
|
|
105
|
-
# plugin, so batches are mapped and written outside the web workers.
|
|
106
|
-
def configure_local_writer
|
|
107
|
-
return unless local?
|
|
108
|
-
return say("No config/puma.rb found; add `plugin :railwatch` to your Puma config yourself (docs/embedded.md).", :yellow) unless File.exist?("config/puma.rb")
|
|
109
|
-
|
|
110
|
-
contents = File.read("config/puma.rb")
|
|
111
|
-
updated = self.class.puma_rb_with_railwatch(contents)
|
|
112
|
-
return say_status(:identical, "config/puma.rb", :blue) if updated == contents
|
|
113
|
-
|
|
114
|
-
create_file "config/puma.rb", updated, force: true
|
|
60
|
+
say "#{Railwatch::JsonCompat.advice} Added the pin to your Gemfile; run `bundle install`.", :yellow
|
|
115
61
|
end
|
|
116
62
|
|
|
117
63
|
def create_kamal_hook
|
|
@@ -120,10 +66,9 @@ module Railwatch
|
|
|
120
66
|
chmod ".kamal/hooks/post-deploy", 0o755
|
|
121
67
|
end
|
|
122
68
|
|
|
123
|
-
# The one browser client
|
|
124
|
-
# is
|
|
125
|
-
#
|
|
126
|
-
# sees exactly what the gem tests.
|
|
69
|
+
# The one browser client (Railwatch.browser_client_path). Copied rather
|
|
70
|
+
# than templated: there is nothing to interpolate, and a copy means the
|
|
71
|
+
# host sees exactly what the gem tests.
|
|
127
72
|
def create_browser_client
|
|
128
73
|
return unless File.directory?("app/frontend")
|
|
129
74
|
copy_file Railwatch.browser_client_path.to_s, "app/frontend/lib/railwatch.ts"
|
|
@@ -167,8 +112,6 @@ module Railwatch
|
|
|
167
112
|
# A token lands in .env only when Git confirms the file is ignored.
|
|
168
113
|
# URLs are not secret and can still be written to a tracked dotenv file.
|
|
169
114
|
def write_env
|
|
170
|
-
return if local?
|
|
171
|
-
|
|
172
115
|
token = resolved_token
|
|
173
116
|
vars = { TOKEN_VAR => token, URL_VAR => options[:url] }.compact
|
|
174
117
|
return if vars.empty?
|
|
@@ -215,58 +158,7 @@ module Railwatch
|
|
|
215
158
|
create_file "config/deploy.yml", updated, force: true
|
|
216
159
|
end
|
|
217
160
|
|
|
218
|
-
# The two databases exist and are migrated when the generator returns:
|
|
219
|
-
# config/database.yml was just rewritten, so this re-reads it and runs
|
|
220
|
-
# the same prepare a deploy runs, for both databases only. The host's
|
|
221
|
-
# own databases are not touched, and a schema file is never written.
|
|
222
|
-
def prepare_local_databases
|
|
223
|
-
return unless local?
|
|
224
|
-
return unless File.exist?("config/database.yml")
|
|
225
|
-
return if @needs_bundle
|
|
226
|
-
return unless defined?(Rails) && Rails.respond_to?(:application) && Rails.application
|
|
227
|
-
|
|
228
|
-
say "\nbin/rails db:prepare (railwatch, railwatch_telemetry)", :green
|
|
229
|
-
require "active_record"
|
|
230
|
-
ActiveRecord::Base.configurations = Rails.application.config.database_configuration
|
|
231
|
-
%w[railwatch railwatch_telemetry].each do |name|
|
|
232
|
-
db_config = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, name: name)
|
|
233
|
-
next say(" #{name}: not in config/database.yml for #{Rails.env}", :yellow) unless db_config
|
|
234
|
-
|
|
235
|
-
ActiveRecord::Tasks::DatabaseTasks.with_temporary_pool_for_each(env: Rails.env, name: name) do
|
|
236
|
-
ActiveRecord::Tasks::DatabaseTasks.migrate
|
|
237
|
-
end
|
|
238
|
-
say_status :prepared, "#{name} (#{db_config.database})", :green
|
|
239
|
-
end
|
|
240
|
-
@prepared = true
|
|
241
|
-
rescue StandardError => e
|
|
242
|
-
say "Could not prepare the Railwatch databases here (#{e.class}: #{e.message}). Run `bin/rails db:prepare` yourself.", :yellow
|
|
243
|
-
end
|
|
244
|
-
|
|
245
161
|
def show_next_steps
|
|
246
|
-
if local?
|
|
247
|
-
say <<~STEPS, :green
|
|
248
|
-
|
|
249
|
-
Next steps
|
|
250
|
-
1. Restart the app and open /railwatch. In development it is open.
|
|
251
|
-
2. Before production, give it a password (it is closed there until
|
|
252
|
-
you do): RAILS_ENV=production bin/rails railwatch:authentication:configure
|
|
253
|
-
Using your own admin auth instead? See docs/embedded.md,
|
|
254
|
-
Authentication (base_controller_class or a routes constraint).
|
|
255
|
-
3. #{@prepared ? "Nothing else to run. Both databases were created just now and" : "Create the two databases: bin/rails db:prepare\n Then"}
|
|
256
|
-
`bin/rails db:prepare` (which a deploy already runs) migrates
|
|
257
|
-
them after every gem update. With `plugin :railwatch` in
|
|
258
|
-
config/puma.rb Puma forks one Railwatch writer process that
|
|
259
|
-
writes every batch and runs the maintenance clock, so no web
|
|
260
|
-
process ever holds the telemetry database. No job worker.
|
|
261
|
-
4. Optional: mirror to Railwatch Cloud for alerts delivered even
|
|
262
|
-
when this app is down, MCP for your AI assistant, and every
|
|
263
|
-
app in one place. Get a token (bin/rails railwatch:token), set
|
|
264
|
-
RAILWATCH_TOKEN, then c.export_enabled = true in the
|
|
265
|
-
initializer (docs/embedded.md).
|
|
266
|
-
STEPS
|
|
267
|
-
return
|
|
268
|
-
end
|
|
269
|
-
|
|
270
162
|
say <<~STEPS, :green
|
|
271
163
|
|
|
272
164
|
Next steps
|
|
@@ -281,6 +173,11 @@ module Railwatch
|
|
|
281
173
|
2. Verify the install: bin/rails railwatch:doctor
|
|
282
174
|
3. Connect your AI assistant: bin/rails railwatch:mcp
|
|
283
175
|
4. Gate performance in CI: see docs/testing.md.
|
|
176
|
+
|
|
177
|
+
Want the dashboard inside this app instead, with no token? Add the
|
|
178
|
+
railwatch-server gem (gem "railwatch-server", then
|
|
179
|
+
`plugin :railwatch_server` in config/puma.rb):
|
|
180
|
+
https://github.com/Rebulk/railwatch-server
|
|
284
181
|
STEPS
|
|
285
182
|
end
|
|
286
183
|
|
|
@@ -290,10 +187,6 @@ module Railwatch
|
|
|
290
187
|
# note below says so rather than letting a ✗ look like a broken install.
|
|
291
188
|
def run_doctor
|
|
292
189
|
return unless options[:doctor]
|
|
293
|
-
# This process read its configuration before the initializer was
|
|
294
|
-
# written, so in local mode the doctor would report an http transport
|
|
295
|
-
# with no token. The databases it would check were prepared above.
|
|
296
|
-
return if local?
|
|
297
190
|
return unless defined?(Rails) && Rails.respond_to?(:application) && Rails.application
|
|
298
191
|
|
|
299
192
|
say "\nbin/rails railwatch:doctor", :green
|
|
@@ -327,117 +220,6 @@ module Railwatch
|
|
|
327
220
|
insert_lines(lines, block_end(lines, secret_start), " - #{name}\n")
|
|
328
221
|
end
|
|
329
222
|
|
|
330
|
-
# Spelled out rather than `<<: *default`, on purpose. The host's default
|
|
331
|
-
# block carries ITS adapter: on a PostgreSQL or MySQL app inheriting it
|
|
332
|
-
# would ask that server for a database called
|
|
333
|
-
# "storage/production_railwatch.sqlite3". These two are always SQLite
|
|
334
|
-
# files the app owns, whatever the app's own database is, so they name
|
|
335
|
-
# their adapter themselves -- which also means the file needs no
|
|
336
|
-
# `&default` anchor to exist at all.
|
|
337
|
-
RAILWATCH_DATABASES = <<~YAML
|
|
338
|
-
railwatch:
|
|
339
|
-
adapter: sqlite3
|
|
340
|
-
database: storage/%<env>s_railwatch.sqlite3
|
|
341
|
-
pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %%>
|
|
342
|
-
timeout: 5000
|
|
343
|
-
migrations_paths: <%%= Railwatch.migrations_path(:railwatch) %%>
|
|
344
|
-
schema_dump: false
|
|
345
|
-
railwatch_telemetry:
|
|
346
|
-
adapter: sqlite3
|
|
347
|
-
database: storage/%<env>s_railwatch_telemetry.sqlite3
|
|
348
|
-
pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %%>
|
|
349
|
-
timeout: 5000
|
|
350
|
-
migrations_paths: <%%= Railwatch.migrations_path(:railwatch_telemetry) %%>
|
|
351
|
-
schema_dump: false
|
|
352
|
-
pragmas:
|
|
353
|
-
journal_mode: wal
|
|
354
|
-
synchronous: normal
|
|
355
|
-
mmap_size: 134217728
|
|
356
|
-
cache_size: -65536
|
|
357
|
-
temp_store: memory
|
|
358
|
-
YAML
|
|
359
|
-
|
|
360
|
-
# Adds the railwatch and railwatch_telemetry databases to every
|
|
361
|
-
# environment in config/database.yml. A flat environment
|
|
362
|
-
# (`development:` straight to `<<: *default`) becomes a `primary:`
|
|
363
|
-
# entry first, since named databases need the nested form. Text
|
|
364
|
-
# insertion rather than a YAML round trip, for the same reason as
|
|
365
|
-
# deploy_yml_with_secret: the comments are most of the file.
|
|
366
|
-
def self.database_yml_with_railwatch(contents)
|
|
367
|
-
lines = contents.lines
|
|
368
|
-
# Rails 8.1's non-Docker template leaves every production `database:`
|
|
369
|
-
# commented out ("path/to/persistent/storage/..."), so db:prepare
|
|
370
|
-
# cannot run in production at all until the host fills them in. The
|
|
371
|
-
# storage/ paths are what the Docker template writes and what
|
|
372
|
-
# config/deploy.yml mounts; use them.
|
|
373
|
-
lines = lines.map do |line|
|
|
374
|
-
line.sub(%r{\A(\s+)# database: path/to/persistent/storage/(\S+)$}, '\1database: storage/\2')
|
|
375
|
-
end
|
|
376
|
-
environments_in(lines).each do |env|
|
|
377
|
-
start = lines.index { |line| line.match?(/\A#{env}:\s*(#.*)?$/) }
|
|
378
|
-
next unless start
|
|
379
|
-
|
|
380
|
-
stop = block_end(lines, start)
|
|
381
|
-
block = lines[(start + 1)...stop]
|
|
382
|
-
next if block.any? { |line| line.match?(/\A\s+railwatch_telemetry:\s*$/) }
|
|
383
|
-
|
|
384
|
-
nested = block.any? { |line| line.match?(/\A [a-z_]+:\s*$/) }
|
|
385
|
-
unless nested
|
|
386
|
-
lines[(start + 1)...stop] = block.map { |line| line.strip.empty? ? line : " #{line}" }
|
|
387
|
-
lines.insert(start + 1, " primary:\n")
|
|
388
|
-
stop += 1
|
|
389
|
-
end
|
|
390
|
-
entries = format(RAILWATCH_DATABASES, env: database_prefix(env)).lines.map { |line| " #{line}" }
|
|
391
|
-
lines = insert_lines(lines, stop, entries.join).lines
|
|
392
|
-
end
|
|
393
|
-
lines.join
|
|
394
|
-
end
|
|
395
|
-
|
|
396
|
-
# What the database filenames are built from. For test that has to carry
|
|
397
|
-
# TEST_ENV_NUMBER, the way Rails' own test database naming does: runners
|
|
398
|
-
# like parallel_tests give each worker its own number and expect a
|
|
399
|
-
# database per worker. Without it every worker in a run opens the same
|
|
400
|
-
# two SQLite files and they fight over them -- observed as
|
|
401
|
-
# PendingMigrationError and "disk I/O error" from configure_connection,
|
|
402
|
-
# on an app running four workers. An app with no such runner sets no
|
|
403
|
-
# TEST_ENV_NUMBER, so the suffix is empty and the name is unchanged.
|
|
404
|
-
def self.database_prefix(env)
|
|
405
|
-
return env unless env == "test"
|
|
406
|
-
|
|
407
|
-
%(test<%= ENV["TEST_ENV_NUMBER"] %>)
|
|
408
|
-
end
|
|
409
|
-
|
|
410
|
-
# Unconditional: `bundle exec puma` evaluates this file before it loads
|
|
411
|
-
# the Rack app, so `if defined?(Railwatch)` would be false there and the
|
|
412
|
-
# writer would never start. The plugin itself is inert when Railwatch is
|
|
413
|
-
# absent, off, or not in embedded mode.
|
|
414
|
-
PUMA_PLUGIN_LINES = <<~RUBY
|
|
415
|
-
|
|
416
|
-
# Railwatch (embedded): fork one writer process from Puma so telemetry
|
|
417
|
-
# is mapped and written outside the processes serving requests.
|
|
418
|
-
plugin :railwatch
|
|
419
|
-
RUBY
|
|
420
|
-
|
|
421
|
-
# Appends the plugin line once. Puma's config is plain Ruby evaluated top
|
|
422
|
-
# to bottom, so the end of the file is always a valid place for it.
|
|
423
|
-
def self.puma_rb_with_railwatch(contents)
|
|
424
|
-
return contents if contents.include?("plugin :railwatch")
|
|
425
|
-
|
|
426
|
-
"#{contents.sub(/\n*\z/, "\n")}#{PUMA_PLUGIN_LINES}"
|
|
427
|
-
end
|
|
428
|
-
|
|
429
|
-
# Every environment the file defines, not a fixed three: an app with a
|
|
430
|
-
# `staging` (or `review`, or `qa`) environment needs the databases
|
|
431
|
-
# there too, and hardcoding names silently left it without them. A
|
|
432
|
-
# top-level key with a block under it, minus YAML's own anchors.
|
|
433
|
-
NON_ENVIRONMENT_KEYS = %w[default shared].freeze
|
|
434
|
-
|
|
435
|
-
def self.environments_in(lines)
|
|
436
|
-
lines.filter_map { |line| line[/\A([a-z_][a-z0-9_]*):\s*(?:&\S+\s*)?(?:#.*)?$/, 1] }
|
|
437
|
-
.reject { |name| NON_ENVIRONMENT_KEYS.include?(name) }
|
|
438
|
-
.uniq
|
|
439
|
-
end
|
|
440
|
-
|
|
441
223
|
# Index of the first line after the block opened at `start`: the next
|
|
442
224
|
# line indented no more deeply than it, ignoring blanks and comments,
|
|
443
225
|
# then backed up over any trailing blank lines so an insertion lands
|
|
@@ -461,16 +243,6 @@ module Railwatch
|
|
|
461
243
|
|
|
462
244
|
private
|
|
463
245
|
|
|
464
|
-
# Embedded unless the invocation asks for the cloud: --cloud itself, or
|
|
465
|
-
# an option that only means something there. A RAILWATCH_TOKEN already
|
|
466
|
-
# in the environment is not asking -- it is picked up when one of these
|
|
467
|
-
# is given, never used to choose the mode.
|
|
468
|
-
CLOUD_OPTIONS = %i[cloud prompt_token token_stdin url kamal_secrets].freeze
|
|
469
|
-
|
|
470
|
-
def local?
|
|
471
|
-
CLOUD_OPTIONS.none? { |name| options[name] }
|
|
472
|
-
end
|
|
473
|
-
|
|
474
246
|
def resolved_token
|
|
475
247
|
@resolved_token ||= begin
|
|
476
248
|
value = if options[:prompt_token]
|
|
@@ -3,47 +3,8 @@
|
|
|
3
3
|
# Railwatch: first-class monitoring for Rails. Every option here can also be
|
|
4
4
|
# set by the RAILWATCH_* env var named in the comment.
|
|
5
5
|
Railwatch.configure do |c|
|
|
6
|
-
<% if local? -%>
|
|
7
|
-
# Telemetry stays in this app's own railwatch_telemetry database and the
|
|
8
|
-
# dashboard is served at /railwatch. No token, no cloud. Who can open the
|
|
9
|
-
# dashboard is the Access block below.
|
|
10
|
-
c.transport = :local # RAILWATCH_TRANSPORT
|
|
11
|
-
c.ignored_request_paths += ["/railwatch", %r{\A/railwatch/}]
|
|
12
|
-
# c.issue_prefix = "APP" # RAILWATCH_ISSUE_PREFIX; issue keys like APP-12
|
|
13
|
-
# c.repository_url = "https://github.com/you/app" # RAILWATCH_REPOSITORY_URL; source links from stack traces
|
|
14
|
-
# c.retention_days = 7 # RAILWATCH_RETENTION_DAYS; PruneTelemetryJob keeps this much
|
|
15
|
-
# Access. The dashboard shows every query, log line and exception this app
|
|
16
|
-
# records, so pick one of these. Out of the box it is HTTP Basic: open in
|
|
17
|
-
# development, closed everywhere else until credentials exist:
|
|
18
|
-
# RAILS_ENV=production bin/rails railwatch:authentication:configure
|
|
19
|
-
# or RAILWATCH_HTTP_BASIC_AUTH_USER / _PASSWORD.
|
|
20
|
-
#
|
|
21
|
-
# Using your own admin auth instead? Turn Basic off and say which, so the
|
|
22
|
-
# gem knows the mount is covered and live updates can follow your rule:
|
|
23
|
-
# c.http_basic_auth_enabled = false # RAILWATCH_HTTP_BASIC_AUTH_ENABLED
|
|
24
|
-
# c.base_controller_class = "AdminController" # every dashboard page inherits it
|
|
25
|
-
# c.dashboard_user = ->(request) { ... } # or decide per request, below
|
|
26
|
-
#
|
|
27
|
-
# Genuinely public (a private network, a VPN, a constraint the gem cannot
|
|
28
|
-
# see)? Say so on purpose. Nothing else makes the dashboard public, and
|
|
29
|
-
# without one of the above the app logs a warning at every boot:
|
|
30
|
-
# c.dashboard_open = false # RAILWATCH_DASHBOARD_OPEN
|
|
31
|
-
# Who the dashboard names on comments and saved views. Returning nil also
|
|
32
|
-
# refuses the request, so this doubles as your authorisation rule:
|
|
33
|
-
# c.dashboard_user = ->(request) { user = Current.user; user&.admin? && { id: user.id, name: user.name, email: user.email } }
|
|
34
|
-
|
|
35
|
-
# The browser beacon (Inertia visit timings and JavaScript errors) is a
|
|
36
|
-
# public endpoint, like every browser telemetry endpoint: it is bounded by
|
|
37
|
-
# an origin allowlist, a per-client rate limit and a ceiling for the whole
|
|
38
|
-
# endpoint, not by a credential.
|
|
39
|
-
# c.beacon_enabled = false # RAILWATCH_BEACON
|
|
40
|
-
# c.beacon_allowed_origins = ["https://app.example.com"] # beyond this app's own
|
|
41
|
-
# c.beacon_rate_limit = 120 # per client IP per minute
|
|
42
|
-
# c.beacon_global_rate_limit = 6_000 # for the endpoint, per minute
|
|
43
|
-
<% else -%>
|
|
44
6
|
# c.token = ENV["RAILWATCH_TOKEN"] # RAILWATCH_TOKEN (required)
|
|
45
|
-
|
|
46
|
-
# c.ingest_url = "https://railwatch.rebulk.com" # RAILWATCH_INGEST_URL
|
|
7
|
+
# c.ingest_url = "https://railwatch.rebulk.com" # RAILWATCH_INGEST_URL; your own server when self-hosting
|
|
47
8
|
# c.deploy = "release-name" # RAILWATCH_DEPLOY; platform/Git auto-detected
|
|
48
9
|
# c.detect_deploy = false # RAILWATCH_DETECT_DEPLOY; default true
|
|
49
10
|
|
|
@@ -75,6 +36,15 @@ Railwatch.configure do |c|
|
|
|
75
36
|
# c.redact_params += %w[ssn] # merged with Rails.application.config.filter_parameters
|
|
76
37
|
# c.ignored_request_paths += ["/internal/health"] # /up and /railwatch/beacon are ignored by default
|
|
77
38
|
|
|
39
|
+
# The browser beacon (Inertia visit timings and JavaScript errors) is a
|
|
40
|
+
# public endpoint, like every browser telemetry endpoint: it is bounded by
|
|
41
|
+
# an origin allowlist, a per-client rate limit and a ceiling for the whole
|
|
42
|
+
# endpoint, not by a credential.
|
|
43
|
+
# c.beacon_enabled = false # RAILWATCH_BEACON
|
|
44
|
+
# c.beacon_allowed_origins = ["https://app.example.com"] # beyond this app's own
|
|
45
|
+
# c.beacon_rate_limit = 120 # per client IP per minute
|
|
46
|
+
# c.beacon_global_rate_limit = 6_000 # for the endpoint, per minute
|
|
47
|
+
|
|
78
48
|
# How the current user is described. Default reads Current.user then Warden.
|
|
79
49
|
# c.user { |user| { id: user.id, name: user.name, email: user.email } }
|
|
80
50
|
end
|
|
@@ -9,13 +9,6 @@
|
|
|
9
9
|
# ingest host instead of shelling into a container. Never fails a deploy.
|
|
10
10
|
set -e
|
|
11
11
|
|
|
12
|
-
# Embedded install (transport = :local): the marker is a row in the app's own
|
|
13
|
-
# railwatch database, so record it inside the deployed container and stop.
|
|
14
|
-
if [ "${RAILWATCH_TRANSPORT:-}" = "local" ]; then
|
|
15
|
-
bin/kamal app exec --primary --reuse "bin/rails railwatch:deploy[$KAMAL_VERSION]" || true
|
|
16
|
-
exit 0
|
|
17
|
-
fi
|
|
18
|
-
|
|
19
12
|
[ -z "$RAILWATCH_TOKEN" ] && exit 0
|
|
20
13
|
|
|
21
14
|
# Optional: .kamal/hooks/post-deploy --sourcemaps, or set
|
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
require "yaml"
|
|
4
4
|
|
|
5
5
|
module Railwatch
|
|
6
|
-
# `bin/rails railwatch:authentication:configure`: stores the
|
|
7
|
-
#
|
|
8
|
-
# credentials, the same way Mission Control Jobs'
|
|
9
|
-
# does. Asks for a user, generates the password,
|
|
10
|
-
# entry to the credentials file the environment
|
|
6
|
+
# `bin/rails railwatch:authentication:configure`: stores the HTTP Basic
|
|
7
|
+
# credentials railwatch-server's dashboard proxy checks in the current
|
|
8
|
+
# environment's Rails credentials, the same way Mission Control Jobs'
|
|
9
|
+
# authentication:configure does. Asks for a user, generates the password,
|
|
10
|
+
# appends a `railwatch:` entry to the credentials file the environment
|
|
11
|
+
# reads.
|
|
11
12
|
module Authentication
|
|
12
13
|
module_function
|
|
13
14
|
|
data/lib/railwatch/buffer.rb
CHANGED
|
@@ -20,6 +20,10 @@ module Railwatch
|
|
|
20
20
|
@bytes = 0
|
|
21
21
|
@dropped = 0
|
|
22
22
|
@dropped_bytes = 0
|
|
23
|
+
# Every record this queue has ever had to drop for room. Unlike
|
|
24
|
+
# @dropped it is never drained, so a caller can difference it across a
|
|
25
|
+
# span of time.
|
|
26
|
+
@overflowed = 0
|
|
23
27
|
@mutex = Mutex.new
|
|
24
28
|
end
|
|
25
29
|
|
|
@@ -58,6 +62,10 @@ module Railwatch
|
|
|
58
62
|
@mutex.synchronize { @bytes }
|
|
59
63
|
end
|
|
60
64
|
|
|
65
|
+
def overflowed
|
|
66
|
+
@mutex.synchronize { @overflowed }
|
|
67
|
+
end
|
|
68
|
+
|
|
61
69
|
def stats
|
|
62
70
|
@mutex.synchronize { [ @records.size, @dropped, @bytes, @dropped_bytes ] }
|
|
63
71
|
end
|
|
@@ -110,6 +118,7 @@ module Railwatch
|
|
|
110
118
|
def drop(bytes)
|
|
111
119
|
@dropped += 1
|
|
112
120
|
@dropped_bytes += bytes
|
|
121
|
+
@overflowed += 1
|
|
113
122
|
end
|
|
114
123
|
|
|
115
124
|
def drop_oldest
|