txray 0.1.0 → 0.2.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/CHANGELOG.md +19 -0
- data/CONTRIBUTING.md +43 -0
- data/README.md +110 -27
- data/Rakefile +1 -0
- data/doc/monitor.png +0 -0
- data/doc/monitor.svg +229 -0
- data/lib/generators/txray/install_generator.rb +28 -0
- data/lib/generators/txray/templates/txray.yml +37 -0
- data/lib/txray/cli.rb +3 -0
- data/lib/txray/config.rb +28 -1
- data/lib/txray/monitor.rb +25 -1
- data/lib/txray/reporters/github.rb +71 -5
- data/lib/txray/reporters/live.rb +157 -69
- data/lib/txray/reporters/text.rb +65 -16
- data/lib/txray/reporters.rb +4 -0
- data/lib/txray/runtime.rb +47 -20
- data/lib/txray/scanner.rb +14 -1
- data/lib/txray/source_file.rb +56 -10
- data/lib/txray/version.rb +1 -1
- metadata +7 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '04384fde1e534819ca3055d82a586c3da880b2ea97aa49d8fbe2c3eaa0182beb'
|
|
4
|
+
data.tar.gz: d8b1dd217628ad5a12780cd295d27bdb7c934b10907443d6afe95726925c62f2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4f2ed21c432a811623ddfae148e50cbdd54283b71fc663f5ca67750fdbe5710c5810ec6ee2c64bc9ce134de420b0195f7f30e8ab0c5c1c54cdcfc2522567791f
|
|
7
|
+
data.tar.gz: 4eab65e95a199496556e0569667b09e414205e0bd8f21fd25af58636ff21a3107ca33b413789a00a8f55d6e4470181b03811e711a02bb007a65d561dfa05b6cc
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.1
|
|
4
|
+
|
|
5
|
+
Nothing in the analyzer, the runtime guard or the reporters changed. This release exists to exercise the publishing pipeline on something low stakes before a release that matters.
|
|
6
|
+
|
|
7
|
+
- Releases are now published by GitHub Actions through RubyGems trusted publishing, so no API key is created, pasted or stored. Merging a version bump tags the commit, runs the suite, publishes the gem and creates the GitHub release with the notes taken from this file.
|
|
8
|
+
- Corrected the release steps in `CONTRIBUTING.md`, which told contributors to push a version bump straight to a protected `main`.
|
|
9
|
+
|
|
10
|
+
## 0.2.0
|
|
11
|
+
|
|
12
|
+
- Track clients held in local variables, instance variables, constants, memoized readers and `delegate` targets, so the request is reported rather than the harmless construction.
|
|
13
|
+
- Resolve `send` and `public_send` with a symbol literal, `define_method` bodies and concern `included` blocks; report genuinely dynamic dispatch as a visible low severity blind spot.
|
|
14
|
+
- Cover custom validations, `before_commit`, advisory locks and migration bodies as transactional scope, and add rules for broadcasts and blocking file, CSV and image work.
|
|
15
|
+
- RuboCop style directive regions: `# txray:disable RULE` ... `# txray:enable RULE`, `all`, and whole file suppression.
|
|
16
|
+
- Rebuilt live monitor with stacked meters, a latency histogram and severity colour, clamped to the terminal; `rails generate txray:install`; `NO_COLOR` support.
|
|
17
|
+
- Report paths relative to the working directory, and escape GitHub workflow commands correctly, so annotations and SARIF attach to the right file.
|
|
18
|
+
- `--format github` now writes a job summary table in addition to inline annotations.
|
|
19
|
+
- Runtime guard supports Rails 7.0 through 8.x, timing the transaction directly on versions that do not emit `start_transaction.active_record`.
|
|
20
|
+
- Clear errors for a malformed config, a missing path and an unparseable file, all of which previously reported a clean scan that never happened.
|
|
21
|
+
|
|
3
22
|
## 0.1.0
|
|
4
23
|
|
|
5
24
|
- Static transaction analysis over explicit transactions, row and advisory locks, save callbacks, custom validations and migrations.
|
data/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
```sh
|
|
4
|
+
bin/setup
|
|
5
|
+
bundle exec rake # specs and rubocop
|
|
6
|
+
```
|
|
7
|
+
|
|
8
|
+
## Adding a rule
|
|
9
|
+
|
|
10
|
+
A rule is three things and a test.
|
|
11
|
+
|
|
12
|
+
1. `Rules.register` in `lib/txray/rule.rb`, with the severity, what the reader sees, and the fix.
|
|
13
|
+
2. A branch in `lib/txray/classifier.rb`, or a namespace in `lib/txray/catalog.rb` if the call is recognised by its receiver.
|
|
14
|
+
3. A scenario in `spec/txray/scenarios_spec.rb` that proves it fires, and one that proves it stays quiet on the shape it should ignore.
|
|
15
|
+
|
|
16
|
+
The second test matters more than the first. A rule that fires on correct code costs more than a rule that misses.
|
|
17
|
+
|
|
18
|
+
## Adding a transactional scope
|
|
19
|
+
|
|
20
|
+
`lib/txray/transaction_scope.rb` decides what counts as being inside a transaction. Anything added there needs a case showing that the corresponding commit-time callback is still left alone.
|
|
21
|
+
|
|
22
|
+
## Testing against other Rails versions
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
RAILS_VERSION=7.1 bundle install && RAILS_VERSION=7.1 bundle exec rspec
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
CI runs 7.1, 7.2 and 8.0 alongside Ruby 3.2, 3.3 and 3.4.
|
|
29
|
+
|
|
30
|
+
## Before opening a pull request
|
|
31
|
+
|
|
32
|
+
Run the scanner against a real application, not only the suite. False positives are the failure mode that matters, and they show up in real code long before they show up in fixtures.
|
|
33
|
+
|
|
34
|
+
## Releasing
|
|
35
|
+
|
|
36
|
+
Merging a version bump releases the gem. There is nothing to run by hand.
|
|
37
|
+
|
|
38
|
+
1. On a branch, bump `Txray::VERSION` in `lib/txray/version.rb` and add a `## x.y.z` section to `CHANGELOG.md`.
|
|
39
|
+
2. Open the pull request and merge it once CI is green.
|
|
40
|
+
|
|
41
|
+
On every push to `main` the release workflow reads `Txray::VERSION` and stops immediately if that version is already tagged, so ordinary merges do nothing. When the version is new it takes the release notes from the matching changelog section, runs the suite, tags the commit, publishes the gem to RubyGems through trusted publishing, and creates the GitHub release with the notes and the built gem attached.
|
|
42
|
+
|
|
43
|
+
The tag is created locally and only pushed once the gem is published, so a failure leaves nothing behind: fix it and merge again. A missing changelog section fails before the suite even runs. No API key is involved anywhere.
|
data/README.md
CHANGED
|
@@ -16,17 +16,31 @@ app/models/order.rb
|
|
|
16
16
|
|
|
17
17
|
## Install
|
|
18
18
|
|
|
19
|
+
For the scanner alone:
|
|
20
|
+
|
|
19
21
|
```ruby
|
|
20
22
|
group :development, :test do
|
|
21
23
|
gem "txray", require: false
|
|
22
24
|
end
|
|
23
25
|
```
|
|
24
26
|
|
|
27
|
+
For the scanner **and** the runtime guard and live monitor, drop `require: false`, or the railtie never loads and the guard silently never installs:
|
|
28
|
+
|
|
29
|
+
```ruby
|
|
30
|
+
group :development, :test do
|
|
31
|
+
gem "txray"
|
|
32
|
+
end
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Then:
|
|
36
|
+
|
|
25
37
|
```sh
|
|
26
38
|
bundle exec txray
|
|
27
39
|
```
|
|
28
40
|
|
|
29
|
-
It exits non zero when it finds something, so it drops straight into CI.
|
|
41
|
+
It exits non zero when it finds something, so it drops straight into CI. No configuration is required; everything below has a default.
|
|
42
|
+
|
|
43
|
+
In a Rails app, `bin/rails generate txray:install` writes a commented `.txray.yml` and prints the next steps. Outside Rails, `bundle exec txray --init` writes the same file.
|
|
30
44
|
|
|
31
45
|
## What counts as a transaction
|
|
32
46
|
|
|
@@ -158,14 +172,35 @@ Exclusions apply to the path below the root being scanned, so `vendor` means the
|
|
|
158
172
|
|
|
159
173
|
### Inline suppression
|
|
160
174
|
|
|
175
|
+
Directives follow the same shape as RuboCop's. A trailing comment silences that line:
|
|
176
|
+
|
|
161
177
|
```ruby
|
|
162
178
|
def charge = Faraday.post(url) # txray:disable
|
|
179
|
+
def charge = Faraday.post(url) # txray:disable http-in-transaction
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
A comment on its own line opens a region that runs until the matching `enable`, or to the end of the file if there is none:
|
|
163
183
|
|
|
184
|
+
```ruby
|
|
164
185
|
# txray:disable http-in-transaction
|
|
165
186
|
Faraday.post(url)
|
|
187
|
+
Faraday.get(url)
|
|
188
|
+
# txray:enable http-in-transaction
|
|
166
189
|
```
|
|
167
190
|
|
|
168
|
-
|
|
191
|
+
Name several rules with commas, and re-enable them independently:
|
|
192
|
+
|
|
193
|
+
```ruby
|
|
194
|
+
# txray:disable http-in-transaction, job-enqueue-in-transaction
|
|
195
|
+
...
|
|
196
|
+
# txray:enable job-enqueue-in-transaction
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
A bare directive, or `all`, covers every rule. At the top of a file that silences the whole file:
|
|
200
|
+
|
|
201
|
+
```ruby
|
|
202
|
+
# txray:disable all
|
|
203
|
+
```
|
|
169
204
|
|
|
170
205
|
## Command line
|
|
171
206
|
|
|
@@ -189,19 +224,43 @@ Usage: txray [options] [paths]
|
|
|
189
224
|
## CI
|
|
190
225
|
|
|
191
226
|
```yaml
|
|
192
|
-
|
|
193
|
-
|
|
227
|
+
name: txray
|
|
228
|
+
|
|
229
|
+
on:
|
|
230
|
+
pull_request:
|
|
231
|
+
push:
|
|
232
|
+
branches: [ main ]
|
|
233
|
+
|
|
234
|
+
jobs:
|
|
235
|
+
transactions:
|
|
236
|
+
runs-on: ubuntu-latest
|
|
237
|
+
steps:
|
|
238
|
+
- uses: actions/checkout@v4
|
|
239
|
+
- uses: ruby/setup-ruby@v1
|
|
240
|
+
with:
|
|
241
|
+
bundler-cache: true
|
|
242
|
+
- name: Scan for slow transactions
|
|
243
|
+
run: bundle exec txray --format github
|
|
194
244
|
```
|
|
195
245
|
|
|
196
|
-
|
|
246
|
+
That one step does three things. It annotates the offending lines inline on the pull request, with the call path and the suggested fix in each annotation. It writes a table of every finding to the job summary, so the run page carries the full list even though GitHub caps how many annotations it will render for a single step. And it exits non zero, so the check fails.
|
|
247
|
+
|
|
248
|
+
Use `--fail-level high` to annotate everything but only fail the build on the high severity rules, or `--fail-level none` to report without ever failing while you work through a backlog.
|
|
249
|
+
|
|
250
|
+
To send findings to GitHub code scanning instead, so they appear in the Security tab and track across branches:
|
|
197
251
|
|
|
198
252
|
```yaml
|
|
199
|
-
- run: bundle exec txray --format sarif > txray.sarif
|
|
200
|
-
-
|
|
201
|
-
|
|
202
|
-
|
|
253
|
+
- run: bundle exec txray --format sarif > txray.sarif
|
|
254
|
+
continue-on-error: true
|
|
255
|
+
- uses: github/codeql-action/upload-sarif@v3
|
|
256
|
+
with:
|
|
257
|
+
sarif_file: txray.sarif
|
|
203
258
|
```
|
|
204
259
|
|
|
260
|
+
That job needs `permissions: security-events: write`. `continue-on-error` matters: without it a failing scan skips the upload step, and the findings never arrive.
|
|
261
|
+
|
|
262
|
+
Paths are reported relative to the working directory in every format, which is what GitHub needs to match a finding to a file in the repository.
|
|
263
|
+
|
|
205
264
|
## Runtime guard
|
|
206
265
|
|
|
207
266
|
Static analysis cannot see through metaprogramming, gem internals or a client handed in as an argument. The optional runtime guard catches what is left, from inside a running application:
|
|
@@ -215,6 +274,10 @@ runtime:
|
|
|
215
274
|
|
|
216
275
|
It reports a transaction that stays open past the threshold, a job enqueued while a transaction is open, mail delivered while a transaction is open, and any `Net::HTTP` request made while a transaction is open (which covers Faraday, HTTParty, RestClient, Octokit and everything else built on it). Each finding is attributed to the transaction that was open, with the duration of the offending call and the application frame that caused it. `on_violation: log` warns, `raise` fails loudly. Run it as `raise` in test and `log` in development.
|
|
217
276
|
|
|
277
|
+
### Rails versions
|
|
278
|
+
|
|
279
|
+
The analyzer has no Rails dependency at all. The runtime guard supports **Rails 7.0 through 8.x** and picks its hook accordingly. From Rails 7.2 it subscribes to the `start_transaction.active_record` and `transaction.active_record` notifications. Rails 7.0 and 7.1 do not emit `start_transaction`, and the duration carried by `transaction.active_record` is not the duration of the transaction, so on those versions the guard times `within_new_transaction` itself. Both paths report the same real duration, attribute findings to the transaction that was open, and record the commit or rollback outcome; both are covered by the test suite and exercised in CI against 7.1, 7.2 and 8.0.
|
|
280
|
+
|
|
218
281
|
Known-good work can be excused:
|
|
219
282
|
|
|
220
283
|
```ruby
|
|
@@ -229,30 +292,48 @@ end
|
|
|
229
292
|
|
|
230
293
|
The guard writes newline delimited JSON to `runtime.log_path`. `txray watch` tails it and renders what your application is doing right now:
|
|
231
294
|
|
|
232
|
-
|
|
233
|
-
txray watching tmp/txray.ndjson 00:04:12
|
|
295
|
+

