railwatch 0.5.1 → 0.6.1
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 +45 -25
- data/CHANGELOG.md +147 -0
- data/README.md +50 -31
- data/app/controllers/railwatch/dashboard_controller.rb +1 -0
- data/app/jobs/railwatch/rollup_job.rb +3 -1
- data/app/models/railwatch/application_record.rb +2 -2
- data/app/models/railwatch/ingest/rollup_absorber.rb +1 -1
- data/app/models/railwatch/telemetry_record.rb +2 -2
- data/docs/ai-and-mcp.md +9 -3
- data/docs/configuration.md +97 -40
- data/docs/embedded.md +96 -43
- data/docs/faq.md +28 -15
- data/docs/getting-started.md +121 -42
- data/docs/records.md +13 -10
- data/docs/replacing-nightwatch.md +16 -14
- data/docs/replacing-sentry.md +19 -10
- data/docs/security.md +24 -3
- data/docs/self-hosting.md +9 -1
- data/docs/testing.md +14 -4
- data/docs/troubleshooting.md +72 -29
- data/lib/generators/railwatch/install/install_generator.rb +32 -16
- data/lib/generators/railwatch/install/templates/initializer.rb.tt +6 -6
- data/lib/puma/plugin/railwatch.rb +48 -3
- data/lib/railwatch/configuration.rb +16 -1
- data/lib/railwatch/engine.rb +14 -3
- data/lib/railwatch/reporter.rb +52 -16
- data/lib/railwatch/transport/http.rb +4 -9
- data/lib/railwatch/version.rb +1 -1
- data/lib/railwatch.rb +23 -1
- data/lib/tasks/railwatch_tasks.rake +11 -4
- data/llms.txt +21 -14
- data/public/railwatch/assets/{app-layout-DDyQa72H.js → app-layout-Zc0v-hYh.js} +1 -1
- data/public/railwatch/assets/{app-wordmark-o9CODKP0.js → app-wordmark-BA_60AVb.js} +1 -1
- data/public/railwatch/assets/{appearance-BwuCXabr.js → appearance-CcfP9tZ7.js} +1 -1
- data/public/railwatch/assets/application-BrN3Sz94.css +1 -0
- data/public/railwatch/assets/{arrow-up-C6PxDiY3.js → arrow-up-CLQ-7heQ.js} +1 -1
- data/public/railwatch/assets/{auth-layout-BRt8MGFD.js → auth-layout-C05gEBIQ.js} +1 -1
- data/public/railwatch/assets/{badge-CAxXV8za.js → badge-DRae8XwK.js} +1 -1
- data/public/railwatch/assets/{braces-DgomTCNf.js → braces-rSYydpLY.js} +1 -1
- data/public/railwatch/assets/{card-cAtqCxWl.js → card-DPjFKfen.js} +1 -1
- data/public/railwatch/assets/{chart-BBeBkkNa.js → chart-DWh7l8yM.js} +1 -1
- data/public/railwatch/assets/{chart-hover-B1M9jc0y.js → chart-hover-CfoZUY4J.js} +1 -1
- data/public/railwatch/assets/{chart-panel-DUQTz_C8.js → chart-panel-CC49WTQL.js} +1 -1
- data/public/railwatch/assets/{checkbox-CmhMHWZO.js → checkbox-DPkLUiwM.js} +1 -1
- data/public/railwatch/assets/{code-DESvxyTj.js → code-CLmYS6FU.js} +1 -1
- data/public/railwatch/assets/{copy-block-BkSU5832.js → copy-block-CGcXxp8J.js} +1 -1
- data/public/railwatch/assets/{copy-id-D03GhN9F.js → copy-id-vBYHQwxg.js} +1 -1
- data/public/railwatch/assets/{cursor-load-more-CRyuMeQb.js → cursor-load-more-Ddwxe5dZ.js} +1 -1
- data/public/railwatch/assets/{data-table-BIlt7Rtm.js → data-table-CzKTEE-O.js} +1 -1
- data/public/railwatch/assets/{edit-O0NSBWxo.js → edit-B1kmWkzd.js} +1 -1
- data/public/railwatch/assets/{edit-DJ0D0wHN.js → edit-D9cx4pbG.js} +1 -1
- data/public/railwatch/assets/{edit-Bb6MKoe4.js → edit-yv6j9p-V.js} +1 -1
- data/public/railwatch/assets/{empty-state-C38il627.js → empty-state-CW4wclK_.js} +1 -1
- data/public/railwatch/assets/{env-layout-REF7OM4q.js → env-layout-Kz7wks1x.js} +1 -1
- data/public/railwatch/assets/{execution-path-FYLq1TwC.js → execution-path-FAuIzOXB.js} +1 -1
- data/public/railwatch/assets/{filter-bar-CYog9Alp.js → filter-bar-CJDjWFib.js} +1 -1
- data/public/railwatch/assets/{flamegraph-DSs69foN.js → flamegraph-EaGkP2NT.js} +1 -1
- data/public/railwatch/assets/{frames-BUi2J5Mk.js → frames-zZuIaNH9.js} +1 -1
- data/public/railwatch/assets/{google-sign-in-button-BQiIKFdd.js → google-sign-in-button-2_zgbgVy.js} +1 -1
- data/public/railwatch/assets/{index-ZOGOB8SA.js → index-B49SWz7K.js} +1 -1
- data/public/railwatch/assets/{index-DqTFTP8p.js → index-BHlY4wKe.js} +1 -1
- data/public/railwatch/assets/{index-DrcKVG2f.js → index-BZpPtyFY.js} +1 -1
- data/public/railwatch/assets/{index-Dh4IRLFI.js → index-BeVK6jCL.js} +1 -1
- data/public/railwatch/assets/{index-umIAl-pL.js → index-BfbSo01U.js} +1 -1
- data/public/railwatch/assets/{index-BoUBioBP.js → index-BfgncAv6.js} +1 -1
- data/public/railwatch/assets/{index-C3A_9imx.js → index-BhNszK1k.js} +1 -1
- data/public/railwatch/assets/{index-tpz-OGUP.js → index-Bu01uWvw.js} +1 -1
- data/public/railwatch/assets/{index-CGs4m_fa.js → index-C1s_hK3p.js} +1 -1
- data/public/railwatch/assets/{index-ZSZg9rtq.js → index-C8Cggnbw.js} +1 -1
- data/public/railwatch/assets/{index-so4lRrRq.js → index-CBip6V4z.js} +1 -1
- data/public/railwatch/assets/{index-DvjY3dPD.js → index-CMJGss5R.js} +1 -1
- data/public/railwatch/assets/{index-DtHmuB9Q.js → index-CRo3yK20.js} +1 -1
- data/public/railwatch/assets/{index-r0tSIplE.js → index-CWB_p2J8.js} +1 -1
- data/public/railwatch/assets/{index-CICUIFHL.js → index-CWHpndGc.js} +1 -1
- data/public/railwatch/assets/{index-CFFpnzIS.js → index-Ca_S4Sc3.js} +1 -1
- data/public/railwatch/assets/{index-DSvlZVWG.js → index-CanPDDOa.js} +1 -1
- data/public/railwatch/assets/{index-CrZ3vHDL.js → index-Cie90Yat.js} +1 -1
- data/public/railwatch/assets/{index-FhUaPPab.js → index-CiepQ_pR.js} +1 -1
- data/public/railwatch/assets/{index-C_upSl_k.js → index-Cm1uCIGN.js} +1 -1
- data/public/railwatch/assets/{index-sTYvcbkh.js → index-CzitnnSC.js} +1 -1
- data/public/railwatch/assets/{index-C7OtLq_3.js → index-DEUfClv3.js} +1 -1
- data/public/railwatch/assets/index-DJKwo-mI.js +1 -0
- data/public/railwatch/assets/{index-DDI_Zx5V.js → index-DK6y0YHp.js} +1 -1
- data/public/railwatch/assets/{index-C-PmdhXA.js → index-DMNPpLH9.js} +1 -1
- data/public/railwatch/assets/{index-BaR1U9An.js → index-DZO1mSfX.js} +1 -1
- data/public/railwatch/assets/{index-CsoN51vW.js → index-Db5wj3M5.js} +1 -1
- data/public/railwatch/assets/{index-BiiyMcA0.js → index-Dcy5WktB.js} +1 -1
- data/public/railwatch/assets/{index-8-hnAhOD.js → index-DvG0-7Lx.js} +1 -1
- data/public/railwatch/assets/{index-QpTtwFwu.js → index-Dvj1wuka.js} +1 -1
- data/public/railwatch/assets/{index-DW2CBbxU.js → index-KyZX46qX.js} +1 -1
- data/public/railwatch/assets/{index-BeOh2t_S.js → index-Ze-KP-sl.js} +1 -1
- data/public/railwatch/assets/{index-CiPo4Gob.js → index-mhRbWLBM.js} +1 -1
- data/public/railwatch/assets/{index-CpkI015n.js → index-x099JL5f.js} +1 -1
- data/public/railwatch/assets/{index-JdCVBrw8.js → index-x28zb_nf.js} +1 -1
- data/public/railwatch/assets/{inertia-DLew8ZNx.js → inertia-Cuyz2ZHO.js} +2 -2
- data/public/railwatch/assets/{input-error-cvM6_Jht.js → input-error-hog6gGxg.js} +1 -1
- data/public/railwatch/assets/{json-viewer-D922McGi.js → json-viewer-DH2W9HXf.js} +1 -1
- data/public/railwatch/assets/{klass-CrwICqN8.js → klass-DHbDelLk.js} +1 -1
- data/public/railwatch/assets/{label-GWl7I6sf.js → label-DdCBgiUn.js} +1 -1
- data/public/railwatch/assets/{layout-0ZAnD3zl.js → layout-Cueyl7c5.js} +1 -1
- data/public/railwatch/assets/{live-dot-D1n_BreY.js → live-dot-BZgYTYdt.js} +1 -1
- data/public/railwatch/assets/{nav-DPxr1NNC.js → nav-BSSGObDZ.js} +1 -1
- data/public/railwatch/assets/{new-D-ZzUK9a.js → new-B8FSb8Bl.js} +1 -1
- data/public/railwatch/assets/{new-DEVkYv-z.js → new-C39_v2Ll.js} +1 -1
- data/public/railwatch/assets/{new-Cdl6pqST.js → new-DVOPwaC5.js} +1 -1
- data/public/railwatch/assets/{new-DHAHDrN7.js → new-Dd40nvJR.js} +1 -1
- data/public/railwatch/assets/{new-Dz4lZf1L.js → new-SxnYe1SE.js} +1 -1
- data/public/railwatch/assets/{new-GMrRFurX.js → new-eP5vKD3Y.js} +1 -1
- data/public/railwatch/assets/onboarding-CUpZl5KB.js +1 -0
- data/public/railwatch/assets/{origin-identity-Bk9yHWZ1.js → origin-identity-BYt2uiuo.js} +1 -1
- data/public/railwatch/assets/{percentile-picker-DfSx9yJO.js → percentile-picker-CeQgllxD.js} +1 -1
- data/public/railwatch/assets/{relative-time-CjIjb8Lg.js → relative-time-D5UbF4oO.js} +1 -1
- data/public/railwatch/assets/{release-health-4b3tivEf.js → release-health-uP-GF3_V.js} +1 -1
- data/public/railwatch/assets/{route-C_5BUtHK.js → route-DQAY8JEr.js} +1 -1
- data/public/railwatch/assets/{segmented-BgbT3wZa.js → segmented-BeIe4uqk.js} +1 -1
- data/public/railwatch/assets/{select-DmunxCKE.js → select-2R16457Z.js} +1 -1
- data/public/railwatch/assets/{separator-BXzEdZ_8.js → separator-D5I0UCB5.js} +1 -1
- data/public/railwatch/assets/series-chart-gnrzhmM6.js +1 -0
- data/public/railwatch/assets/show-2BkeNRUC.js +2 -0
- data/public/railwatch/assets/{show-mU38uGTg.js → show-B0X1hRQH.js} +1 -1
- data/public/railwatch/assets/{show-CAl7xcex.js → show-BKUV5l5q.js} +1 -1
- data/public/railwatch/assets/{show-DnR1Dnjd.js → show-BQJD_CsF.js} +1 -1
- data/public/railwatch/assets/{show-Dn-GwFZL.js → show-BhmD6Sxp.js} +1 -1
- data/public/railwatch/assets/{show-Y74rM0VT.js → show-C3KcnVo2.js} +1 -1
- data/public/railwatch/assets/{show-Dily73Xk.js → show-C95cHd06.js} +1 -1
- data/public/railwatch/assets/{show-DXs4deaC.js → show-CdB4uVQS.js} +1 -1
- data/public/railwatch/assets/{show-DI8IhNUH.js → show-CoCqcVIp.js} +1 -1
- data/public/railwatch/assets/{show-vQ4bndYD.js → show-D2LqjEsU.js} +1 -1
- data/public/railwatch/assets/{show-DcpTFiLi.js → show-DNpnKyTh.js} +1 -1
- data/public/railwatch/assets/{show-B2zLAW83.js → show-DOlTxbig.js} +1 -1
- data/public/railwatch/assets/{show-SvLOcPrx.js → show-DQCkttL8.js} +1 -1
- data/public/railwatch/assets/{show-BLpWUHWD.js → show-DcxesipC.js} +1 -1
- data/public/railwatch/assets/{show-DYskfl3-.js → show-IGJoc_X0.js} +1 -1
- data/public/railwatch/assets/{show-DSP9Cq_C.js → show-YsNpqbcI.js} +1 -1
- data/public/railwatch/assets/{show-DlRVS18-.js → show-qNV6H8SH.js} +1 -1
- data/public/railwatch/assets/{sort-header-Dcq9bzmo.js → sort-header-mEJUM3Av.js} +1 -1
- data/public/railwatch/assets/{sparkline-cell-BON3qQUB.js → sparkline-cell-Dwg7awwh.js} +1 -1
- data/public/railwatch/assets/{stat-DFEyFxkO.js → stat-ZtxGU8lE.js} +1 -1
- data/public/railwatch/assets/{status-badge-BaUKP7Yo.js → status-badge-CH5P-Xkj.js} +1 -1
- data/public/railwatch/assets/{tenant-path-DPZPc985.js → tenant-path-CQoP-BeF.js} +1 -1
- data/public/railwatch/assets/{text-link-BO77t9Xk.js → text-link-DHJ8BXx5.js} +1 -1
- data/public/railwatch/assets/{textarea-DTqrCiV0.js → textarea-BeQtQyl5.js} +1 -1
- data/public/railwatch/assets/{timeline-D5rJ0es2.js → timeline-CaKQVu48.js} +1 -1
- data/public/railwatch/assets/{transition-DMIrZVth.js → transition-ksDpqhKJ.js} +1 -1
- data/public/railwatch/assets/{use-clipboard-ByoUGQqA.js → use-clipboard-DNefo-ky.js} +1 -1
- data/public/railwatch/assets/{use-live-D7xKz2ma.js → use-live-DMTuhKfB.js} +1 -1
- data/public/railwatch/manifest.json +1286 -1286
- metadata +116 -116
- data/public/railwatch/assets/application-B7h1MIhi.css +0 -1
- data/public/railwatch/assets/index-CFRLPs4J.js +0 -1
- data/public/railwatch/assets/onboarding-D1vwaHYT.js +0 -1
- data/public/railwatch/assets/series-chart-Xf49v9cv.js +0 -1
- data/public/railwatch/assets/show-SHwZjXb7.js +0 -2
data/docs/getting-started.md
CHANGED
|
@@ -1,27 +1,100 @@
|
|
|
1
1
|
# Getting started
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
Two commands on a Rails 8 app, then a dashboard at `/railwatch`.
|
|
4
|
+
Everything below is the gem's own generator and rake tasks; nothing else
|
|
5
|
+
has to be wired by hand.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
no token and no cloud? That is `bin/rails generate railwatch:install
|
|
9
|
-
--local`; see [Embedded mode](embedded.md). The rest of this page is
|
|
10
|
-
the cloud install.
|
|
11
|
-
|
|
12
|
-
## 1. Add the gem
|
|
7
|
+
## Install
|
|
13
8
|
|
|
14
9
|
```sh
|
|
15
10
|
bundle add railwatch
|
|
11
|
+
bin/rails generate railwatch:install
|
|
16
12
|
```
|
|
17
13
|
|
|
18
14
|
The gem, its Ruby namespace, and its require path share one name:
|
|
19
15
|
`railwatch`, `Railwatch::*`, `require "railwatch"`.
|
|
20
16
|
|
|
21
|
-
|
|
17
|
+
With no flags the install is [embedded](embedded.md): telemetry stays in
|
|
18
|
+
two SQLite databases the app owns, Puma forks one writer process for
|
|
19
|
+
them (`plugin :railwatch` in `config/puma.rb`), and the dashboard is
|
|
20
|
+
served from the app. No token, no cloud, no job worker. The generator
|
|
21
|
+
creates and migrates both databases before it returns. On a PostgreSQL
|
|
22
|
+
or MySQL app without the `sqlite3` gem it adds the gem instead; run
|
|
23
|
+
`bundle install` and `bin/rails db:prepare` to finish.
|
|
24
|
+
|
|
25
|
+
Restart the app and open `/railwatch`. In development it is open; every
|
|
26
|
+
other environment answers 401 until you give it a password:
|
|
22
27
|
|
|
23
28
|
```sh
|
|
24
|
-
bin/rails
|
|
29
|
+
RAILS_ENV=production bin/rails railwatch:authentication:configure
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
[Embedded mode](embedded.md#authentication) covers using your own
|
|
33
|
+
admin authentication instead.
|
|
34
|
+
|
|
35
|
+
### What the generator writes
|
|
36
|
+
|
|
37
|
+
In every mode:
|
|
38
|
+
|
|
39
|
+
- `config/initializers/railwatch.rb`, with every option commented out at
|
|
40
|
+
its default (embedded: `c.transport = :local`).
|
|
41
|
+
- `mount Railwatch::Engine, at: "/railwatch"` in `config/routes.rb`: the
|
|
42
|
+
embedded dashboard and the beacon endpoint the browser client posts to.
|
|
43
|
+
- `.kamal/hooks/post-deploy` — only if `config/deploy.yml` already
|
|
44
|
+
exists.
|
|
45
|
+
- `app/frontend/lib/railwatch.ts` plus the `startRailwatch()` call in your
|
|
46
|
+
Inertia entrypoint — only if `app/frontend/` exists. If it can't find
|
|
47
|
+
an entrypoint it prints the two lines to add.
|
|
48
|
+
- `require "railwatch/rspec"` in `spec/rails_helper.rb`, or
|
|
49
|
+
`require "railwatch/minitest"` in `test/test_helper.rb`.
|
|
50
|
+
|
|
51
|
+
Embedded adds the two databases to `config/database.yml` and the Puma
|
|
52
|
+
plugin line; see [Embedded mode](embedded.md#your-applications-own-database).
|
|
53
|
+
|
|
54
|
+
### Check the wiring
|
|
55
|
+
|
|
56
|
+
```sh
|
|
57
|
+
bin/rails railwatch:doctor
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
With the app running, the embedded checklist starts like this:
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
✓ transport: local (telemetry stays in this app; dashboard at the engine mount)
|
|
64
|
+
✓ railwatch database: storage/development_railwatch.sqlite3
|
|
65
|
+
✓ railwatch_telemetry database: storage/development_railwatch_telemetry.sqlite3
|
|
66
|
+
✓ railwatch_telemetry migrations: up to date
|
|
67
|
+
✓ railwatch migrations: up to date
|
|
68
|
+
✓ telemetry disk: auto_vacuum=incremental; the nightly prune returns freed pages
|
|
69
|
+
✓ writer process: listening at /app/tmp/sockets/railwatch-writer.sock
|
|
70
|
+
✓ last write: 12 seconds ago
|
|
71
|
+
✓ dashboard access: open in development. Production stays closed until you run `RAILS_ENV=production bin/rails railwatch:authentication:configure`
|
|
72
|
+
✓ request middleware: Railwatch::Middleware::Request at position 0
|
|
73
|
+
✓ engine mounted: POST /railwatch/beacon -> railwatch/beacon#create
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
With the app stopped, `writer process` and `maintenance` are `✗`, and say
|
|
77
|
+
so. Every line and what to do about a `✗` is in
|
|
78
|
+
[`troubleshooting.md`](troubleshooting.md).
|
|
79
|
+
|
|
80
|
+
### Adding Railwatch Cloud
|
|
81
|
+
|
|
82
|
+
Railwatch Cloud delivers alerts to Slack, email, webhooks or Linear
|
|
83
|
+
(embedded mode only records them), serves the [MCP server](ai-and-mcp.md)
|
|
84
|
+
to your AI assistant, and puts many apps and servers in one place. An
|
|
85
|
+
embedded install can mirror every record to it and keep `/railwatch`:
|
|
86
|
+
set a token ([below](#where-the-token-comes-from)) and
|
|
87
|
+
`c.export_enabled = true`. See
|
|
88
|
+
[Embedded mode](embedded.md#three-ways-to-run-it).
|
|
89
|
+
|
|
90
|
+
## Railwatch Cloud instead
|
|
91
|
+
|
|
92
|
+
To send telemetry only to Railwatch Cloud, with no local databases, run
|
|
93
|
+
the same generator with `--cloud`:
|
|
94
|
+
|
|
95
|
+
```sh
|
|
96
|
+
bundle add railwatch
|
|
97
|
+
bin/rails generate railwatch:install --cloud
|
|
25
98
|
```
|
|
26
99
|
|
|
27
100
|
With the token already in hand, let the generator read it without placing the
|
|
@@ -36,6 +109,10 @@ bin/rails generate railwatch:install \
|
|
|
36
109
|
|
|
37
110
|
- `--prompt-token` reads without echo. `--token-stdin` is available for a
|
|
38
111
|
secret-manager pipe; an already exported `RAILWATCH_TOKEN` is also detected.
|
|
112
|
+
- Any of `--prompt-token`, `--token-stdin`, `--url=` and `--kamal-secrets`
|
|
113
|
+
means the cloud, so none of them needs `--cloud` as well. An exported
|
|
114
|
+
`RAILWATCH_TOKEN` on its own does not: without one of these flags the
|
|
115
|
+
install is embedded.
|
|
39
116
|
- A token is written to `.env` only when Git confirms that `.env` is ignored.
|
|
40
117
|
A tracked or non-ignored dotenv file is refused; use Rails credentials, a
|
|
41
118
|
deployment secret manager, or add `.env` to `.gitignore` first. Token values
|
|
@@ -47,24 +124,12 @@ bin/rails generate railwatch:install \
|
|
|
47
124
|
`config/deploy.yml`, which is the pair of edits Kamal needs to pass a
|
|
48
125
|
secret through to the containers.
|
|
49
126
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
its default.
|
|
54
|
-
- `mount Railwatch::Engine, at: "/railwatch"` in `config/routes.rb` (the
|
|
55
|
-
beacon endpoint the browser client posts to).
|
|
56
|
-
- `.kamal/hooks/post-deploy` — only if `config/deploy.yml` already
|
|
57
|
-
exists.
|
|
58
|
-
- `app/frontend/lib/railwatch.ts` plus the `startRailwatch()` call in your
|
|
59
|
-
Inertia entrypoint — only if `app/frontend/` exists. If it can't find
|
|
60
|
-
an entrypoint it prints the two lines to add.
|
|
61
|
-
- `require "railwatch/rspec"` in `spec/rails_helper.rb`, or
|
|
62
|
-
`require "railwatch/minitest"` in `test/test_helper.rb`.
|
|
63
|
-
|
|
64
|
-
It finishes by running `railwatch:doctor` for you, so the install either
|
|
127
|
+
It writes the same files as the embedded install
|
|
128
|
+
([above](#what-the-generator-writes)) minus the databases and the Puma
|
|
129
|
+
plugin, and finishes by running `railwatch:doctor`, so the install either
|
|
65
130
|
ends in a clean checklist or tells you what is still missing.
|
|
66
131
|
|
|
67
|
-
|
|
132
|
+
### Where the token comes from
|
|
68
133
|
|
|
69
134
|
In Railwatch Cloud, create an application, then an environment inside it
|
|
70
135
|
(`production`, `staging`, one token each). The token is shown once, on
|
|
@@ -76,12 +141,12 @@ update `RAILWATCH_TOKEN`.
|
|
|
76
141
|
bin/rails railwatch:token # prints the URL to create/copy a token
|
|
77
142
|
```
|
|
78
143
|
|
|
79
|
-
A token looks like `rw_` followed by 40 characters.
|
|
80
|
-
|
|
81
|
-
token.present?`, so an app with no token installs no
|
|
82
|
-
ships nothing.
|
|
144
|
+
A token looks like `rw_` followed by 40 characters. A cloud install is
|
|
145
|
+
inert without one: with `transport = :http`, `Railwatch.enabled?` is
|
|
146
|
+
`config.enabled && token.present?`, so an app with no token installs no
|
|
147
|
+
subscribers and ships nothing.
|
|
83
148
|
|
|
84
|
-
|
|
149
|
+
### Check the cloud wiring
|
|
85
150
|
|
|
86
151
|
```sh
|
|
87
152
|
bin/rails railwatch:doctor
|
|
@@ -89,12 +154,14 @@ bin/rails railwatch:doctor
|
|
|
89
154
|
|
|
90
155
|
```
|
|
91
156
|
✓ token: rw_9Qv... (43 chars)
|
|
157
|
+
✓ token storage: no tracked plaintext Railwatch token found
|
|
92
158
|
✓ ingest url: https://railwatch.rebulk.com
|
|
159
|
+
✓ ingest transport security: HTTPS with certificate verification
|
|
93
160
|
✓ ingest reachable: GET https://railwatch.rebulk.com/ingest/ping
|
|
94
161
|
✓ request middleware: Railwatch::Middleware::Request at position 0
|
|
95
162
|
✓ engine mounted: POST /railwatch/beacon -> railwatch/beacon#create
|
|
96
163
|
✓ deploy: 8f31c0a42e91 (from GIT_REV)
|
|
97
|
-
✓ sample rates: requests=1.0 jobs=1.0 commands=1.0 scheduled_tasks=1.0 exceptions=1.0
|
|
164
|
+
✓ sample rates: requests=1.0 jobs=1.0 commands=1.0 scheduled_tasks=1.0 channels=1.0 exceptions=1.0
|
|
98
165
|
✓ ignored record types: none
|
|
99
166
|
```
|
|
100
167
|
|
|
@@ -102,13 +169,12 @@ It exits non-zero on three lines: `token` (missing), `token storage`
|
|
|
102
169
|
(a plaintext token in a tracked file), and `ingest reachable`. The rest
|
|
103
170
|
of the checklist is informational. Run it after the token is in place:
|
|
104
171
|
`ingest reachable` sends the token, so a missing or wrong token makes
|
|
105
|
-
the platform answer 401 and that line is `✗` as well.
|
|
106
|
-
what to do about a `✗` is in [`troubleshooting.md`](troubleshooting.md).
|
|
172
|
+
the platform answer 401 and that line is `✗` as well.
|
|
107
173
|
|
|
108
174
|
`bin/rails railwatch:status` is the one-line version: it pings
|
|
109
175
|
`{ingest_url}/ingest/ping` and prints the ingest URL, deploy, and server.
|
|
110
176
|
|
|
111
|
-
##
|
|
177
|
+
## Make one request
|
|
112
178
|
|
|
113
179
|
```sh
|
|
114
180
|
bin/rails server
|
|
@@ -117,9 +183,10 @@ curl http://localhost:3000/
|
|
|
117
183
|
|
|
118
184
|
Records are batched in-process and flushed every `flush_interval`
|
|
119
185
|
(2 seconds by default) or every 500 records, whichever comes first, so
|
|
120
|
-
the request shows up on the
|
|
121
|
-
|
|
122
|
-
and log lines already
|
|
186
|
+
the request shows up on the **Requests** page (`/railwatch`, or the
|
|
187
|
+
environment in Railwatch Cloud) a couple of seconds after you make it —
|
|
188
|
+
with its queries, cache reads, view renders, and log lines already
|
|
189
|
+
attached to it.
|
|
123
190
|
|
|
124
191
|
## Three optional lines
|
|
125
192
|
|
|
@@ -187,8 +254,8 @@ newest issues, deploy markers), Requests (routes table and per-request
|
|
|
187
254
|
waterfall of every child record), Jobs, Scheduled tasks, Commands,
|
|
188
255
|
Exceptions, Queries (slow list and N+1 list with the app line that
|
|
189
256
|
issued them), Spans, Profiles, Transactions, View renders, Cache, Mail,
|
|
190
|
-
Notifications, Broadcasts, Outgoing requests,
|
|
191
|
-
Deprecations.
|
|
257
|
+
Notifications, Broadcasts, Outgoing requests, LLM (RubyLLM calls with
|
|
258
|
+
tokens, cost, cut-offs and tool calls), Storage, Logs, Deprecations.
|
|
192
259
|
|
|
193
260
|
**Monitoring** — Visits (Inertia page-visit timing and web vitals),
|
|
194
261
|
Users, Tenants, Deploys, Releases (crash-free session and user rates),
|
|
@@ -199,6 +266,15 @@ up, on the account.
|
|
|
199
266
|
|
|
200
267
|
## Deploying
|
|
201
268
|
|
|
269
|
+
An embedded install deploys like the rest of the app: the two databases
|
|
270
|
+
are files under `storage/`, which the Rails 8 Kamal template already
|
|
271
|
+
mounts as a volume, and the Rails 8 Docker entrypoint's
|
|
272
|
+
`bin/rails db:prepare` migrates them. Give the
|
|
273
|
+
dashboard a password first ([Install](#install)). For deploy markers
|
|
274
|
+
from the Kamal hook, set `RAILWATCH_TRANSPORT=local` in the deployer's
|
|
275
|
+
environment ([Embedded mode](embedded.md#deploys)). The token steps
|
|
276
|
+
below are for a cloud install or export.
|
|
277
|
+
|
|
202
278
|
### Kamal
|
|
203
279
|
|
|
204
280
|
Two edits, both of which `--kamal-secrets` makes for you:
|
|
@@ -276,9 +352,12 @@ one — the deploy is still recorded.
|
|
|
276
352
|
`sentry-rails`.
|
|
277
353
|
- [`replacing-nightwatch.md`](replacing-nightwatch.md) — for people
|
|
278
354
|
coming from Laravel.
|
|
355
|
+
- [`embedded.md`](embedded.md) — authentication, the writer process,
|
|
356
|
+
maintenance, disk, and export to the cloud.
|
|
279
357
|
- [`self-hosting.md`](self-hosting.md) — pointing the gem at your own
|
|
280
358
|
platform install.
|
|
281
359
|
- [`troubleshooting.md`](troubleshooting.md) — every `railwatch:doctor`
|
|
282
360
|
line and what a failure means.
|
|
283
361
|
- [`faq.md`](faq.md) — overhead, retention, PII, unreachable platform.
|
|
284
|
-
- [`ai-and-mcp.md`](ai-and-mcp.md) — asking an AI assistant what broke
|
|
362
|
+
- [`ai-and-mcp.md`](ai-and-mcp.md) — asking an AI assistant what broke
|
|
363
|
+
(Railwatch Cloud).
|
data/docs/records.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Record types
|
|
2
2
|
|
|
3
3
|
Every record Railwatch ships is a flat hash. See `lib/railwatch/record.rb`.
|
|
4
|
-
This lists all
|
|
4
|
+
This lists all 28, field by field, sourced from the subscriber or patch
|
|
5
5
|
that builds each one. Field names below are the hash keys as sent over
|
|
6
6
|
the wire: symbols in Ruby, strings in the gzip NDJSON payload.
|
|
7
7
|
|
|
@@ -207,7 +207,7 @@ vs `#execute`. `db:migrate` and other tasks in
|
|
|
207
207
|
| `name` | Task name, or `"runner"`. |
|
|
208
208
|
| `command` | Full invocation, e.g. `"rake db:seed[foo]"` or `"rails runner SomeScript.run"`. |
|
|
209
209
|
| `exit_code` | 0 on success, `SystemExit`'s status, or 1 on an unhandled exception, clamped to 0-255. |
|
|
210
|
-
| `interactive` | `true` on a `bin/rails runner` an engineer typed or piped (`-`, inline code, or a `.rb` file under `config.interactive_runner_paths`); absent otherwise. Such a run ships this record — with its `exit_code` and `exception_preview` — but its exception is not reported. A deployed script (`rails runner script/nightly.rb`), a rake task, and a job are never interactive. See [Console and runner sessions](replacing-sentry.md#console-and-runner-sessions). |
|
|
210
|
+
| `interactive` | `true` on a `bin/rails runner` an engineer typed or piped (`-`, inline code, or a `.rb` file under `config.interactive_runner_paths`); absent otherwise. Such a run ships this record — with its `exit_code` and `exception_preview` — but its exception is not reported. A deployed script (`rails runner script/nightly.rb`), a rake task, and a job are never interactive. See [Console and runner sessions](replacing-sentry.md#12-console-and-runner-sessions). |
|
|
211
211
|
|
|
212
212
|
### `channel_action`
|
|
213
213
|
|
|
@@ -526,14 +526,17 @@ adds the `workflow_*` fields. `cost_nanos` is null rather than zero
|
|
|
526
526
|
whenever RubyLLM reported no cost or the model registry could not price
|
|
527
527
|
it — an unpriced call is not a free one.
|
|
528
528
|
|
|
529
|
-
**
|
|
530
|
-
`tool_concurrency
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
529
|
+
**Tool calls run on their own threads are not recorded.** RubyLLM's opt-in
|
|
530
|
+
`tool_concurrency: :threads` runs each tool in a fresh thread.
|
|
531
|
+
`Railwatch::Current` is backed by `ActiveSupport::IsolatedExecutionState`,
|
|
532
|
+
which a new thread does not inherit, so the `tool_call.ruby_llm` event
|
|
533
|
+
fires with no execution to attach to and the record is dropped rather
|
|
534
|
+
than misattributed. This affects every Railwatch subscriber in an
|
|
535
|
+
app-spawned thread, not just this one. `:fibers` depends on Rails'
|
|
536
|
+
isolation level: under the default, `:thread`, fibers share their
|
|
537
|
+
thread's state and the tool calls are recorded; with
|
|
538
|
+
`config.active_support.isolation_level = :fiber` they are dropped the same
|
|
539
|
+
way. Tool concurrency is off by default; with it off, tool calls are
|
|
537
540
|
recorded normally. The model calls themselves are unaffected either way,
|
|
538
541
|
so cost is always complete.
|
|
539
542
|
|
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
Railwatch is the same product shape for Rails: one package instruments the
|
|
4
4
|
framework end to end, records are grouped under the execution that
|
|
5
|
-
produced them, and a
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
produced them, and a dashboard turns them into routes, jobs, queries,
|
|
6
|
+
issues, and alerts. Unlike Nightwatch, that dashboard runs inside your
|
|
7
|
+
app by default ([embedded mode](embedded.md)); Railwatch Cloud is the
|
|
8
|
+
hosted option. If you know Nightwatch, you already know how to read
|
|
9
|
+
Railwatch — this page maps the vocabulary and points out the three
|
|
10
|
+
places the Rails answer is genuinely different.
|
|
9
11
|
|
|
10
12
|
Railwatch is an independent product and is not affiliated with Laravel or
|
|
11
13
|
Laravel Nightwatch.
|
|
@@ -18,10 +20,10 @@ separate daemon POSTs them.
|
|
|
18
20
|
|
|
19
21
|
Puma and Solid Queue workers *are* long-lived, so Railwatch skips that
|
|
20
22
|
tier. A single reporter thread per process holds a bounded buffer
|
|
21
|
-
(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
(16 MiB and 10,000 records by default, oldest dropped and counted) and
|
|
24
|
+
flushes batches every 2 seconds or every 500 records: to the embedded
|
|
25
|
+
writer process Puma forks, or as gzip-NDJSON POSTs to Railwatch Cloud.
|
|
26
|
+
There is no daemon to install, supervise, or forget to restart.
|
|
25
27
|
|
|
26
28
|
The thread is re-armed after `fork`, so clustered Puma workers and
|
|
27
29
|
forked Solid Queue workers each get their own with no `on_worker_boot`
|
|
@@ -50,7 +52,7 @@ Nightwatch's types, and what they're called here:
|
|
|
50
52
|
| `queued-job` | `enqueued_job` | The enqueue side, in the execution that enqueued it. |
|
|
51
53
|
| `log` | `log` | Lines at or above `log_level`, plus Rails 8.1 structured `Rails.event` events. |
|
|
52
54
|
| `user` | `user` | Resolved once per user per process-hour, not once per request. |
|
|
53
|
-
| deployment (`nightwatch:deploy`) | `Deploy` on the
|
|
55
|
+
| deployment (`nightwatch:deploy`) | `Deploy` on the dashboard | Recorded by `railwatch:deploy` or the Kamal `post-deploy` hook, with up to 50 commits when Git history is at hand, so the dashboard can diff what shipped. |
|
|
54
56
|
| request `stages` | parent `stages` | Same idea, Rails boundaries: `middleware_before`, `action`, `render`, `middleware_after`, `body`. Laravel's `bootstrap` has no equivalent in a warm process — Railwatch reports boot time once per process as a `process` record instead. |
|
|
55
57
|
|
|
56
58
|
And the types with no Nightwatch counterpart at all: `storage_op` (Active
|
|
@@ -59,7 +61,7 @@ Storage), `view_render`, `span` (your own timed blocks), `attachment`,
|
|
|
59
61
|
`session` (release health), `process`, `health` (Puma pool, Active Record
|
|
60
62
|
pool, Solid Queue backlog), and `profile` (sampled stack profiles).
|
|
61
63
|
|
|
62
|
-
Field-by-field detail for all
|
|
64
|
+
Field-by-field detail for all 28 is in [`records.md`](records.md).
|
|
63
65
|
|
|
64
66
|
## What "execution" means
|
|
65
67
|
|
|
@@ -76,7 +78,7 @@ it is propagated across services as a W3C `traceparent` on outgoing HTTP
|
|
|
76
78
|
— an inbound `traceparent` is adopted, so a trace spans services rather
|
|
77
79
|
than stopping at the process boundary.
|
|
78
80
|
|
|
79
|
-
Practically: on the
|
|
81
|
+
Practically: on the dashboard you never look at a query in isolation. You
|
|
80
82
|
open the request, and the query is in its waterfall with everything else
|
|
81
83
|
that execution did.
|
|
82
84
|
|
|
@@ -182,7 +184,7 @@ every setting still has an env var — `NIGHTWATCH_*` becomes `RAILWATCH_*`.
|
|
|
182
184
|
| `nightwatch:agent` | Nothing — the reporter thread lives in the app process. |
|
|
183
185
|
| `nightwatch:status` | `bin/rails railwatch:status` |
|
|
184
186
|
| `nightwatch:deploy {deploy} --ref --name --url` | `bin/rails railwatch:deploy[ref,name,url]`, or the generated `.kamal/hooks/post-deploy` |
|
|
185
|
-
| — | `bin/rails railwatch:doctor`, which checks the whole install and exits non-zero if the token or the ingest host is wrong |
|
|
187
|
+
| — | `bin/rails railwatch:doctor`, which checks the whole install and exits non-zero if the embedded databases, the token, or the ingest host is wrong |
|
|
186
188
|
|
|
187
189
|
## The three things worth knowing about Rails
|
|
188
190
|
|
|
@@ -211,6 +213,6 @@ See [`testing.md`](testing.md).
|
|
|
211
213
|
|
|
212
214
|
## Next
|
|
213
215
|
|
|
214
|
-
- [`getting-started.md`](getting-started.md) —
|
|
216
|
+
- [`getting-started.md`](getting-started.md) — the two-command install.
|
|
215
217
|
- [`configuration.md`](configuration.md) — every option and env var.
|
|
216
|
-
- [`records.md`](records.md) — all
|
|
218
|
+
- [`records.md`](records.md) — all 28 record types, field by field.
|
data/docs/replacing-sentry.md
CHANGED
|
@@ -8,7 +8,10 @@ depends on a conditional row.
|
|
|
8
8
|
|
|
9
9
|
Install Railwatch first ([`getting-started.md`](getting-started.md)); you
|
|
10
10
|
can run both for a day if you want to compare, since neither knows about
|
|
11
|
-
the other.
|
|
11
|
+
the other. The default install is embedded, with the dashboard at
|
|
12
|
+
`/railwatch` and no token. The token and alert steps below apply when
|
|
13
|
+
you send to Railwatch Cloud, either as a cloud install or by exporting
|
|
14
|
+
from embedded.
|
|
12
15
|
|
|
13
16
|
## Decide whether Railwatch covers your workload
|
|
14
17
|
|
|
@@ -30,7 +33,7 @@ Railwatch does not claim those broader capabilities.
|
|
|
30
33
|
| Ruby profiling | Conditional | Requires `vernier` or `stackprof`; there is no profiler bundled into the SDK. |
|
|
31
34
|
| Browser monitoring | Partial | The optional Inertia client reports visits, Web Vitals, browser errors, and breadcrumbs. Session Replay, native/mobile SDKs, and Sentry's full browser/source-map workflow are outside the currently released Rails-server replacement. |
|
|
32
35
|
| Runtime compatibility | Narrow today | The currently proved pair is Ruby 3.4 + Rails 8.1. A maintained compatibility matrix and any safe lowering of requirements are tracked by [#26](https://github.com/Rebulk/railwatch/issues/26). |
|
|
33
|
-
| Managed integrations and operations | Partial | Railwatch Cloud
|
|
36
|
+
| Managed integrations and operations | Partial | Railwatch Cloud delivers alerts to email, Slack, webhooks and Linear, plus self-hosting; embedded mode records alerts without delivering them. It does not promise Sentry's broader integration catalog. |
|
|
34
37
|
| SQL value privacy | Supported by default | Query records carry normalized SQL shapes without literal values, and Active Record binds are never sent. Raw SQL and query plans are separate opt-ins; either can contain values. |
|
|
35
38
|
|
|
36
39
|
For a Rails 8.1 application whose work enters through Rack and Active Job,
|
|
@@ -70,9 +73,11 @@ deploy config once the app boots without it.
|
|
|
70
73
|
`config/initializers/railwatch.rb` (written by the install generator) is
|
|
71
74
|
where every option from `Sentry.init` lands. The mapping:
|
|
72
75
|
|
|
73
|
-
- **`dsn:`**
|
|
74
|
-
|
|
75
|
-
|
|
76
|
+
- **`dsn:`** has no equivalent in embedded mode, which stores telemetry in
|
|
77
|
+
the app. For Railwatch Cloud it becomes `RAILWATCH_TOKEN`, one token per
|
|
78
|
+
environment, created in Railwatch Cloud. Self-hosting adds
|
|
79
|
+
`RAILWATCH_INGEST_URL`. On a cloud install the token is also the on/off
|
|
80
|
+
switch: with it blank, Railwatch installs nothing.
|
|
76
81
|
- **`environment:`** becomes `c.environment`, which defaults to
|
|
77
82
|
`Rails.env` — set it only to report under a different name.
|
|
78
83
|
- **`release:`** becomes `c.deploy`, which auto-detects the release from
|
|
@@ -111,7 +116,9 @@ where every option from `Sentry.init` lands. The mapping:
|
|
|
111
116
|
- **Rack `X-Request-Start` queue time** needs no setting: it is parsed
|
|
112
117
|
into `queue_time` on every `request` record.
|
|
113
118
|
|
|
114
|
-
A worked initializer, roughly what a `Sentry.init`
|
|
119
|
+
A worked initializer for a cloud install, roughly what a `Sentry.init`
|
|
120
|
+
block turns into (an embedded install has `c.transport = :local` instead
|
|
121
|
+
of the token):
|
|
115
122
|
|
|
116
123
|
```ruby
|
|
117
124
|
# config/initializers/railwatch.rb
|
|
@@ -415,9 +422,9 @@ startRailwatch({
|
|
|
415
422
|
|
|
416
423
|
| Sentry | Railwatch |
|
|
417
424
|
|---|---|
|
|
418
|
-
| `Sentry.init({ dsn })` | `startRailwatch()`. There is no DSN: the beacon posts to the app's own origin and the *server* decides whether to record it (`c.beacon_enabled`,
|
|
425
|
+
| `Sentry.init({ dsn })` | `startRailwatch()`. There is no DSN: the beacon posts to the app's own origin and the *server* decides whether to record it (`c.beacon_enabled`, and whether Railwatch is enabled). The gate you already have on whether `startRailwatch()` runs at all is the only gate. |
|
|
419
426
|
| `release` | Automatic. The record is stamped with `c.deploy`, the same release the server records carry, so a browser issue and a server issue from one deploy line up without a matching pair of settings to get wrong. |
|
|
420
|
-
| `environment` | Automatic — the ingest token identifies
|
|
427
|
+
| `environment` | Automatic — the embedded databases belong to one environment, and on Railwatch Cloud the ingest token identifies it. |
|
|
421
428
|
| `ignoreErrors` | `startRailwatch({ ignoreErrors })`. Strings match anywhere in the message; regexes are tested against it. Both `ResizeObserver` messages are ignored by default. |
|
|
422
429
|
| `denyUrls` | `startRailwatch({ denyUrls })`, matched against the top stack frame's URL. `/extensions\//i`, `/^chrome:\/\//i`, and `/^moz-extension:\/\//i` are denied by default, **and** any frame from an origin that isn't the app's own is dropped — extensions, injected widgets, tag managers. |
|
|
423
430
|
| `Sentry.setUser` | Server-side. The beacon is a same-origin POST carrying the session cookie, so the server resolves the user the same way it does for a request (`Railwatch.user`) when `Current.user` or Warden is set by middleware; an app that authenticates in a `before_action` gives Railwatch the same lookup with `c.beacon_user { \|request\| ... }`. Nothing the browser sends names the user, so it cannot be forged. |
|
|
@@ -561,8 +568,10 @@ hook matching `rails runner …` previews, or `sentry_runner_noise.rb` under
|
|
|
561
568
|
| Inertia visit timing | Real browser page-visit duration, prop byte size, partial reloads, SSR time, and Core Web Vitals, from a client the generator installs. |
|
|
562
569
|
| Spec matchers as a CI gate | `have_railwatch_queries`, `have_railwatch_n_plus_one`, `have_railwatch_outgoing_requests` fail the pull request that regresses a hot path. |
|
|
563
570
|
| Zero app-DB writes | The gem holds records in memory and ships them from a background thread; a bench gate asserts no `INSERT`/`UPDATE`/`DELETE` ever originates in `lib/railwatch`. This is why it is safe on single-writer SQLite. |
|
|
564
|
-
|
|
|
565
|
-
|
|
|
571
|
+
| A dashboard inside the app | The default install serves the whole dashboard at `/railwatch` from two SQLite files the app owns, with no hosted service at all ([`embedded.md`](embedded.md)). |
|
|
572
|
+
| One SQLite database per environment | Railwatch Cloud stores each monitored environment's telemetry in its own database file, which makes retention pruning, backup, and restore per-environment operations. |
|
|
573
|
+
| LLM calls | RubyLLM calls are recorded with tokens, cost, finish reason, tool calls and workflows, in the request or job that made them ([`records.md`](records.md#llm_call)). |
|
|
574
|
+
| An MCP server | With Railwatch Cloud, AI assistants can ask what broke after the last deploy, list slow routes, read an execution's timeline, and search logs ([`ai-and-mcp.md`](ai-and-mcp.md)). |
|
|
566
575
|
|
|
567
576
|
## See also
|
|
568
577
|
|
data/docs/security.md
CHANGED
|
@@ -3,8 +3,25 @@
|
|
|
3
3
|
This review covers the open-source gem that runs in a customer's Rails
|
|
4
4
|
application. Railwatch Cloud is a separate service and is outside this review.
|
|
5
5
|
|
|
6
|
+
## Embedded dashboard
|
|
7
|
+
|
|
8
|
+
In embedded mode (the installer's default) the gem also serves a dashboard at
|
|
9
|
+
the engine's mount, showing every query, log line and exception the app
|
|
10
|
+
recorded. HTTP Basic authentication is on by default. With no credentials
|
|
11
|
+
configured, every dashboard request is 401 in every environment except
|
|
12
|
+
development, where it is open; the app logs a warning at boot outside
|
|
13
|
+
development, and `railwatch:doctor` reports the gate. Credentials, once set,
|
|
14
|
+
apply in development too. An app that turns Basic off gates pages with its own
|
|
15
|
+
`base_controller_class` or a routes constraint; live updates over Action Cable
|
|
16
|
+
are refused unless Basic, a `dashboard_user` resolver, or `dashboard_open`
|
|
17
|
+
authorizes them. See [Embedded mode](embedded.md#authentication).
|
|
18
|
+
|
|
19
|
+
The writer socket that web workers hand batches to is created with a 0700
|
|
20
|
+
directory and a 0600 socket, so only the app's own user can connect.
|
|
21
|
+
|
|
6
22
|
## Transport
|
|
7
23
|
|
|
24
|
+
This applies to a cloud install and to export from an embedded install.
|
|
8
25
|
`Railwatch::Transport::Http` sends gzip NDJSON with `Net::HTTP`. HTTPS explicitly
|
|
9
26
|
sets OpenSSL's `VERIFY_PEER`; a spec pins that setting. The transport does not
|
|
10
27
|
implement redirect handling, so a redirect response is treated as a permanent
|
|
@@ -35,8 +52,9 @@ configuration and record specs:
|
|
|
35
52
|
filtered. `Locals.inspect_value` rescues an `inspect` implementation that
|
|
36
53
|
raises; this behavior is covered by an exception-record spec.
|
|
37
54
|
- `capture_exception_source` is true. Source lines surrounding in-application
|
|
38
|
-
backtrace frames are sent to Railwatch Cloud
|
|
39
|
-
|
|
55
|
+
backtrace frames are recorded, and sent to Railwatch Cloud when the install
|
|
56
|
+
reports or exports there. Disable it if source context is outside the
|
|
57
|
+
application's telemetry policy.
|
|
40
58
|
|
|
41
59
|
Log record messages are sent exactly as supplied to `Rails.logger`. Railwatch
|
|
42
60
|
does not attempt to parse and partially filter `key=value` text because doing
|
|
@@ -46,7 +64,8 @@ and `Railwatch.reject_logs` or `RAILWATCH_IGNORE_LOGS=true` can omit log records
|
|
|
46
64
|
|
|
47
65
|
## Browser beacon
|
|
48
66
|
|
|
49
|
-
`POST /railwatch/beacon` is the gem's only inbound
|
|
67
|
+
`POST /railwatch/beacon` is the gem's only inbound endpoint that is
|
|
68
|
+
unauthenticated by design. It
|
|
50
69
|
is rate-limited per client IP through the Rails cache (120 requests per minute
|
|
51
70
|
by default), limited to a 256 KiB body, and capped at 50 visits and 50 errors
|
|
52
71
|
per request. Nested error stacks, messages, breadcrumbs, context, visit partial
|
|
@@ -88,6 +107,8 @@ application/deployment secret and must not be committed.
|
|
|
88
107
|
application-specific credentials and personal data.
|
|
89
108
|
- Keep secrets out of exception messages, log text, source files, tenant ids,
|
|
90
109
|
user resolvers, and custom context.
|
|
110
|
+
- In embedded mode, set dashboard credentials (or your own gate) before
|
|
111
|
+
deploying; see [Embedded mode](embedded.md#authentication).
|
|
91
112
|
- Use HTTPS in production and keep TLS verification enabled.
|
|
92
113
|
- Put a request-body limit at the reverse proxy when the public beacon is
|
|
93
114
|
enabled, and choose a shared cache if rate limits must span processes.
|
data/docs/self-hosting.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Self-hosting
|
|
2
2
|
|
|
3
|
+
This page is for sending telemetry to a Railwatch Cloud you run
|
|
4
|
+
yourself. An [embedded](embedded.md) install keeps everything inside
|
|
5
|
+
your app and needs none of it.
|
|
6
|
+
|
|
3
7
|
Railwatch Cloud is a Rails app you can run yourself. The gem doesn't care
|
|
4
8
|
which install it talks to — point it at yours and everything works the
|
|
5
9
|
same.
|
|
@@ -17,12 +21,16 @@ end
|
|
|
17
21
|
`ingest_url` defaults to `https://railwatch.rebulk.com`, so this is the one
|
|
18
22
|
setting a self-hosted install always needs; everything the gem sends —
|
|
19
23
|
records, ping, deploys — hangs off that host. Pass `--url=` to the
|
|
20
|
-
installer to have it written for you:
|
|
24
|
+
installer to have it written for you (it implies `--cloud`):
|
|
21
25
|
|
|
22
26
|
```sh
|
|
23
27
|
bin/rails generate railwatch:install --url=https://telemetry.example.com
|
|
24
28
|
```
|
|
25
29
|
|
|
30
|
+
To keep an embedded install and mirror it to your platform, set
|
|
31
|
+
`RAILWATCH_INGEST_URL` and `RAILWATCH_TOKEN` and turn on
|
|
32
|
+
[export](embedded.md#three-ways-to-run-it) instead.
|
|
33
|
+
|
|
26
34
|
## Getting a token
|
|
27
35
|
|
|
28
36
|
On your install: sign up, create an application, then create an
|
data/docs/testing.md
CHANGED
|
@@ -27,10 +27,20 @@ require "railwatch/minitest"
|
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
Railwatch must be *enabled* in the test environment or every block would look
|
|
30
|
-
empty.
|
|
31
|
-
present, so set any non-blank `RAILWATCH_TOKEN` for
|
|
32
|
-
an in-memory transport, never over the
|
|
33
|
-
|
|
30
|
+
empty. An embedded install (`transport = :local`) is enabled with no token. A
|
|
31
|
+
cloud install needs a token present, so set any non-blank `RAILWATCH_TOKEN` for
|
|
32
|
+
the test env. Either way records go to an in-memory transport, never over the
|
|
33
|
+
network or into the telemetry database. If Railwatch is disabled, the matchers
|
|
34
|
+
raise `Railwatch::SpecHelper::Disabled` rather than quietly passing.
|
|
35
|
+
|
|
36
|
+
An embedded install adds its two databases to the `test` environment too, and
|
|
37
|
+
Rails' schema check refuses to run the suite while their migrations are
|
|
38
|
+
pending. `bin/rails db:test:prepare` does not create them, because they keep no
|
|
39
|
+
schema file; run this once locally and as a CI setup step:
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
RAILS_ENV=test bin/rails db:prepare
|
|
43
|
+
```
|
|
34
44
|
|
|
35
45
|
Sampling is forced on for the block, so a fractional `c.sample` in the app's
|
|
36
46
|
test config can't turn an assertion into one that never fires either.
|