railwatch 0.6.0 → 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 +42 -26
- data/CHANGELOG.md +28 -0
- data/README.md +40 -29
- data/app/jobs/railwatch/rollup_job.rb +3 -1
- data/app/models/railwatch/ingest/rollup_absorber.rb +1 -1
- data/docs/ai-and-mcp.md +9 -3
- data/docs/configuration.md +68 -27
- data/docs/embedded.md +76 -43
- data/docs/faq.md +22 -10
- data/docs/getting-started.md +116 -41
- 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 +62 -23
- data/lib/generators/railwatch/install/install_generator.rb +5 -4
- data/lib/generators/railwatch/install/templates/initializer.rb.tt +2 -2
- data/lib/railwatch/version.rb +1 -1
- data/llms.txt +21 -18
- data/public/railwatch/assets/{app-layout-2xAKndVD.js → app-layout-Zc0v-hYh.js} +1 -1
- data/public/railwatch/assets/{app-wordmark-Bt6XqURo.js → app-wordmark-BA_60AVb.js} +1 -1
- data/public/railwatch/assets/{appearance-DyCfeh9o.js → appearance-CcfP9tZ7.js} +1 -1
- data/public/railwatch/assets/application-BrN3Sz94.css +1 -0
- data/public/railwatch/assets/{arrow-up-EmePsTKP.js → arrow-up-CLQ-7heQ.js} +1 -1
- data/public/railwatch/assets/{auth-layout-BuwogWMZ.js → auth-layout-C05gEBIQ.js} +1 -1
- data/public/railwatch/assets/{badge-Dv7I-ebP.js → badge-DRae8XwK.js} +1 -1
- data/public/railwatch/assets/{braces-iZ7MEfYP.js → braces-rSYydpLY.js} +1 -1
- data/public/railwatch/assets/{card-DqdEEKcJ.js → card-DPjFKfen.js} +1 -1
- data/public/railwatch/assets/{chart-C4k7Ub9W.js → chart-DWh7l8yM.js} +1 -1
- data/public/railwatch/assets/{chart-hover-2yZG3w2u.js → chart-hover-CfoZUY4J.js} +1 -1
- data/public/railwatch/assets/{chart-panel-CONkZcn9.js → chart-panel-CC49WTQL.js} +1 -1
- data/public/railwatch/assets/{checkbox-DKbThbxr.js → checkbox-DPkLUiwM.js} +1 -1
- data/public/railwatch/assets/{code-CYahYQHZ.js → code-CLmYS6FU.js} +1 -1
- data/public/railwatch/assets/{copy-block-CbJum9s8.js → copy-block-CGcXxp8J.js} +1 -1
- data/public/railwatch/assets/{copy-id-JmvLfkmx.js → copy-id-vBYHQwxg.js} +1 -1
- data/public/railwatch/assets/{cursor-load-more-C1ylbqqL.js → cursor-load-more-Ddwxe5dZ.js} +1 -1
- data/public/railwatch/assets/{data-table-CVKR2wkq.js → data-table-CzKTEE-O.js} +1 -1
- data/public/railwatch/assets/{edit-BwSEVrTG.js → edit-B1kmWkzd.js} +1 -1
- data/public/railwatch/assets/{edit-BXF5oNYA.js → edit-D9cx4pbG.js} +1 -1
- data/public/railwatch/assets/{edit-D5BRDwLT.js → edit-yv6j9p-V.js} +1 -1
- data/public/railwatch/assets/{empty-state-Bn48Gplm.js → empty-state-CW4wclK_.js} +1 -1
- data/public/railwatch/assets/{env-layout-CKLL8Ds7.js → env-layout-Kz7wks1x.js} +1 -1
- data/public/railwatch/assets/{execution-path-DWovhgLi.js → execution-path-FAuIzOXB.js} +1 -1
- data/public/railwatch/assets/{filter-bar-xcbJC93z.js → filter-bar-CJDjWFib.js} +1 -1
- data/public/railwatch/assets/{flamegraph-BNbNIuKT.js → flamegraph-EaGkP2NT.js} +1 -1
- data/public/railwatch/assets/{frames-Bk39xfJG.js → frames-zZuIaNH9.js} +1 -1
- data/public/railwatch/assets/{google-sign-in-button-B5as2ps1.js → google-sign-in-button-2_zgbgVy.js} +1 -1
- data/public/railwatch/assets/{index-XjXcV-EO.js → index-B49SWz7K.js} +1 -1
- data/public/railwatch/assets/{index-B0xgzHuN.js → index-BHlY4wKe.js} +1 -1
- data/public/railwatch/assets/{index-CiaeS-n7.js → index-BZpPtyFY.js} +1 -1
- data/public/railwatch/assets/{index-BfitxVBv.js → index-BeVK6jCL.js} +1 -1
- data/public/railwatch/assets/{index-C-EwH4lO.js → index-BfbSo01U.js} +1 -1
- data/public/railwatch/assets/{index-CrA7jBTK.js → index-BfgncAv6.js} +1 -1
- data/public/railwatch/assets/{index-D0mKj3Hg.js → index-BhNszK1k.js} +1 -1
- data/public/railwatch/assets/{index-CDXlITyG.js → index-Bu01uWvw.js} +1 -1
- data/public/railwatch/assets/{index-w_skgMmK.js → index-C1s_hK3p.js} +1 -1
- data/public/railwatch/assets/{index-Zkt4W_CM.js → index-C8Cggnbw.js} +1 -1
- data/public/railwatch/assets/{index-BDVShWMs.js → index-CBip6V4z.js} +1 -1
- data/public/railwatch/assets/{index-DeLtMWou.js → index-CMJGss5R.js} +1 -1
- data/public/railwatch/assets/{index-LKgpCzoR.js → index-CRo3yK20.js} +1 -1
- data/public/railwatch/assets/{index-gCvuyG1l.js → index-CWB_p2J8.js} +1 -1
- data/public/railwatch/assets/{index-C8JIdb3_.js → index-CWHpndGc.js} +1 -1
- data/public/railwatch/assets/{index-CBFCtJAR.js → index-Ca_S4Sc3.js} +1 -1
- data/public/railwatch/assets/{index-o1xqGvLo.js → index-CanPDDOa.js} +1 -1
- data/public/railwatch/assets/{index-Bd4YpOIO.js → index-Cie90Yat.js} +1 -1
- data/public/railwatch/assets/{index-C_hQmVTP.js → index-CiepQ_pR.js} +1 -1
- data/public/railwatch/assets/{index-BRvRkfob.js → index-Cm1uCIGN.js} +1 -1
- data/public/railwatch/assets/{index-dpG-19ZN.js → index-CzitnnSC.js} +1 -1
- data/public/railwatch/assets/{index-Vj93BTR5.js → index-DEUfClv3.js} +1 -1
- data/public/railwatch/assets/index-DJKwo-mI.js +1 -0
- data/public/railwatch/assets/{index-cTzBycCg.js → index-DK6y0YHp.js} +1 -1
- data/public/railwatch/assets/{index-B-FXAo1w.js → index-DMNPpLH9.js} +1 -1
- data/public/railwatch/assets/{index-BeQw69GM.js → index-DZO1mSfX.js} +1 -1
- data/public/railwatch/assets/{index-BVWapDeB.js → index-Db5wj3M5.js} +1 -1
- data/public/railwatch/assets/{index-DV5MdmpY.js → index-Dcy5WktB.js} +1 -1
- data/public/railwatch/assets/{index-aPCZX-pW.js → index-DvG0-7Lx.js} +1 -1
- data/public/railwatch/assets/{index-Atp1FTgX.js → index-Dvj1wuka.js} +1 -1
- data/public/railwatch/assets/{index-COd9lT84.js → index-KyZX46qX.js} +1 -1
- data/public/railwatch/assets/{index-S0NVaLj9.js → index-Ze-KP-sl.js} +1 -1
- data/public/railwatch/assets/{index-DaE-1xhx.js → index-mhRbWLBM.js} +1 -1
- data/public/railwatch/assets/{index-DdVg9LKX.js → index-x099JL5f.js} +1 -1
- data/public/railwatch/assets/{index-CcennT28.js → index-x28zb_nf.js} +1 -1
- data/public/railwatch/assets/{inertia-CqnzqPVD.js → inertia-Cuyz2ZHO.js} +2 -2
- data/public/railwatch/assets/{input-error-ByT28UdP.js → input-error-hog6gGxg.js} +1 -1
- data/public/railwatch/assets/{json-viewer-Bnu9RsJL.js → json-viewer-DH2W9HXf.js} +1 -1
- data/public/railwatch/assets/{klass-nLiBV-Az.js → klass-DHbDelLk.js} +1 -1
- data/public/railwatch/assets/{label-1nSzpSB6.js → label-DdCBgiUn.js} +1 -1
- data/public/railwatch/assets/{layout-D7lE1Doo.js → layout-Cueyl7c5.js} +1 -1
- data/public/railwatch/assets/{live-dot-UdzOfzJb.js → live-dot-BZgYTYdt.js} +1 -1
- data/public/railwatch/assets/{nav-BRCgp2w3.js → nav-BSSGObDZ.js} +1 -1
- data/public/railwatch/assets/{new-COyDKOOb.js → new-B8FSb8Bl.js} +1 -1
- data/public/railwatch/assets/{new-Cd9zPNJ8.js → new-C39_v2Ll.js} +1 -1
- data/public/railwatch/assets/{new-vjRzERL3.js → new-DVOPwaC5.js} +1 -1
- data/public/railwatch/assets/{new-C60G72DR.js → new-Dd40nvJR.js} +1 -1
- data/public/railwatch/assets/{new-DMJGKjmH.js → new-SxnYe1SE.js} +1 -1
- data/public/railwatch/assets/{new-Omvf-c9s.js → new-eP5vKD3Y.js} +1 -1
- data/public/railwatch/assets/{onboarding-gUjJQc-6.js → onboarding-CUpZl5KB.js} +1 -1
- data/public/railwatch/assets/{origin-identity-M1YUPYS0.js → origin-identity-BYt2uiuo.js} +1 -1
- data/public/railwatch/assets/{percentile-picker-BxIVblBw.js → percentile-picker-CeQgllxD.js} +1 -1
- data/public/railwatch/assets/{relative-time-DQ4kjowZ.js → relative-time-D5UbF4oO.js} +1 -1
- data/public/railwatch/assets/{release-health-Cb1tMOOO.js → release-health-uP-GF3_V.js} +1 -1
- data/public/railwatch/assets/{route-BJkoEm7r.js → route-DQAY8JEr.js} +1 -1
- data/public/railwatch/assets/{segmented-C4oWE73o.js → segmented-BeIe4uqk.js} +1 -1
- data/public/railwatch/assets/{select-ocdQJdQo.js → select-2R16457Z.js} +1 -1
- data/public/railwatch/assets/{separator-Ct2UcZ2x.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-BLJOyLb_.js → show-B0X1hRQH.js} +1 -1
- data/public/railwatch/assets/{show-C8TkpEgm.js → show-BKUV5l5q.js} +1 -1
- data/public/railwatch/assets/{show-BAjfddEn.js → show-BQJD_CsF.js} +1 -1
- data/public/railwatch/assets/{show-C6OiJ6Hr.js → show-BhmD6Sxp.js} +1 -1
- data/public/railwatch/assets/{show-CEhPVMMT.js → show-C3KcnVo2.js} +1 -1
- data/public/railwatch/assets/{show-CX_pab9S.js → show-C95cHd06.js} +1 -1
- data/public/railwatch/assets/{show-Bsu1rvnK.js → show-CdB4uVQS.js} +1 -1
- data/public/railwatch/assets/{show-yEOrk6Pt.js → show-CoCqcVIp.js} +1 -1
- data/public/railwatch/assets/{show-DDVsv-uS.js → show-D2LqjEsU.js} +1 -1
- data/public/railwatch/assets/{show-D6lgzL6n.js → show-DNpnKyTh.js} +1 -1
- data/public/railwatch/assets/{show-eKazflRI.js → show-DOlTxbig.js} +1 -1
- data/public/railwatch/assets/{show-Dwiq-8hA.js → show-DQCkttL8.js} +1 -1
- data/public/railwatch/assets/{show-CBFBiBY2.js → show-DcxesipC.js} +1 -1
- data/public/railwatch/assets/{show-BoVyMQS6.js → show-IGJoc_X0.js} +1 -1
- data/public/railwatch/assets/{show-gtwKsTsl.js → show-YsNpqbcI.js} +1 -1
- data/public/railwatch/assets/{show-B4bwI9x2.js → show-qNV6H8SH.js} +1 -1
- data/public/railwatch/assets/{sort-header-CPM6fhXv.js → sort-header-mEJUM3Av.js} +1 -1
- data/public/railwatch/assets/{sparkline-cell-CXCxXPWt.js → sparkline-cell-Dwg7awwh.js} +1 -1
- data/public/railwatch/assets/{stat-BNEfE8X1.js → stat-ZtxGU8lE.js} +1 -1
- data/public/railwatch/assets/{status-badge-D0aylrM5.js → status-badge-CH5P-Xkj.js} +1 -1
- data/public/railwatch/assets/{tenant-path-C4aM_cO0.js → tenant-path-CQoP-BeF.js} +1 -1
- data/public/railwatch/assets/{text-link-CP4lqUi6.js → text-link-DHJ8BXx5.js} +1 -1
- data/public/railwatch/assets/{textarea-DzJo2ds4.js → textarea-BeQtQyl5.js} +1 -1
- data/public/railwatch/assets/{timeline-BNcJ9PMc.js → timeline-CaKQVu48.js} +1 -1
- data/public/railwatch/assets/{transition-BX2M1iaq.js → transition-ksDpqhKJ.js} +1 -1
- data/public/railwatch/assets/{use-clipboard-C1ApSwzA.js → use-clipboard-DNefo-ky.js} +1 -1
- data/public/railwatch/assets/{use-live-DWwLg1Nj.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-DiQ4mqT_.js +0 -1
- data/public/railwatch/assets/series-chart-D6upsrSn.js +0 -1
- data/public/railwatch/assets/show-CV-lG2b6.js +0 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 293f64188856fdbadb54fb91bc640441a0996ba3d914d320b778c72536cbb3a8
|
|
4
|
+
data.tar.gz: b10e36a4cbb054e5170d1984003be84d2ea62acee2c1a3bda1c3b560a5f54bab
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7e45fc461237f0d9aceccbd99f359b8a81a404d6620600361d3d4fd973f986baf32779ea3921f581a81527c4764687aa8771897c466498860cd8f30c53cd5e4b
|
|
7
|
+
data.tar.gz: 5152d7d1b250d98299e2f675a951ec6f544e0a91405365877b434e8fa6fb1cbfddf061f84fe06aca00cb9202de0092f0016c024779707e69f9d400d33117a007
|
data/AGENTS.md
CHANGED
|
@@ -4,11 +4,12 @@ About *using* the `railwatch` gem from a Rails application; copy
|
|
|
4
4
|
this into that application's repository. Index of everything else:
|
|
5
5
|
[`llms.txt`](llms.txt).
|
|
6
6
|
|
|
7
|
-
Railwatch instruments a Rails app end to end and
|
|
7
|
+
Railwatch instruments a Rails app end to end and stores linked telemetry in
|
|
8
|
+
two SQLite databases of the app's own (embedded, the default) or sends it to
|
|
8
9
|
Railwatch Cloud. Every request, job attempt, scheduled task run, and command is
|
|
9
|
-
an **execution**; every query, cache read, log line, outgoing HTTP call,
|
|
10
|
-
render, exception, and span is a child of one, linked by
|
|
11
|
-
`execution_id`/`trace_id`. It never writes to the app's database.
|
|
10
|
+
an **execution**; every query, cache read, log line, outgoing HTTP call, LLM
|
|
11
|
+
call, view render, exception, and span is a child of one, linked by
|
|
12
|
+
`execution_id`/`trace_id`. It never writes to the app's primary database.
|
|
12
13
|
|
|
13
14
|
## Install
|
|
14
15
|
|
|
@@ -19,24 +20,35 @@ bin/rails generate railwatch:install --prompt-token --kamal-secrets # or: Railw
|
|
|
19
20
|
```
|
|
20
21
|
|
|
21
22
|
With no flags the install is embedded (see [Embedded mode](docs/embedded.md)):
|
|
22
|
-
telemetry stays in two SQLite databases the app owns
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
at `/railwatch
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
23
|
+
telemetry stays in two SQLite databases the app owns (`railwatch`,
|
|
24
|
+
`railwatch_telemetry`, added to `config/database.yml`), Puma forks one writer
|
|
25
|
+
for them (`plugin :railwatch` in `config/puma.rb`), and the dashboard is served
|
|
26
|
+
at `/railwatch`. It is open in development and answers 401 elsewhere until
|
|
27
|
+
`RAILS_ENV=production bin/rails railwatch:authentication:configure` sets a
|
|
28
|
+
password. An exported `RAILWATCH_TOKEN` alone does not change the mode.
|
|
29
|
+
`--cloud`, or any of `--prompt-token`, `--token-stdin`, `--url`,
|
|
30
|
+
`--kamal-secrets`, sends telemetry to Railwatch Cloud instead.
|
|
31
|
+
|
|
32
|
+
Both modes write `config/initializers/railwatch.rb`, mount `Railwatch::Engine`
|
|
33
|
+
at `/railwatch`, add the Kamal `post-deploy` hook and the Inertia browser client
|
|
34
|
+
where the app has them, and require `railwatch/rspec` (or `railwatch/minitest`)
|
|
35
|
+
in the test helper; the cloud install then runs `railwatch:doctor`. A
|
|
36
|
+
prompted/stdin/environment token goes into `.env` only when Git confirms that
|
|
37
|
+
file is ignored; token values are never printed. Configuration lives only in
|
|
38
|
+
that initializer; every option also has a `RAILWATCH_*` environment variable.
|
|
39
|
+
An embedded install mirrors to Railwatch Cloud as well with a token and
|
|
40
|
+
`c.export_enabled = true`.
|
|
31
41
|
|
|
32
42
|
## Rake tasks
|
|
33
43
|
|
|
34
44
|
| Task | Does |
|
|
35
45
|
|---|---|
|
|
36
|
-
| `bin/rails railwatch:doctor` | ✓/✗ per check: token, ingest URL, reachability
|
|
37
|
-
| `bin/rails railwatch:
|
|
38
|
-
| `bin/rails railwatch:
|
|
39
|
-
| `bin/rails railwatch:
|
|
46
|
+
| `bin/rails railwatch:doctor` | ✓/✗ per check. Embedded: databases, migrations, writer process, last write, maintenance, dashboard access, export. Cloud: token, token storage, ingest URL, reachability. Both: middleware, engine mount, deploy, sample rates, ignored types, Kamal hook, browser client and whether an entrypoint calls it, profiler backend, test matchers. Exits non-zero on a missing database, pending migrations, broken export, a missing or tracked token, or an unreachable host. **Run this first when telemetry is missing.** |
|
|
47
|
+
| `bin/rails railwatch:authentication:configure` | Sets the embedded dashboard's HTTP Basic credentials for the current `RAILS_ENV`. |
|
|
48
|
+
| `bin/rails railwatch:export:status` | What the export queue holds and whether it can send. |
|
|
49
|
+
| `bin/rails railwatch:token` | Where to create a Railwatch Cloud ingest token for this app. |
|
|
50
|
+
| `bin/rails railwatch:mcp` | Paste-ready MCP client configuration for this app's Railwatch Cloud. |
|
|
51
|
+
| `bin/rails railwatch:deploy[ref,name,url]` | Records a deploy marker (in the embedded database, or on the cloud). Use as a release step when not deploying with Kamal. |
|
|
40
52
|
| `bin/rails 'railwatch:sourcemaps[public,true]'` | Uploads Vite source maps for the configured deploy, then deletes acknowledged files. Run after building and before publishing assets. Omit `true` to retain files. See [Source maps](docs/source-maps.md). |
|
|
41
53
|
|
|
42
54
|
## Facade
|
|
@@ -77,9 +89,11 @@ Per action, in a controller class body: `railwatch_sample 0.01, only: :index`,
|
|
|
77
89
|
## Specs
|
|
78
90
|
|
|
79
91
|
`require "railwatch/rspec"` in `spec/rails_helper.rb` (or `"railwatch/minitest"` in
|
|
80
|
-
`test/test_helper.rb`). Railwatch must be enabled in the test env
|
|
81
|
-
|
|
82
|
-
|
|
92
|
+
`test/test_helper.rb`). Railwatch must be enabled in the test env: an embedded
|
|
93
|
+
install is, and needs `RAILS_ENV=test bin/rails db:prepare` once so its test
|
|
94
|
+
databases exist; a cloud install needs any non-blank `RAILWATCH_TOKEN`. Records
|
|
95
|
+
go to an in-memory transport, never over the wire. All matchers are block
|
|
96
|
+
matchers.
|
|
83
97
|
|
|
84
98
|
```ruby
|
|
85
99
|
expect { get "/widgets" }.to have_railwatch_queries(at_most: 6) # or exactly:/at_least:
|
|
@@ -102,8 +116,8 @@ melt in production.
|
|
|
102
116
|
|
|
103
117
|
## MCP
|
|
104
118
|
|
|
105
|
-
|
|
106
|
-
token at Settings → Profile → "API & MCP token", then:
|
|
119
|
+
Railwatch Cloud (not embedded mode) is an MCP server at `<ingest host>/mcp`.
|
|
120
|
+
Generate a personal token at Settings → Profile → "API & MCP token", then:
|
|
107
121
|
|
|
108
122
|
```sh
|
|
109
123
|
claude mcp add railwatch --transport http https://railwatch.rebulk.com/mcp \
|
|
@@ -112,10 +126,12 @@ claude mcp add railwatch --transport http https://railwatch.rebulk.com/mcp \
|
|
|
112
126
|
|
|
113
127
|
`bin/rails railwatch:mcp` prints this and the Claude Desktop, Cursor, VS Code, and
|
|
114
128
|
Zed equivalents for whichever platform the app points at. Call
|
|
115
|
-
`list_applications` first; then `list_issues`, `get_issue
|
|
116
|
-
|
|
117
|
-
`
|
|
118
|
-
`
|
|
129
|
+
`list_applications` first; then `list_issues`, `get_issue` (stack frames,
|
|
130
|
+
cause, locals, breadcrumbs), `get_route`, `search_requests`, `get_execution`,
|
|
131
|
+
`explain_query`, `get_profile`, `search_logs`, `search_telemetry`,
|
|
132
|
+
`release_health`, `recent_deploys`, `list_alerts`, and the `triage_issue` /
|
|
133
|
+
`slow_route` / `daily_summary` prompts. `update_issue` and `add_comment` write,
|
|
134
|
+
attributed to the token's user and your agent name. All durations are
|
|
119
135
|
milliseconds; these docs are served at `railwatch://docs/<name>`.
|
|
120
136
|
|
|
121
137
|
## Gotchas
|
data/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,34 @@
|
|
|
6
6
|
filled in by the release commit, which is also the only commit that
|
|
7
7
|
touches lib/railwatch/version.rb and Gemfile.lock. See CONTRIBUTING.md. -->
|
|
8
8
|
|
|
9
|
+
## 0.6.1 (2026-09-23)
|
|
10
|
+
|
|
11
|
+
- The docs describe embedded as the default install throughout: two
|
|
12
|
+
commands, then optional Railwatch Cloud. They correct what 0.6.0 left
|
|
13
|
+
stale: an embedded install needs no token; the PostgreSQL and MySQL
|
|
14
|
+
steps; export needs a token first; a `dashboard_user` resolver names
|
|
15
|
+
the operator but does not gate pages; the Puma plugin line must be
|
|
16
|
+
unconditional; an embedded app's test databases need one
|
|
17
|
+
`RAILS_ENV=test bin/rails db:prepare`. MCP is marked as a Railwatch
|
|
18
|
+
Cloud feature. LLM calls are listed.
|
|
19
|
+
- The installer's embedded next steps say to get a token before turning
|
|
20
|
+
on export.
|
|
21
|
+
- Draw the Trend sparklines. Recharts pads every side of a chart by 5px,
|
|
22
|
+
which left the 32x10 table-cell sparkline a plot area 0px tall, so every
|
|
23
|
+
Trend column on every page was blank.
|
|
24
|
+
- LLM calls: a failed call no longer counts as "unpriced". It used no
|
|
25
|
+
tokens and has nothing to price, yet any failure made its model read
|
|
26
|
+
"+N unpriced" beside a complete spend total; the Recent table shows "–"
|
|
27
|
+
for it instead. The model and tool call charts label their bars ok /
|
|
28
|
+
failed instead of HTTP status classes, and the Recent table's Call
|
|
29
|
+
column truncates instead of pushing Detail off the right edge.
|
|
30
|
+
- Issues: the daily chart marks each deploy day with a line and names the
|
|
31
|
+
deploys in the tooltip, instead of printing refs that overprint when
|
|
32
|
+
several ship in a week.
|
|
33
|
+
- Jobs: the recent runs card keeps the job name readable. The exception
|
|
34
|
+
truncates, and the origin user and tenant columns appear from 2xl up
|
|
35
|
+
(the job class page shows them at every width).
|
|
36
|
+
|
|
9
37
|
## 0.6.0 (2026-09-22)
|
|
10
38
|
|
|
11
39
|
- Embedded is now the installer's default. `bin/rails generate
|
data/README.md
CHANGED
|
@@ -9,9 +9,10 @@ microseconds per query, with zero writes to your database.
|
|
|
9
9
|
By default all of it stays inside the app:
|
|
10
10
|
[embedded mode](docs/embedded.md) serves the full dashboard at
|
|
11
11
|
`/railwatch` out of two SQLite files your app owns, with no token, no
|
|
12
|
-
Node, no Redis and no job worker. Railwatch Cloud is optional
|
|
13
|
-
alerts
|
|
14
|
-
|
|
12
|
+
Node, no Redis and no job worker. Railwatch Cloud is optional: it
|
|
13
|
+
delivers alerts to Slack, email, webhooks or Linear (including when the
|
|
14
|
+
app is down), serves MCP to your AI assistant, and puts many apps and
|
|
15
|
+
servers in one place.
|
|
15
16
|
|
|
16
17
|
## Install
|
|
17
18
|
|
|
@@ -23,9 +24,12 @@ bin/rails generate railwatch:install # 2. embedded: databases, Puma writer, das
|
|
|
23
24
|
Restart and open `/railwatch`. It is open in development; before you
|
|
24
25
|
deploy, give it a password with
|
|
25
26
|
`RAILS_ENV=production bin/rails railwatch:authentication:configure`
|
|
26
|
-
(it answers 401 in production until you do).
|
|
27
|
+
(it answers 401 in production until you do). On a PostgreSQL or MySQL
|
|
28
|
+
app without the `sqlite3` gem, the generator adds it to the Gemfile;
|
|
29
|
+
run `bundle install` and `bin/rails db:prepare` to finish.
|
|
27
30
|
|
|
28
|
-
|
|
31
|
+
To send everything to Railwatch Cloud instead, pass `--cloud` or a token
|
|
32
|
+
option:
|
|
29
33
|
|
|
30
34
|
```sh
|
|
31
35
|
bundle add railwatch
|
|
@@ -33,10 +37,10 @@ bin/rails generate railwatch:install --prompt-token # hidden token input plus a
|
|
|
33
37
|
bin/rails railwatch:doctor # check every piece is wired up after restart
|
|
34
38
|
```
|
|
35
39
|
|
|
36
|
-
|
|
40
|
+
The generator's flags, getting a token, and deploying with Kamal,
|
|
37
41
|
Docker, Heroku, or Render are covered in
|
|
38
|
-
[Getting started](docs/getting-started.md). For
|
|
39
|
-
|
|
42
|
+
[Getting started](docs/getting-started.md). For an unreleased revision,
|
|
43
|
+
use the Git source instead:
|
|
40
44
|
|
|
41
45
|
```ruby
|
|
42
46
|
gem "railwatch", github: "Rebulk/railwatch"
|
|
@@ -52,12 +56,16 @@ gem "railwatch", github: "Rebulk/railwatch"
|
|
|
52
56
|
([Configuration](docs/configuration.md)).
|
|
53
57
|
- An optional stack profiler through `vernier` or `stackprof`, off by
|
|
54
58
|
default ([Configuration](docs/configuration.md)).
|
|
59
|
+
- LLM calls made through RubyLLM: tokens, cost, cut-off answers, tool
|
|
60
|
+
calls and workflows, each tied to the request or job that made it
|
|
61
|
+
([Record types](docs/records.md#llm_call)).
|
|
55
62
|
- A browser client for Inertia apps: page-visit timing, Core Web Vitals,
|
|
56
63
|
and browser errors ([Getting started](docs/getting-started.md)).
|
|
57
64
|
- RSpec and Minitest matchers that turn a query budget into a CI gate
|
|
58
65
|
([Testing](docs/testing.md)).
|
|
59
|
-
-
|
|
60
|
-
|
|
66
|
+
- With Railwatch Cloud, an MCP server so Claude Code, Cursor, VS Code, or
|
|
67
|
+
Zed can read your production data
|
|
68
|
+
([AI assistants and MCP](docs/ai-and-mcp.md)).
|
|
61
69
|
|
|
62
70
|
Configuration lives in `config/initializers/railwatch.rb`; most options
|
|
63
71
|
also have a `RAILWATCH_*` environment variable.
|
|
@@ -79,11 +87,11 @@ expect { get "/widgets" }.not_to have_railwatch_n_plus_one
|
|
|
79
87
|
|
|
80
88
|
## Embedded mode
|
|
81
89
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
90
|
+
Telemetry goes to two SQLite files the app owns -- `railwatch` for
|
|
91
|
+
issues, comments and saved views, `railwatch_telemetry` for what the app
|
|
92
|
+
reports -- and the dashboard is served at `/railwatch` from a bundle
|
|
93
|
+
shipped inside the gem. Nothing leaves the machine unless you turn on
|
|
94
|
+
export to Railwatch Cloud, and there is nothing else to run.
|
|
87
95
|
|
|
88
96
|
Puma forks a single writer process (`plugin :railwatch`, which the
|
|
89
97
|
installer adds) that owns both files. The web workers hand it batches
|
|
@@ -92,20 +100,21 @@ runs the maintenance clock too, so exception grouping, rollups, retention
|
|
|
92
100
|
and threshold scans happen without a queue.
|
|
93
101
|
|
|
94
102
|
That dashboard reads every query, log line and exception the app
|
|
95
|
-
produced, so
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
+
produced, so it is gated the way Mission Control Jobs is: HTTP Basic is
|
|
104
|
+
on, and with no credentials every request is 401 until you set them
|
|
105
|
+
with `bin/rails railwatch:authentication:configure`. Development is the
|
|
106
|
+
exception: with no credentials set there, it is open. Apps that would
|
|
107
|
+
rather use their own authentication turn Basic off and gate the pages
|
|
108
|
+
with `base_controller_class` or a routes constraint around the mount; a
|
|
109
|
+
`dashboard_user` resolver then names the operator and authorizes live
|
|
110
|
+
updates, but does not gate pages. [Embedded mode](docs/embedded.md)
|
|
111
|
+
covers all of it, including upgrades and what it costs to store.
|
|
103
112
|
|
|
104
113
|
## Documentation
|
|
105
114
|
|
|
106
|
-
- [Getting started](docs/getting-started.md) —
|
|
107
|
-
|
|
108
|
-
Heroku, Render, or none of them.
|
|
115
|
+
- [Getting started](docs/getting-started.md) — the embedded install, the
|
|
116
|
+
Railwatch Cloud install and its token, the three optional lines, and
|
|
117
|
+
deploying with Kamal, Docker, Heroku, Render, or none of them.
|
|
109
118
|
- [Configuration](docs/configuration.md) — every option and `RAILWATCH_*`
|
|
110
119
|
variable, field by field.
|
|
111
120
|
- [Record types](docs/records.md) — every record Railwatch ships and every
|
|
@@ -113,13 +122,15 @@ upgrades and what it costs to store.
|
|
|
113
122
|
- [Testing](docs/testing.md) — the RSpec and Minitest matchers, and a CI
|
|
114
123
|
performance gate.
|
|
115
124
|
- [AI assistants and MCP](docs/ai-and-mcp.md) — connecting Claude Code,
|
|
116
|
-
Cursor, VS Code, or Zed to your production data
|
|
125
|
+
Cursor, VS Code, or Zed to your production data through Railwatch
|
|
126
|
+
Cloud.
|
|
117
127
|
- [Replacing Sentry](docs/replacing-sentry.md) — a step-by-step migration,
|
|
118
128
|
option by option and call site by call site.
|
|
119
129
|
- [Coming from Laravel Nightwatch](docs/replacing-nightwatch.md) — the
|
|
120
130
|
record-type mapping and the sampling model, for Laravel people.
|
|
121
|
-
- [Embedded mode](docs/embedded.md) — the whole dashboard
|
|
122
|
-
app, telemetry in your own SQLite files,
|
|
131
|
+
- [Embedded mode](docs/embedded.md) — the default: the whole dashboard
|
|
132
|
+
inside your app, telemetry in your own SQLite files, and optional
|
|
133
|
+
export to Railwatch Cloud.
|
|
123
134
|
- [Self-hosting](docs/self-hosting.md) — pointing the gem at your own
|
|
124
135
|
Railwatch Cloud.
|
|
125
136
|
- [Troubleshooting](docs/troubleshooting.md) — every failure mode, paired
|
|
@@ -118,7 +118,9 @@ module Railwatch
|
|
|
118
118
|
cache_read_tokens: group.sum { |r| r.cache_read_tokens.to_i }, cache_write_tokens: group.sum { |r| r.cache_write_tokens.to_i },
|
|
119
119
|
cost_nanos: group.sum { |r| r.cost_nanos.to_i },
|
|
120
120
|
priced: group.count { |r| r.cost_nanos },
|
|
121
|
-
|
|
121
|
+
# A call that failed before the provider answered used no tokens,
|
|
122
|
+
# so there is nothing to price; only an answered call can be unpriced.
|
|
123
|
+
unpriced: group.count { |r| r.cost_nanos.nil? && r.status != "failed" },
|
|
122
124
|
# A cut-off answer is not an error and will never show in the error
|
|
123
125
|
# rate, so it needs counting on its own or it stays invisible.
|
|
124
126
|
truncated: group.count { |r| r.finish_reason == "max_tokens" },
|
|
@@ -160,7 +160,7 @@ module Railwatch
|
|
|
160
160
|
extra["cache_write_tokens"] += row[:cache_write_tokens].to_i
|
|
161
161
|
extra["cost_nanos"] += row[:cost_nanos].to_i
|
|
162
162
|
extra["priced"] += 1 if row[:cost_nanos]
|
|
163
|
-
extra["unpriced"] += 1 if row[:cost_nanos].nil?
|
|
163
|
+
extra["unpriced"] += 1 if row[:cost_nanos].nil? && row[:status] != "failed"
|
|
164
164
|
extra["truncated"] += 1 if row[:finish_reason] == "max_tokens"
|
|
165
165
|
extra["with_attachments"] += 1 if row[:attachments].to_i.positive?
|
|
166
166
|
end
|
data/docs/ai-and-mcp.md
CHANGED
|
@@ -9,6 +9,10 @@ crash-free rates per release. It can also write — resolve an issue, set a
|
|
|
9
9
|
priority, leave a comment — and every write is signed with your user and the
|
|
10
10
|
agent's name in the issue's activity feed.
|
|
11
11
|
|
|
12
|
+
MCP is a Railwatch Cloud feature. The embedded dashboard at `/railwatch`
|
|
13
|
+
does not serve it; an embedded install gets it by
|
|
14
|
+
[exporting to the cloud](embedded.md#three-ways-to-run-it).
|
|
15
|
+
|
|
12
16
|
The endpoint is `<your ingest host>/mcp`. For the hosted platform that's
|
|
13
17
|
`https://railwatch.rebulk.com/mcp`; if you self-host, it is your own host (see
|
|
14
18
|
[`self-hosting.md`](self-hosting.md)). The gem knows which one you're on:
|
|
@@ -145,9 +149,9 @@ durations are milliseconds.** Every `window` argument takes `1h`, `6h`,
|
|
|
145
149
|
|
|
146
150
|
| Tool | Arguments | Returns |
|
|
147
151
|
|---|---|---|
|
|
148
|
-
| `list_applications` | — | applications, their account, and their environments. Start here — everything else takes the `application_slug` and `environment` this returns. |
|
|
152
|
+
| `list_applications` | — | applications, their account, and their environments. Start here — everything else takes the `application_slug` and `environment` this returns (plus `account_slug`/`application_id` when two accounts share a slug). |
|
|
149
153
|
| `list_issues` | `application_slug?`, `environment?`, `status?` (`open`/`resolved`/`ignored`, default `open`), `limit?` | issues with key, title, kind, status, priority, occurrence and affected-user counts, culprit, first/last seen. |
|
|
150
|
-
| `get_issue` | `key` | one issue
|
|
154
|
+
| `get_issue` | `key` or `issue_id` | one issue. For an exception, the sample and latest occurrences with stored stack frames, cause, locals, parsed context, browser breadcrumbs, and the execution it happened inside. For a performance or anomaly issue, the metric, limit or baseline, representative records, trend, and deploy comparison. |
|
|
151
155
|
| `update_issue` | `key`, `status?`, `priority?`, `assignee_email?`, `agent?` | the updated issue. Writes an activity entry attributed to your user and `agent`. |
|
|
152
156
|
| `add_comment` | `key`, `body`, `agent?` | the created comment, attributed the same way. |
|
|
153
157
|
| `list_slow_routes` | `application_slug`, `environment`, `window?` | the 20 slowest routes by p95, each with `group_hash`, count, errors, `p95_ms`. |
|
|
@@ -157,6 +161,7 @@ durations are milliseconds.** Every `window` argument takes `1h`, `6h`,
|
|
|
157
161
|
| `explain_query` | `application_slug`, `environment`, `group_hash`, `window?` | the stored query plan for a query group, with the SQL and the sample's duration. `explain` is null unless the app sets `RAILWATCH_CAPTURE_QUERY_EXPLAIN`. `sql` is the normalized shape unless the app also sets `RAILWATCH_CAPTURE_SQL_VALUES`. |
|
|
158
162
|
| `get_profile` | `application_slug`, `environment`, `profile_id?`, `execution_id?`, `limit?` | the hottest frames of a stack profile — self and total samples, each with a percentage. |
|
|
159
163
|
| `search_logs` | `application_slug`, `environment`, `q`, `level?`, `limit?` | matching log lines, each with the `execution_id` to expand with `get_execution`. |
|
|
164
|
+
| `search_telemetry` | `application_slug`, `environment`, `resource` (`jobs`/`exceptions`/`logs`/`queries`), `q?`, `window?`, `cursor?`, `limit?` | raw rows of that resource with cursor pagination (`meta.next_cursor`, `meta.has_more`). `q` takes `after:`, `before:`, `user:`, `tenant:`, `deploy:`, plus per-resource keys such as `class:`, `queue:`, `level:`. |
|
|
160
165
|
| `list_tenants` | `application_slug`, `environment`, `window?`, `q?` | your app's own tenants (whatever it passes to `Railwatch.context(tenant:)`) with request, error, job, exception, and user counts. |
|
|
161
166
|
| `recent_deploys` | `application_slug`, `environment` | the 20 most recent deploys with ref, name, time, and link. |
|
|
162
167
|
| `release_health` | `application_slug`, `environment`, `window?` | crash-free session rate, crash-free user rate, and adoption per release. |
|
|
@@ -220,7 +225,8 @@ Copy either into your own app's repo to give its agent the same context.
|
|
|
220
225
|
|
|
221
226
|
## See also
|
|
222
227
|
|
|
223
|
-
- [`getting-started.md`](getting-started.md) — install, token,
|
|
228
|
+
- [`getting-started.md`](getting-started.md) — install, the cloud token,
|
|
229
|
+
first request.
|
|
224
230
|
- [`self-hosting.md`](self-hosting.md) — pointing the gem, and this endpoint,
|
|
225
231
|
at your own platform.
|
|
226
232
|
- [`troubleshooting.md`](troubleshooting.md) — when something isn't
|
data/docs/configuration.md
CHANGED
|
@@ -2,18 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
Everything below lives on `Railwatch::Configuration`, in
|
|
4
4
|
`lib/railwatch/configuration.rb`. Set it via
|
|
5
|
-
`Railwatch.configure { |c| ... }` in `config/initializers/railwatch.rb
|
|
6
|
-
|
|
7
|
-
`
|
|
8
|
-
|
|
9
|
-
initializer always win over the env var.
|
|
5
|
+
`Railwatch.configure { |c| ... }` in `config/initializers/railwatch.rb`,
|
|
6
|
+
which `bin/rails generate railwatch:install` creates. Most settings have a
|
|
7
|
+
`RAILWATCH_*` env var default; the tables below show which. Explicit
|
|
8
|
+
values set in the initializer always win over the env var.
|
|
10
9
|
|
|
11
10
|
## Core
|
|
12
11
|
|
|
13
12
|
| Attribute | Env var | Default | Meaning |
|
|
14
13
|
|---|---|---|---|
|
|
15
|
-
| `enabled` | `RAILWATCH_ENABLED` | `true` | Master switch. `Railwatch.enabled?` is also `false` whenever `token` is blank, so setting only `RAILWATCH_TOKEN` is enough to turn
|
|
16
|
-
| `token` | `RAILWATCH_TOKEN` | nil | Bearer token for `/ingest`. Required. |
|
|
14
|
+
| `enabled` | `RAILWATCH_ENABLED` | `true` | Master switch. With `transport = :http`, `Railwatch.enabled?` is also `false` whenever `token` is blank, so setting only `RAILWATCH_TOKEN` is enough to turn a cloud install on. An embedded install needs no token. |
|
|
15
|
+
| `token` | `RAILWATCH_TOKEN` | nil | Bearer token for `/ingest`. Required with `transport = :http` and for [export](#export-to-railwatch-cloud). |
|
|
17
16
|
| `ingest_url` | `RAILWATCH_INGEST_URL` | `https://railwatch.rebulk.com` | Platform base URL. Point at a self-hosted instance to override. |
|
|
18
17
|
| `allow_http` | `RAILWATCH_ALLOW_HTTP` | `false` | Permit a non-loopback plain HTTP ingest URL. HTTPS is required by default; `localhost`, `127.0.0.1`, and `::1` remain available for local self-hosted development. |
|
|
19
18
|
| `deploy` | `RAILWATCH_DEPLOY` | auto-detected (order below), then nil | Version tag stamped on every record and used by `railwatch:deploy`. Full 40-character SHAs are shortened to 12 characters. |
|
|
@@ -26,7 +25,8 @@ initializer always win over the env var.
|
|
|
26
25
|
| `beacon_rate_limit` | `RAILWATCH_BEACON_RATE_LIMIT` | `120` | Beacon POSTs accepted per client IP per minute before `POST /railwatch/beacon` answers 429. The beacon is unauthenticated and keeps every browser error it is sent, so this is what stops a script from spending the app's event quota. Counted in the app's cache store; `0` turns it off. |
|
|
27
26
|
|
|
28
27
|
`Railwatch.enabled?` delegates to `config.enabled?`, which is `@enabled &&
|
|
29
|
-
token.present
|
|
28
|
+
(local? || token.present?)`. There is no separate "is configured" check
|
|
29
|
+
elsewhere.
|
|
30
30
|
|
|
31
31
|
Deploy detection stops at the first value found: `RAILWATCH_DEPLOY`,
|
|
32
32
|
`KAMAL_VERSION`, `GIT_REV`, `GIT_SHA`, `SOURCE_VERSION`,
|
|
@@ -569,7 +569,7 @@ Rake tasks and Solid Queue jobs are never interactive.
|
|
|
569
569
|
|
|
570
570
|
| Attribute | Env var | Default | Meaning |
|
|
571
571
|
|---|---|---|---|
|
|
572
|
-
| `capture_exception_source` | `RAILWATCH_CAPTURE_EXCEPTION_SOURCE_CODE` | `true` |
|
|
572
|
+
| `capture_exception_source` | `RAILWATCH_CAPTURE_EXCEPTION_SOURCE_CODE` | `true` | Record source snippet lines surrounding each in-application exception frame (sent to Railwatch Cloud when the install reports there or exports). This is on by default for crash context; disable it when source disclosure is outside the application's telemetry policy. |
|
|
573
573
|
| `capture_exception_locals` | `RAILWATCH_CAPTURE_EXCEPTION_LOCALS` | `false` | Snapshot the raising frame's local variables (up to 25, values truncated to 200 chars, run through the same filter as request params) onto each exception, like Sentry's locals panel. Installs a `TracePoint(:raise)`; opt in per environment. |
|
|
574
574
|
| `capture_request_payload` | `RAILWATCH_CAPTURE_REQUEST_PAYLOAD` | `false` | Capture (redacted) request params — only for a request that raised, never otherwise. |
|
|
575
575
|
| `capture_job_arguments` | `RAILWATCH_CAPTURE_JOB_ARGUMENTS` | `false` | Add the job's real arguments (`job.serialize["arguments"]`) to each `job_attempt`/`scheduled_task` record, capped at 8 KiB of JSON. Hash arguments run through the same filter as request params. Off by default because job arguments routinely carry PII; `arguments_preview` (argument *shapes* only) is always on regardless. |
|
|
@@ -884,7 +884,7 @@ ignore block's building blocks, and are nestable.
|
|
|
884
884
|
## Embedded mode
|
|
885
885
|
|
|
886
886
|
```ruby
|
|
887
|
-
c.transport = :local # RAILWATCH_TRANSPORT; default "http"
|
|
887
|
+
c.transport = :local # RAILWATCH_TRANSPORT; runtime default "http", but the installer writes :local
|
|
888
888
|
c.issue_prefix = "SHOP" # RAILWATCH_ISSUE_PREFIX; default from the app name
|
|
889
889
|
c.repository_url = "..." # RAILWATCH_REPOSITORY_URL
|
|
890
890
|
c.retention_days = 7 # RAILWATCH_RETENTION_DAYS
|
|
@@ -892,8 +892,9 @@ c.http_basic_auth_enabled = true # RAILWATCH_HTTP_BASIC_AUTH_ENABLED; on, a
|
|
|
892
892
|
c.http_basic_auth_user = "ops" # RAILWATCH_HTTP_BASIC_AUTH_USER, or credentials railwatch.http_basic_auth_user
|
|
893
893
|
c.http_basic_auth_password = "..." # RAILWATCH_HTTP_BASIC_AUTH_PASSWORD, or credentials railwatch.http_basic_auth_password
|
|
894
894
|
c.base_controller_class = "AdminController" # RAILWATCH_BASE_CONTROLLER_CLASS; default ActionController::Base
|
|
895
|
-
c.dashboard_open = false # RAILWATCH_DASHBOARD_OPEN; public on purpose
|
|
895
|
+
c.dashboard_open = false # RAILWATCH_DASHBOARD_OPEN; default false, true makes it public on purpose
|
|
896
896
|
c.dashboard_user = ->(request) { { id:, name:, email: } or nil }
|
|
897
|
+
c.writer_socket = "tmp/sockets/railwatch-writer.sock" # RAILWATCH_WRITER_SOCKET
|
|
897
898
|
```
|
|
898
899
|
|
|
899
900
|
With `transport = :local` the reporter writes each batch into the app's
|
|
@@ -905,25 +906,48 @@ railwatch:authentication:configure` has written credentials; a host with
|
|
|
905
906
|
its own admin auth turns Basic off and sets `base_controller_class` or a
|
|
906
907
|
routes constraint. Full walkthrough: [Embedded mode](embedded.md).
|
|
907
908
|
|
|
909
|
+
### Export to Railwatch Cloud
|
|
910
|
+
|
|
911
|
+
An embedded install can also mirror every record to Railwatch Cloud.
|
|
912
|
+
Off unless `export_enabled` is set; a configured token alone sends
|
|
913
|
+
nothing. Only meaningful with `transport = :local`.
|
|
914
|
+
|
|
915
|
+
| Attribute | Env var | Default | Meaning |
|
|
916
|
+
|---|---|---|---|
|
|
917
|
+
| `export_enabled` | `RAILWATCH_EXPORT_ENABLED` | `false` | Mirror every record to the cloud as well as storing it locally. |
|
|
918
|
+
| `export_token` | `RAILWATCH_EXPORT_TOKEN` | `token` | Cloud environment token to send with. |
|
|
919
|
+
| `export_url` | `RAILWATCH_EXPORT_URL` | `{ingest_url}/ingest` | Where to send. |
|
|
920
|
+
| `export_max_bytes` | `RAILWATCH_EXPORT_MAX_BYTES` | `268435456` (256 MiB) | Queue ceiling in bytes. At capacity new work is refused and counted, not swapped for old. |
|
|
921
|
+
| `export_max_deliveries` | `RAILWATCH_EXPORT_MAX_DELIVERIES` | `100000` | Queue ceiling in deliveries. |
|
|
922
|
+
| `export_max_age` | `RAILWATCH_EXPORT_MAX_AGE_SECONDS` | `86400` | How long a queued delivery is kept. At most seven days, past which the receiver no longer recognises it. |
|
|
923
|
+
|
|
924
|
+
`railwatch:doctor` fails when export is enabled and cannot work, and
|
|
925
|
+
`railwatch:export:status` shows the queue. See
|
|
926
|
+
[Embedded mode](embedded.md#three-ways-to-run-it).
|
|
927
|
+
|
|
908
928
|
## Rake tasks
|
|
909
929
|
|
|
910
930
|
Ship with the gem via Rails::Engine's default `lib/tasks` convention, in
|
|
911
931
|
`lib/tasks/railwatch_tasks.rake`:
|
|
912
932
|
|
|
913
|
-
- **`railwatch:status`**
|
|
914
|
-
|
|
915
|
-
fails.
|
|
916
|
-
- **`railwatch:doctor`** prints a ✓/✗ checklist of the whole install
|
|
917
|
-
|
|
933
|
+
- **`railwatch:status`** embedded: prints that telemetry is stored in the
|
|
934
|
+
app. Cloud: pings `{ingest_url}/ingest/ping` with the configured token,
|
|
935
|
+
and aborts if `RAILWATCH_TOKEN` is unset or the ping fails.
|
|
936
|
+
- **`railwatch:doctor`** prints a ✓/✗ checklist of the whole install.
|
|
937
|
+
Embedded, it checks both databases and their migrations, telemetry
|
|
938
|
+
disk mode, the maintenance clock, the writer process, dashboard access,
|
|
939
|
+
and export when enabled. Cloud, it checks the token, where the token is
|
|
940
|
+
stored, the ingest URL and its transport security, and
|
|
941
|
+
`GET /ingest/ping`. Then, either way: `Railwatch::Middleware::Request`
|
|
918
942
|
in the middleware stack, the mounted engine's beacon route,
|
|
919
|
-
`config.deploy` and its
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
server, timestamp, performer, destination, service, commits}` to
|
|
943
|
+
`config.deploy` and its source, sample rates, ignored record types, the
|
|
944
|
+
Kamal `post-deploy` hook, the browser client and whether an entrypoint
|
|
945
|
+
calls it, the profiler backend, and the test matchers. It exits
|
|
946
|
+
non-zero only on the lines marked fatal in
|
|
947
|
+
[Troubleshooting](troubleshooting.md).
|
|
948
|
+
- **`railwatch:deploy[ref,name,url]`** embedded: writes the deploy marker
|
|
949
|
+
to the app's `railwatch` database. Cloud: POSTs `{deploy, ref, name,
|
|
950
|
+
url, server, timestamp, performer, destination, service, commits}` to
|
|
927
951
|
`{ingest_url}/ingest/deploys`. `deploy` comes from `config.deploy`. It
|
|
928
952
|
aborts if that's unset. `ref` defaults to `git rev-parse HEAD` when not
|
|
929
953
|
passed. `performer`/`destination`/`service` come from `KAMAL_PERFORMER`,
|
|
@@ -931,13 +955,30 @@ Ship with the gem via Rails::Engine's default `lib/tasks` convention, in
|
|
|
931
955
|
`{sha, author, message, at}` objects, newest first, from `git log`. It
|
|
932
956
|
is empty inside an app container, which has no `.git`. That is why the
|
|
933
957
|
hook below posts from the deployer instead.
|
|
958
|
+
- **`railwatch:authentication:configure`** writes the embedded
|
|
959
|
+
dashboard's HTTP Basic credentials to the current environment's Rails
|
|
960
|
+
credentials.
|
|
961
|
+
- **`railwatch:export:status`**, **`railwatch:export:rebind`**,
|
|
962
|
+
**`railwatch:export:discard`** show the export queue, clear a
|
|
963
|
+
credential block (abandoning work queued under the old token), and
|
|
964
|
+
abandon everything queued.
|
|
965
|
+
- **`railwatch:vacuum:status`** and **`railwatch:vacuum`** report and
|
|
966
|
+
reclaim the telemetry database's disk
|
|
967
|
+
([Embedded mode](embedded.md#giving-the-disk-back)).
|
|
968
|
+
- **`railwatch:token`** and **`railwatch:mcp`** print where to create a
|
|
969
|
+
cloud ingest token and paste-ready MCP client configuration.
|
|
970
|
+
- **`railwatch:sourcemaps[directory,delete]`** uploads browser source
|
|
971
|
+
maps ([Source maps](source-maps.md)).
|
|
934
972
|
|
|
935
973
|
## Kamal integration
|
|
936
974
|
|
|
937
975
|
`bin/rails generate railwatch:install` writes `.kamal/hooks/post-deploy`,
|
|
938
|
-
but only if `config/deploy.yml` already exists.
|
|
939
|
-
`
|
|
940
|
-
|
|
976
|
+
but only if `config/deploy.yml` already exists. With
|
|
977
|
+
`RAILWATCH_TRANSPORT=local` in the deployer's environment it runs
|
|
978
|
+
`bin/rails railwatch:deploy[$KAMAL_VERSION]` in the primary container,
|
|
979
|
+
which records the marker in the embedded database, and stops. Otherwise
|
|
980
|
+
it no-ops when `RAILWATCH_TOKEN` isn't set. It never fails a deploy.
|
|
981
|
+
Every network call ends in `|| true`.
|
|
941
982
|
|
|
942
983
|
The hook runs on the **deployer machine**, not in a container, which is
|
|
943
984
|
the whole point. That's where the git history lives and where Kamal
|