|
|
234
296
|
|
|
235
|
-
|
|
236
|
-
duration p50 12ms p95 240ms max 1.84s
|
|
237
|
-
▁▁▂▁▁▁▁▁▁▂▁▁▁▁▁▅▁▁▁▁▁▂▁▁▁▁▁▁▁▁▂█
|
|
297
|
+
The `txns` meter is a stacked bar of clean, slow and flagged transactions. The `time` row is a log scaled sparkline of recent durations next to p50, p95 and max, all coloured against your threshold. Below that is a latency histogram, then the live feed, where findings hang under the transaction that produced them, so you see the 1.84 second transaction and the two calls that account for most of it. Hotspots rank what keeps happening. Everything is coloured by rule severity and clipped to your terminal, down to 60 columns.
|
|
238
298
|
|
|
239
|
-
|
|
240
|
-
14:02:11 1.84s x app/models/order.rb:31 settle
|
|
241
|
-
| http-in-transaction POST api.stripe.com/v1/charges (1.61s)
|
|
242
|
-
| mail-in-transaction ReceiptMailer delivered mail (180ms)
|
|
243
|
-
14:02:10 268ms ! app/services/checkout.rb:12 call
|
|
244
|
-
14:02:09 11ms . app/models/photo.rb:8 save
|
|
299
|
+
### Turning it on
|
|
245
300
|
|
|
246
|
-
|
|
247
|
-
3x http-in-transaction app/models/order.rb:34
|
|
248
|
-
1x job-enqueue-in-transaction app/models/photo.rb:8
|
|
301
|
+
Three steps, in this order:
|
|
249
302
|
|
|
250
|
-
|
|
251
|
-
|
|
303
|
+
1. **Gemfile** must not say `require: false` (see Install above), or nothing is ever written.
|
|
304
|
+
2. **Enable the guard**, in `.txray.yml`:
|
|
252
305
|
|
|
253
|
-
|
|
306
|
+
```yaml
|
|
307
|
+
runtime:
|
|
308
|
+
enabled: true
|
|
309
|
+
threshold_ms: 250
|
|
310
|
+
```
|
|
254
311
|
|
|
255
|
-
|
|
312
|
+
or in `config/environments/development.rb`:
|
|
313
|
+
|
|
314
|
+
```ruby
|
|
315
|
+
config.txray.enabled = true
|
|
316
|
+
config.txray.threshold_ms = 250
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
3. **Restart the app**, then run the two side by side:
|
|
320
|
+
|
|
321
|
+
```
|
|
322
|
+
terminal 1: bin/dev
|
|
323
|
+
terminal 2: bundle exec txray watch
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
Exercise the app and transactions appear as they close. Only transactions that ran past the threshold or carried a finding are recorded, so the feed stays quiet until something is actually wrong. If the log does not exist yet, `txray watch` says so and repeats these steps on screen rather than sitting blank.
|
|
327
|
+
|
|
328
|
+
| Flag | Effect |
|
|
329
|
+
| --- | --- |
|
|
330
|
+
| `--from-start` | replay the existing log instead of only new events |
|
|
331
|
+
| `--threshold 100` | lower the bar to see more |
|
|
332
|
+
| `--file PATH` | watch a different log |
|
|
333
|
+
|
|
334
|
+
Ctrl-C prints a summary of the session. Piped to a file or another process it emits the raw event stream instead of the dashboard, so `txray watch | jq` works.
|
|
335
|
+
|
|
336
|
+
The log is append only with an exclusive lock per write, so several Puma workers and Sidekiq processes can share one file. It is newline delimited JSON rather than a text log because the watcher reads it incrementally while it is being written: one line is one complete record, so a partial read is never a partial event, concurrent writers cannot interleave into one record, and the nested findings and durations survive without parsing prose. It is still a plain text file, so `tail -f tmp/txray.ndjson` works.
|
|
256
337
|
|
|
257
338
|
## How this differs from what already exists
|
|
258
339
|
|
|
@@ -269,6 +350,8 @@ bin/setup
|
|
|
269
350
|
bundle exec rake
|
|
270
351
|
```
|
|
271
352
|
|
|
353
|
+
Releases are cut by tagging; see [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
354
|
+
|
|
272
355
|
## License
|
|
273
356
|
|
|
274
357
|
MIT
|
data/Rakefile
CHANGED
data/doc/monitor.png
ADDED
|
Binary file
|
data/doc/monitor.svg
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="741" height="617" viewBox="0 0 741 617" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" font-size="13">
|
|
2
|
+
<rect width="741" height="617" rx="8" fill="#16181d"/>
|
|
3
|
+
<rect x="14.0" y="13.5" width="713.0" height="19.0" fill="#3a3f4b"/>
|
|
4
|
+
<text x="14.0" y="27.0" xml:space="preserve" fill="#f5f6f8" font-weight="700"> txray tmp/txray.ndjson up 00:04:12 2 pids </text>
|
|
5
|
+
<text x="14.0" y="65.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
6
|
+
<text x="29.5" y="65.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62">txns </text>
|
|
7
|
+
<text x="76.0" y="65.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62">[</text>
|
|
8
|
+
<text x="83.75" y="65.0" xml:space="preserve" fill="#5ddc9a">████████████████████████████████</text>
|
|
9
|
+
<text x="331.75" y="65.0" xml:space="preserve" fill="#e5c07b">█</text>
|
|
10
|
+
<text x="339.5" y="65.0" xml:space="preserve" fill="#ff6b6b">█</text>
|
|
11
|
+
<text x="347.25" y="65.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62">]</text>
|
|
12
|
+
<text x="355.0" y="65.0" xml:space="preserve" fill="#c8ccd4"> 123 txns</text>
|
|
13
|
+
<text x="432.5" y="65.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62"> · </text>
|
|
14
|
+
<text x="455.75" y="65.0" xml:space="preserve" fill="#5ddc9a">120 ok</text>
|
|
15
|
+
<text x="502.25" y="65.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62"> · </text>
|
|
16
|
+
<text x="525.5" y="65.0" xml:space="preserve" fill="#e5c07b">1 slow</text>
|
|
17
|
+
<text x="572.0" y="65.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62"> · </text>
|
|
18
|
+
<text x="595.25" y="65.0" xml:space="preserve" fill="#ff6b6b">2 flagged</text>
|
|
19
|
+
<text x="14.0" y="84.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
20
|
+
<text x="29.5" y="84.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62">time </text>
|
|
21
|
+
<text x="76.0" y="84.0" xml:space="preserve" fill="#5ddc9a">▃</text>
|
|
22
|
+
<text x="83.75" y="84.0" xml:space="preserve" fill="#5ddc9a">▃</text>
|
|
23
|
+
<text x="91.5" y="84.0" xml:space="preserve" fill="#5ddc9a">▄</text>
|
|
24
|
+
<text x="99.25" y="84.0" xml:space="preserve" fill="#5ddc9a">▅</text>
|
|
25
|
+
<text x="107.0" y="84.0" xml:space="preserve" fill="#5ddc9a">▃</text>
|
|
26
|
+
<text x="114.75" y="84.0" xml:space="preserve" fill="#5ddc9a">▄</text>
|
|
27
|
+
<text x="122.5" y="84.0" xml:space="preserve" fill="#5ddc9a">▃</text>
|
|
28
|
+
<text x="130.25" y="84.0" xml:space="preserve" fill="#5ddc9a">▃</text>
|
|
29
|
+
<text x="138.0" y="84.0" xml:space="preserve" fill="#5ddc9a">▃</text>
|
|
30
|
+
<text x="145.75" y="84.0" xml:space="preserve" fill="#5ddc9a">▄</text>
|
|
31
|
+
<text x="153.5" y="84.0" xml:space="preserve" fill="#5ddc9a">▅</text>
|
|
32
|
+
<text x="161.25" y="84.0" xml:space="preserve" fill="#5ddc9a">▃</text>
|
|
33
|
+
<text x="169.0" y="84.0" xml:space="preserve" fill="#5ddc9a">▃</text>
|
|
34
|
+
<text x="176.75" y="84.0" xml:space="preserve" fill="#5ddc9a">▅</text>
|
|
35
|
+
<text x="184.5" y="84.0" xml:space="preserve" fill="#5ddc9a">▄</text>
|
|
36
|
+
<text x="192.25" y="84.0" xml:space="preserve" fill="#5ddc9a">▅</text>
|
|
37
|
+
<text x="200.0" y="84.0" xml:space="preserve" fill="#5ddc9a">▄</text>
|
|
38
|
+
<text x="207.75" y="84.0" xml:space="preserve" fill="#5ddc9a">▄</text>
|
|
39
|
+
<text x="215.5" y="84.0" xml:space="preserve" fill="#5ddc9a">▃</text>
|
|
40
|
+
<text x="223.25" y="84.0" xml:space="preserve" fill="#5ddc9a">▄</text>
|
|
41
|
+
<text x="231.0" y="84.0" xml:space="preserve" fill="#5ddc9a">▅</text>
|
|
42
|
+
<text x="238.75" y="84.0" xml:space="preserve" fill="#5ddc9a">▂</text>
|
|
43
|
+
<text x="246.5" y="84.0" xml:space="preserve" fill="#5ddc9a">▃</text>
|
|
44
|
+
<text x="254.25" y="84.0" xml:space="preserve" fill="#5ddc9a">▂</text>
|
|
45
|
+
<text x="262.0" y="84.0" xml:space="preserve" fill="#5ddc9a">▄</text>
|
|
46
|
+
<text x="269.75" y="84.0" xml:space="preserve" fill="#5ddc9a">▅</text>
|
|
47
|
+
<text x="277.5" y="84.0" xml:space="preserve" fill="#5ddc9a">▄</text>
|
|
48
|
+
<text x="285.25" y="84.0" xml:space="preserve" fill="#5ddc9a">▄</text>
|
|
49
|
+
<text x="293.0" y="84.0" xml:space="preserve" fill="#5ddc9a">▃</text>
|
|
50
|
+
<text x="300.75" y="84.0" xml:space="preserve" fill="#5ddc9a">▄</text>
|
|
51
|
+
<text x="308.5" y="84.0" xml:space="preserve" fill="#5ddc9a">▃</text>
|
|
52
|
+
<text x="316.25" y="84.0" xml:space="preserve" fill="#ff6b6b">▆</text>
|
|
53
|
+
<text x="324.0" y="84.0" xml:space="preserve" fill="#ff6b6b">▇</text>
|
|
54
|
+
<text x="331.75" y="84.0" xml:space="preserve" fill="#ff6b6b">█</text>
|
|
55
|
+
<text x="339.5" y="84.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
56
|
+
<text x="355.0" y="84.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62">p50</text>
|
|
57
|
+
<text x="378.25" y="84.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
58
|
+
<text x="386.0" y="84.0" xml:space="preserve" fill="#5ddc9a">18ms</text>
|
|
59
|
+
<text x="417.0" y="84.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62"> · </text>
|
|
60
|
+
<text x="440.25" y="84.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62">p95</text>
|
|
61
|
+
<text x="463.5" y="84.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
62
|
+
<text x="471.25" y="84.0" xml:space="preserve" fill="#e5c07b">140ms</text>
|
|
63
|
+
<text x="510.0" y="84.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62"> · </text>
|
|
64
|
+
<text x="533.25" y="84.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62">max</text>
|
|
65
|
+
<text x="556.5" y="84.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
66
|
+
<text x="564.25" y="84.0" xml:space="preserve" fill="#ff6b6b">1.84s</text>
|
|
67
|
+
<text x="14.0" y="122.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
68
|
+
<text x="29.5" y="122.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62"> <10ms</text>
|
|
69
|
+
<text x="83.75" y="122.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
70
|
+
<text x="91.5" y="122.0" xml:space="preserve" fill="#5ddc9a">███████████████████████████████</text>
|
|
71
|
+
<text x="331.75" y="122.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
72
|
+
<text x="339.5" y="122.0" xml:space="preserve" fill="#f0f0f0">45 </text>
|
|
73
|
+
<text x="370.5" y="122.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
74
|
+
<text x="386.0" y="122.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62"> <50ms</text>
|
|
75
|
+
<text x="440.25" y="122.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
76
|
+
<text x="448.0" y="122.0" xml:space="preserve" fill="#5ddc9a">█████████████████████████████</text>
|
|
77
|
+
<text x="672.75" y="122.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62">··</text>
|
|
78
|
+
<text x="688.25" y="122.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
79
|
+
<text x="696.0" y="122.0" xml:space="preserve" fill="#f0f0f0">42 </text>
|
|
80
|
+
<text x="14.0" y="141.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
81
|
+
<text x="29.5" y="141.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62"> <100ms</text>
|
|
82
|
+
<text x="83.75" y="141.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
83
|
+
<text x="91.5" y="141.0" xml:space="preserve" fill="#5ddc9a">███████████████████</text>
|
|
84
|
+
<text x="238.75" y="141.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62">············</text>
|
|
85
|
+
<text x="331.75" y="141.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
86
|
+
<text x="339.5" y="141.0" xml:space="preserve" fill="#f0f0f0">27 </text>
|
|
87
|
+
<text x="370.5" y="141.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
88
|
+
<text x="386.0" y="141.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62"> <250ms</text>
|
|
89
|
+
<text x="440.25" y="141.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
90
|
+
<text x="448.0" y="141.0" xml:space="preserve" fill="#e5c07b">████</text>
|
|
91
|
+
<text x="479.0" y="141.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62">···························</text>
|
|
92
|
+
<text x="688.25" y="141.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
93
|
+
<text x="696.0" y="141.0" xml:space="preserve" fill="#f0f0f0">6 </text>
|
|
94
|
+
<text x="14.0" y="160.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
95
|
+
<text x="29.5" y="160.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62"> <1s</text>
|
|
96
|
+
<text x="83.75" y="160.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
97
|
+
<text x="91.5" y="160.0" xml:space="preserve" fill="#ff6b6b">█</text>
|
|
98
|
+
<text x="99.25" y="160.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62">······························</text>
|
|
99
|
+
<text x="331.75" y="160.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
100
|
+
<text x="339.5" y="160.0" xml:space="preserve" fill="#f0f0f0">2 </text>
|
|
101
|
+
<text x="370.5" y="160.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
102
|
+
<text x="386.0" y="160.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62"> 1s+</text>
|
|
103
|
+
<text x="440.25" y="160.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
104
|
+
<text x="448.0" y="160.0" xml:space="preserve" fill="#ff6b6b">█</text>
|
|
105
|
+
<text x="455.75" y="160.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62">······························</text>
|
|
106
|
+
<text x="688.25" y="160.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
107
|
+
<text x="696.0" y="160.0" xml:space="preserve" fill="#f0f0f0">1 </text>
|
|
108
|
+
<rect x="14.0" y="184.5" width="713.0" height="19.0" fill="#3a3f4b"/>
|
|
109
|
+
<text x="14.0" y="198.0" xml:space="preserve" fill="#f5f6f8" font-weight="700"> TIME ELAPSED SOURCE </text>
|
|
110
|
+
<text x="14.0" y="217.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
111
|
+
<text x="29.5" y="217.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62">14:12:13</text>
|
|
112
|
+
<text x="91.5" y="217.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
113
|
+
<text x="99.25" y="217.0" xml:space="preserve" fill="#ff6b6b"> 1.84s</text>
|
|
114
|
+
<text x="169.0" y="217.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
115
|
+
<text x="176.75" y="217.0" xml:space="preserve" fill="#ff6b6b">●</text>
|
|
116
|
+
<text x="184.5" y="217.0" xml:space="preserve" fill="#c8ccd4"> app/models/order.rb:31 settle</text>
|
|
117
|
+
<text x="14.0" y="236.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
118
|
+
<text x="184.5" y="236.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62">└</text>
|
|
119
|
+
<text x="192.25" y="236.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
120
|
+
<text x="200.0" y="236.0" xml:space="preserve" fill="#ff6b6b">http-in-transaction</text>
|
|
121
|
+
<text x="347.25" y="236.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
122
|
+
<text x="355.0" y="236.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62">POST api.stripe.com/v1/charges (1.61s)</text>
|
|
123
|
+
<text x="14.0" y="255.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
124
|
+
<text x="184.5" y="255.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62">└</text>
|
|
125
|
+
<text x="192.25" y="255.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
126
|
+
<text x="200.0" y="255.0" xml:space="preserve" fill="#e5c07b">job-enqueue-in-transaction</text>
|
|
127
|
+
<text x="401.5" y="255.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
128
|
+
<text x="409.25" y="255.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62">ShipJob enqueued</text>
|
|
129
|
+
<text x="14.0" y="274.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
130
|
+
<text x="29.5" y="274.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62">14:12:04</text>
|
|
131
|
+
<text x="91.5" y="274.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
132
|
+
<text x="99.25" y="274.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62"> -</text>
|
|
133
|
+
<text x="169.0" y="274.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
134
|
+
<text x="176.75" y="274.0" xml:space="preserve" fill="#56b6c2">●</text>
|
|
135
|
+
<text x="184.5" y="274.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
136
|
+
<text x="192.25" y="274.0" xml:space="preserve" fill="#56b6c2">cache-in-transaction</text>
|
|
137
|
+
<text x="347.25" y="274.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
138
|
+
<text x="355.0" y="274.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62">app/models/album.rb:19</text>
|
|
139
|
+
<text x="14.0" y="293.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
140
|
+
<text x="29.5" y="293.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62">14:11:54</text>
|
|
141
|
+
<text x="91.5" y="293.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
142
|
+
<text x="99.25" y="293.0" xml:space="preserve" fill="#ff6b6b"> 612ms</text>
|
|
143
|
+
<text x="169.0" y="293.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
144
|
+
<text x="176.75" y="293.0" xml:space="preserve" fill="#ff6b6b">●</text>
|
|
145
|
+
<text x="184.5" y="293.0" xml:space="preserve" fill="#c8ccd4"> app/models/photo.rb:44 attach_original</text>
|
|
146
|
+
<text x="14.0" y="312.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
147
|
+
<text x="184.5" y="312.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62">└</text>
|
|
148
|
+
<text x="192.25" y="312.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
149
|
+
<text x="200.0" y="312.0" xml:space="preserve" fill="#e5c07b">upload-in-transaction</text>
|
|
150
|
+
<text x="362.75" y="312.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
151
|
+
<text x="370.5" y="312.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62">original.attach(io: file) (540ms)</text>
|
|
152
|
+
<text x="14.0" y="331.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
153
|
+
<text x="29.5" y="331.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62">14:11:36</text>
|
|
154
|
+
<text x="91.5" y="331.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
155
|
+
<text x="99.25" y="331.0" xml:space="preserve" fill="#ff6b6b"> 268ms</text>
|
|
156
|
+
<text x="169.0" y="331.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
157
|
+
<text x="176.75" y="331.0" xml:space="preserve" fill="#e5c07b">●</text>
|
|
158
|
+
<text x="184.5" y="331.0" xml:space="preserve" fill="#c8ccd4"> app/services/checkout.rb:12 call</text>
|
|
159
|
+
<text x="14.0" y="350.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
160
|
+
<text x="29.5" y="350.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62">14:09:15</text>
|
|
161
|
+
<text x="91.5" y="350.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
162
|
+
<text x="99.25" y="350.0" xml:space="preserve" fill="#5ddc9a"> 9ms</text>
|
|
163
|
+
<text x="169.0" y="350.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
164
|
+
<text x="176.75" y="350.0" xml:space="preserve" fill="#5ddc9a">·</text>
|
|
165
|
+
<text x="184.5" y="350.0" xml:space="preserve" fill="#c8ccd4"> app/models/photo.rb:8 save</text>
|
|
166
|
+
<text x="14.0" y="369.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
167
|
+
<text x="29.5" y="369.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62">14:09:14</text>
|
|
168
|
+
<text x="91.5" y="369.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
169
|
+
<text x="99.25" y="369.0" xml:space="preserve" fill="#5ddc9a"> 18ms</text>
|
|
170
|
+
<text x="169.0" y="369.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
171
|
+
<text x="176.75" y="369.0" xml:space="preserve" fill="#5ddc9a">·</text>
|
|
172
|
+
<text x="184.5" y="369.0" xml:space="preserve" fill="#c8ccd4"> app/models/photo.rb:8 save</text>
|
|
173
|
+
<text x="14.0" y="388.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
174
|
+
<text x="29.5" y="388.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62">14:09:13</text>
|
|
175
|
+
<text x="91.5" y="388.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
176
|
+
<text x="99.25" y="388.0" xml:space="preserve" fill="#5ddc9a"> 9ms</text>
|
|
177
|
+
<text x="169.0" y="388.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
178
|
+
<text x="176.75" y="388.0" xml:space="preserve" fill="#5ddc9a">·</text>
|
|
179
|
+
<text x="184.5" y="388.0" xml:space="preserve" fill="#c8ccd4"> app/models/photo.rb:8 save</text>
|
|
180
|
+
<text x="14.0" y="407.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
181
|
+
<text x="29.5" y="407.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62">14:09:12</text>
|
|
182
|
+
<text x="91.5" y="407.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
183
|
+
<text x="99.25" y="407.0" xml:space="preserve" fill="#5ddc9a"> 41ms</text>
|
|
184
|
+
<text x="169.0" y="407.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
185
|
+
<text x="176.75" y="407.0" xml:space="preserve" fill="#5ddc9a">·</text>
|
|
186
|
+
<text x="184.5" y="407.0" xml:space="preserve" fill="#c8ccd4"> app/models/photo.rb:8 save</text>
|
|
187
|
+
<text x="14.0" y="426.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
188
|
+
<text x="29.5" y="426.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62">14:09:11</text>
|
|
189
|
+
<text x="91.5" y="426.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
190
|
+
<text x="99.25" y="426.0" xml:space="preserve" fill="#5ddc9a"> 18ms</text>
|
|
191
|
+
<text x="169.0" y="426.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
192
|
+
<text x="176.75" y="426.0" xml:space="preserve" fill="#5ddc9a">·</text>
|
|
193
|
+
<text x="184.5" y="426.0" xml:space="preserve" fill="#c8ccd4"> app/models/photo.rb:8 save</text>
|
|
194
|
+
<text x="14.0" y="445.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
195
|
+
<text x="29.5" y="445.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62">14:09:10</text>
|
|
196
|
+
<text x="91.5" y="445.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
197
|
+
<text x="99.25" y="445.0" xml:space="preserve" fill="#5ddc9a"> 63ms</text>
|
|
198
|
+
<text x="169.0" y="445.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
199
|
+
<text x="176.75" y="445.0" xml:space="preserve" fill="#5ddc9a">·</text>
|
|
200
|
+
<text x="184.5" y="445.0" xml:space="preserve" fill="#c8ccd4"> app/models/photo.rb:8 save</text>
|
|
201
|
+
<rect x="14.0" y="469.5" width="713.0" height="19.0" fill="#3a3f4b"/>
|
|
202
|
+
<text x="14.0" y="483.0" xml:space="preserve" fill="#f5f6f8" font-weight="700"> HOTSPOTS </text>
|
|
203
|
+
<text x="14.0" y="502.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
204
|
+
<text x="29.5" y="502.0" xml:space="preserve" fill="#f0f0f0"> 1x</text>
|
|
205
|
+
<text x="68.25" y="502.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
206
|
+
<text x="83.75" y="502.0" xml:space="preserve" fill="#56b6c2">cache-in-transaction </text>
|
|
207
|
+
<text x="331.75" y="502.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
208
|
+
<text x="339.5" y="502.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62">app/models/album.rb:19</text>
|
|
209
|
+
<text x="14.0" y="521.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
210
|
+
<text x="29.5" y="521.0" xml:space="preserve" fill="#f0f0f0"> 1x</text>
|
|
211
|
+
<text x="68.25" y="521.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
212
|
+
<text x="83.75" y="521.0" xml:space="preserve" fill="#e5c07b">upload-in-transaction </text>
|
|
213
|
+
<text x="331.75" y="521.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
214
|
+
<text x="339.5" y="521.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62">app/models/photo.rb:46</text>
|
|
215
|
+
<text x="14.0" y="540.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
216
|
+
<text x="29.5" y="540.0" xml:space="preserve" fill="#f0f0f0"> 1x</text>
|
|
217
|
+
<text x="68.25" y="540.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
218
|
+
<text x="83.75" y="540.0" xml:space="preserve" fill="#ff6b6b">http-in-transaction </text>
|
|
219
|
+
<text x="331.75" y="540.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
220
|
+
<text x="339.5" y="540.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62">app/models/order.rb:34</text>
|
|
221
|
+
<text x="14.0" y="559.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
222
|
+
<text x="29.5" y="559.0" xml:space="preserve" fill="#f0f0f0"> 1x</text>
|
|
223
|
+
<text x="68.25" y="559.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
224
|
+
<text x="83.75" y="559.0" xml:space="preserve" fill="#e5c07b">job-enqueue-in-transaction </text>
|
|
225
|
+
<text x="331.75" y="559.0" xml:space="preserve" fill="#c8ccd4"> </text>
|
|
226
|
+
<text x="339.5" y="559.0" xml:space="preserve" fill="#c8ccd4" opacity="0.62">app/models/order.rb:38</text>
|
|
227
|
+
<rect x="14.0" y="583.5" width="713.0" height="19.0" fill="#3a3f4b"/>
|
|
228
|
+
<text x="14.0" y="597.0" xml:space="preserve" fill="#f5f6f8" font-weight="700"> ctrl-c stop </text>
|
|
229
|
+
</svg>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators/base"
|
|
4
|
+
|
|
5
|
+
module Txray
|
|
6
|
+
module Generators
|
|
7
|
+
class InstallGenerator < Rails::Generators::Base
|
|
8
|
+
source_root File.expand_path("templates", __dir__)
|
|
9
|
+
|
|
10
|
+
desc "Writes .txray.yml and explains how to turn the runtime guard on."
|
|
11
|
+
|
|
12
|
+
def create_config
|
|
13
|
+
copy_file "txray.yml", ".txray.yml"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def explain
|
|
17
|
+
say ""
|
|
18
|
+
say "Scan the application:", :green
|
|
19
|
+
say " bundle exec txray"
|
|
20
|
+
say ""
|
|
21
|
+
say "To watch transactions live, set runtime.enabled to true in .txray.yml,", :green
|
|
22
|
+
say "make sure the Gemfile does not say require: false, restart, then run:"
|
|
23
|
+
say " bundle exec txray watch"
|
|
24
|
+
say ""
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Where to look. Paths are relative to the project root.
|
|
2
|
+
include:
|
|
3
|
+
- app
|
|
4
|
+
- lib
|
|
5
|
+
- db/migrate
|
|
6
|
+
exclude:
|
|
7
|
+
- spec
|
|
8
|
+
- test
|
|
9
|
+
- vendor
|
|
10
|
+
|
|
11
|
+
# How far to follow a call out of a transaction before giving up.
|
|
12
|
+
max_depth: 3
|
|
13
|
+
|
|
14
|
+
# Exit non zero at or above this severity: low, medium, high or none.
|
|
15
|
+
fail_level: low
|
|
16
|
+
|
|
17
|
+
# Rules to turn off entirely. `txray --rules` lists them all.
|
|
18
|
+
disabled_rules: []
|
|
19
|
+
|
|
20
|
+
# Raise or lower a rule without turning it off.
|
|
21
|
+
severities: {}
|
|
22
|
+
|
|
23
|
+
# Your own service wrappers. Any constant under these is treated as a
|
|
24
|
+
# third party client, so a call on one inside a transaction is reported.
|
|
25
|
+
external_clients: []
|
|
26
|
+
|
|
27
|
+
# The runtime guard. Off by default. It reports transactions that run long
|
|
28
|
+
# and the HTTP, mail and job work that happens while one is open, and writes
|
|
29
|
+
# the event log that `txray watch` reads.
|
|
30
|
+
#
|
|
31
|
+
# The gem must NOT be `require: false` in your Gemfile for this to load.
|
|
32
|
+
runtime:
|
|
33
|
+
enabled: false
|
|
34
|
+
threshold_ms: 250
|
|
35
|
+
on_violation: log # log, or raise to fail loudly in test
|
|
36
|
+
log_path: tmp/txray.ndjson
|
|
37
|
+
ignore: []
|
data/lib/txray/cli.rb
CHANGED
|
@@ -63,6 +63,9 @@ module Txray
|
|
|
63
63
|
|
|
64
64
|
File.write(Config::FILENAME, YAML.dump(Config::DEFAULTS))
|
|
65
65
|
@io.puts "created #{Config::FILENAME}"
|
|
66
|
+
@io.puts
|
|
67
|
+
@io.puts "scan with: bundle exec txray"
|
|
68
|
+
@io.puts "watch transactions: set runtime.enabled to true, restart, then bundle exec txray watch"
|
|
66
69
|
EXIT_CLEAN
|
|
67
70
|
end
|
|
68
71
|
|