request_trail 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +24 -1
- data/README.md +70 -7
- data/ROADMAP.md +2 -26
- data/lib/request_trail/collector.rb +9 -1
- data/lib/request_trail/configuration.rb +5 -1
- data/lib/request_trail/formatter.rb +24 -2
- data/lib/request_trail/formatters/flame_graph.rb +80 -0
- data/lib/request_trail/subscriber.rb +12 -1
- data/lib/request_trail/version.rb +1 -1
- data/lib/request_trail.rb +2 -2
- data/sig/request_trail/collector.rbs +5 -0
- data/sig/request_trail/configuration.rbs +2 -0
- data/sig/request_trail/formatter.rbs +8 -0
- data/sig/request_trail/formatters/flame_graph.rbs +23 -0
- data/sig/request_trail/subscriber.rbs +2 -0
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ad0751f86ff717630ea62f478707c758fc9d45540721c54016a55e19c86030be
|
|
4
|
+
data.tar.gz: 6192f331657a761ee7d537c29ded481629cfedfb1da74eb819e9181677010036
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 45d23c714ab2cebe3d5d33db4e8832301e69e496d3cd27419c370eef734bf8581c7d9f815485ab595acf7ce95d9c82c07d580eca805386526edbcd21bfffc74b
|
|
7
|
+
data.tar.gz: da3154bce7516976fdbff6437b877d639cf9b235a3308cb7101462d63e368b5b64530b932f101975f785cbb18365448e0ef18fbad84c2a5e2d8a3c9db126249c
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.4.0] - 2026-06-12
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- `RequestTrail::Formatters::FlameGraph` — opt-in ASCII flame-graph formatter with proportional `█` bars and per-layer ANSI colour (auto-detected via TTY)
|
|
8
|
+
- Opt in via `config.formatter = RequestTrail::Formatters::FlameGraph.new`
|
|
9
|
+
|
|
10
|
+
## [0.3.0] - 2026-06-12
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Controller and view tracing via `process_action.action_controller` — records total controller duration and view runtime per request
|
|
15
|
+
- Formatter switches to tiered multi-line output when controller data is present:
|
|
16
|
+
```
|
|
17
|
+
[RequestTrail] GET /orders 142ms
|
|
18
|
+
controller 104ms
|
|
19
|
+
sql 38ms (7 queries)
|
|
20
|
+
cache 2ms (4 hits, 1 miss)
|
|
21
|
+
view 22ms
|
|
22
|
+
```
|
|
23
|
+
|
|
3
24
|
## [0.2.0] - 2026-06-11
|
|
4
25
|
|
|
5
26
|
### Added
|
|
@@ -19,6 +40,8 @@
|
|
|
19
40
|
- `RequestTrail::Subscriber` — attach/detach API for notification subscriptions
|
|
20
41
|
- `RequestTrail::Collector` — thread-safe per-request event accumulator
|
|
21
42
|
|
|
22
|
-
[Unreleased]: https://github.com/eclectic-coding/request-trail/compare/v0.
|
|
43
|
+
[Unreleased]: https://github.com/eclectic-coding/request-trail/compare/v0.4.0...HEAD
|
|
44
|
+
[0.4.0]: https://github.com/eclectic-coding/request-trail/releases/tag/v0.4.0
|
|
45
|
+
[0.3.0]: https://github.com/eclectic-coding/request-trail/releases/tag/v0.3.0
|
|
23
46
|
[0.2.0]: https://github.com/eclectic-coding/request-trail/releases/tag/v0.2.0
|
|
24
47
|
[0.1.0]: https://github.com/eclectic-coding/request-trail/releases/tag/v0.1.0
|
data/README.md
CHANGED
|
@@ -36,16 +36,45 @@ gem install request_trail
|
|
|
36
36
|
|
|
37
37
|
### Rails
|
|
38
38
|
|
|
39
|
-
RequestTrail auto-inserts itself via a Railtie. No manual middleware configuration is needed — just add the gem to your `Gemfile` and it will log a summary after every request
|
|
39
|
+
RequestTrail auto-inserts itself via a Railtie. No manual middleware configuration is needed — just add the gem to your `Gemfile` and it will log a summary after every request.
|
|
40
|
+
|
|
41
|
+
When controller tracing is active, output is tiered:
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
[RequestTrail] GET /orders 142ms
|
|
45
|
+
controller 104ms
|
|
46
|
+
sql 38ms (7 queries)
|
|
47
|
+
cache 2ms (4 hits, 1 miss)
|
|
48
|
+
view 22ms
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Without controller data (plain Rack apps), a single-line summary is emitted:
|
|
40
52
|
|
|
41
53
|
```
|
|
42
54
|
[RequestTrail] GET /orders 142ms | SQL: 7/38.3ms | Cache: 4 hits, 1 miss, 2.0ms
|
|
43
55
|
```
|
|
44
56
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
57
|
+
### Flame graph formatter
|
|
58
|
+
|
|
59
|
+
Opt into the ASCII flame-graph formatter for a visual proportional breakdown:
|
|
60
|
+
|
|
61
|
+
```ruby
|
|
62
|
+
RequestTrail.configure do |config|
|
|
63
|
+
config.formatter = RequestTrail::Formatters::FlameGraph.new
|
|
64
|
+
end
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Output (with ANSI colour when stdout is a TTY):
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
[RequestTrail] GET /orders 142ms ████████████████████████████████████
|
|
71
|
+
controller 104ms ████████████████████████████
|
|
72
|
+
sql 38ms █████████
|
|
73
|
+
cache 2ms
|
|
74
|
+
view 22ms █████
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Colour scheme: controller = blue, sql = yellow, cache = green, view = magenta. Plain bars are emitted when stdout is not a TTY (e.g. log files, CI).
|
|
49
78
|
|
|
50
79
|
### Configuration
|
|
51
80
|
|
|
@@ -58,6 +87,7 @@ RequestTrail.configure do |config|
|
|
|
58
87
|
config.log_level = :info # Rails logger level (:debug, :info, :warn)
|
|
59
88
|
config.threshold_ms = 200 # only log requests slower than this (0 = log all)
|
|
60
89
|
config.logger = nil # defaults to Rails.logger
|
|
90
|
+
config.formatter = RequestTrail::Formatters::FlameGraph.new # optional
|
|
61
91
|
end
|
|
62
92
|
```
|
|
63
93
|
|
|
@@ -78,9 +108,42 @@ run MyApp
|
|
|
78
108
|
|
|
79
109
|
## Development
|
|
80
110
|
|
|
81
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake
|
|
111
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rake` to run the full CI suite (audit + lint + tests). You can also run `bin/console` for an interactive prompt.
|
|
112
|
+
|
|
113
|
+
### Running tests
|
|
82
114
|
|
|
83
|
-
|
|
115
|
+
```bash
|
|
116
|
+
bundle exec rake spec # full test suite
|
|
117
|
+
bundle exec rspec spec/path/to/file_spec.rb # single file
|
|
118
|
+
bundle exec rspec spec/path/to/file_spec.rb:42 # single example
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Dummy app
|
|
122
|
+
|
|
123
|
+
A minimal Rails app lives in `spec/dummy` for manual end-to-end testing. It mounts a single `GET /ping` endpoint and logs RequestTrail output to `spec/dummy/log/request_trail.log`.
|
|
124
|
+
|
|
125
|
+
Start the server:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
bundle exec rackup spec/dummy/config.ru --port 3000
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Then make a request and tail the log:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
curl http://localhost:3000/ping
|
|
135
|
+
tail -f spec/dummy/log/request_trail.log
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
You should see tiered output like:
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
[RequestTrail] GET /ping 33ms
|
|
142
|
+
controller 3ms
|
|
143
|
+
sql 0.0ms (0 queries)
|
|
144
|
+
cache 0.0ms (0 hits, 0 misses)
|
|
145
|
+
view 2.8ms
|
|
146
|
+
```
|
|
84
147
|
|
|
85
148
|
[Back to top](#requesttrail)
|
|
86
149
|
|
data/ROADMAP.md
CHANGED
|
@@ -2,38 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
`request_trail` traces a Rails request through every processing layer — middleware, controller, ActiveRecord, cache — and emits a flame-graph-style summary to the log. This roadmap describes the incremental path to a stable 1.0.0.
|
|
4
4
|
|
|
5
|
-
## 0.3.0 — Controller & View Tracing
|
|
6
|
-
|
|
7
|
-
- Subscribe to `process_action.action_controller` and `render_template.action_view`
|
|
8
|
-
- Tiered multi-line breakdown showing time spent in each layer:
|
|
9
|
-
```
|
|
10
|
-
[RequestTrail] GET /orders 142ms
|
|
11
|
-
controller 104ms
|
|
12
|
-
sql 38ms (7 queries)
|
|
13
|
-
cache 2ms (4 hits, 1 miss)
|
|
14
|
-
view 22ms
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
## 0.4.0 — Flame Graph Output
|
|
18
|
-
|
|
19
|
-
- Indented ASCII flame-graph renderer with proportional timing bars
|
|
20
|
-
- Optional ANSI colour with automatic TTY detection
|
|
21
|
-
- Ships as `Request::Trail::Formatters::FlameGraph` alongside the existing plain-text formatter:
|
|
22
|
-
```
|
|
23
|
-
[RequestTrail] GET /orders 142ms ████████████████████████████████████
|
|
24
|
-
middleware 4ms █
|
|
25
|
-
controller 100ms ████████████████████████
|
|
26
|
-
sql 38ms █████████
|
|
27
|
-
cache 2ms
|
|
28
|
-
view 22ms █████
|
|
29
|
-
```
|
|
30
|
-
|
|
31
5
|
## 0.5.0 — Filtering & Sampling
|
|
32
6
|
|
|
33
7
|
- Path filters: skip tracing for `/assets`, `/health`, or custom regex patterns
|
|
34
8
|
- Slow-request mode: only emit summaries above `threshold_ms`
|
|
35
9
|
- Sampling: trace only N% of requests (useful in production)
|
|
36
10
|
- Custom formatter API: `config.formatter = MyFormatter`
|
|
11
|
+
- FlameGraph colour overrides: `FlameGraph.new(colorize: true, colors: { controller: "\e[36m" })`
|
|
12
|
+
- Rails generator to scaffold the config initializer (`rails generate request_trail:install`)
|
|
37
13
|
|
|
38
14
|
## 0.6.0 — Structured Output & Integrations
|
|
39
15
|
|
|
@@ -5,7 +5,8 @@ module RequestTrail
|
|
|
5
5
|
THREAD_KEY = :request_trail_collector
|
|
6
6
|
|
|
7
7
|
attr_reader :sql_count, :sql_duration_ms,
|
|
8
|
-
:cache_hits, :cache_misses, :cache_writes, :cache_duration_ms
|
|
8
|
+
:cache_hits, :cache_misses, :cache_writes, :cache_duration_ms,
|
|
9
|
+
:action_duration_ms, :view_duration_ms
|
|
9
10
|
|
|
10
11
|
def self.current
|
|
11
12
|
Thread.current[THREAD_KEY]
|
|
@@ -27,6 +28,8 @@ module RequestTrail
|
|
|
27
28
|
@cache_misses = 0
|
|
28
29
|
@cache_writes = 0
|
|
29
30
|
@cache_duration_ms = 0.0
|
|
31
|
+
@action_duration_ms = 0.0
|
|
32
|
+
@view_duration_ms = 0.0
|
|
30
33
|
end
|
|
31
34
|
|
|
32
35
|
def record_sql(duration_ms)
|
|
@@ -44,6 +47,11 @@ module RequestTrail
|
|
|
44
47
|
@cache_duration_ms += duration_ms
|
|
45
48
|
end
|
|
46
49
|
|
|
50
|
+
def record_action(duration_ms:, view_duration_ms:)
|
|
51
|
+
@action_duration_ms = duration_ms
|
|
52
|
+
@view_duration_ms = view_duration_ms
|
|
53
|
+
end
|
|
54
|
+
|
|
47
55
|
def elapsed_ms
|
|
48
56
|
elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - @started_at
|
|
49
57
|
(elapsed * 1000).round(2)
|
|
@@ -4,7 +4,7 @@ require "logger"
|
|
|
4
4
|
|
|
5
5
|
module RequestTrail
|
|
6
6
|
class Configuration
|
|
7
|
-
attr_writer :logger
|
|
7
|
+
attr_writer :logger, :formatter
|
|
8
8
|
attr_accessor :enabled, :log_level, :threshold_ms
|
|
9
9
|
|
|
10
10
|
def initialize
|
|
@@ -17,6 +17,10 @@ module RequestTrail
|
|
|
17
17
|
@logger ||= rails_logger || Logger.new($stdout)
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
+
def formatter
|
|
21
|
+
@formatter ||= RequestTrail::Formatter.new
|
|
22
|
+
end
|
|
23
|
+
|
|
20
24
|
private
|
|
21
25
|
|
|
22
26
|
def rails_logger
|
|
@@ -3,12 +3,24 @@
|
|
|
3
3
|
module RequestTrail
|
|
4
4
|
class Formatter
|
|
5
5
|
def format(request, collector)
|
|
6
|
-
header = "[RequestTrail] #{request.request_method} #{request.path}"
|
|
7
|
-
|
|
6
|
+
header = "[RequestTrail] #{request.request_method} #{request.path} #{collector.elapsed_ms.round}ms"
|
|
7
|
+
return tiered_format(header, collector) if collector.action_duration_ms.positive?
|
|
8
|
+
|
|
9
|
+
"#{header} | #{sql_summary(collector)} | #{cache_summary(collector)}"
|
|
8
10
|
end
|
|
9
11
|
|
|
10
12
|
private
|
|
11
13
|
|
|
14
|
+
def tiered_format(header, collector)
|
|
15
|
+
[
|
|
16
|
+
header,
|
|
17
|
+
" controller #{collector.action_duration_ms.round}ms",
|
|
18
|
+
" sql #{collector.sql_duration_ms.round(1)}ms (#{sql_count_label(collector)})",
|
|
19
|
+
" cache #{collector.cache_duration_ms.round(1)}ms (#{cache_detail(collector)})",
|
|
20
|
+
" view #{collector.view_duration_ms.round(1)}ms"
|
|
21
|
+
].join("\n")
|
|
22
|
+
end
|
|
23
|
+
|
|
12
24
|
def sql_summary(collector)
|
|
13
25
|
"SQL: #{collector.sql_count}/#{collector.sql_duration_ms.round(1)}ms"
|
|
14
26
|
end
|
|
@@ -19,5 +31,15 @@ module RequestTrail
|
|
|
19
31
|
duration = collector.cache_duration_ms.round(1)
|
|
20
32
|
"Cache: #{collector.cache_hits} #{hit_label}, #{collector.cache_misses} #{miss_label}, #{duration}ms"
|
|
21
33
|
end
|
|
34
|
+
|
|
35
|
+
def sql_count_label(collector)
|
|
36
|
+
collector.sql_count == 1 ? "1 query" : "#{collector.sql_count} queries"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def cache_detail(collector)
|
|
40
|
+
hit_label = collector.cache_hits == 1 ? "hit" : "hits"
|
|
41
|
+
miss_label = collector.cache_misses == 1 ? "miss" : "misses"
|
|
42
|
+
"#{collector.cache_hits} #{hit_label}, #{collector.cache_misses} #{miss_label}"
|
|
43
|
+
end
|
|
22
44
|
end
|
|
23
45
|
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RequestTrail
|
|
4
|
+
module Formatters
|
|
5
|
+
class FlameGraph
|
|
6
|
+
BAR_WIDTH = 36
|
|
7
|
+
BAR_CHAR = "█"
|
|
8
|
+
|
|
9
|
+
COLORS = {
|
|
10
|
+
header: "\e[1m",
|
|
11
|
+
controller: "\e[34m",
|
|
12
|
+
sql: "\e[33m",
|
|
13
|
+
cache: "\e[32m",
|
|
14
|
+
view: "\e[35m"
|
|
15
|
+
}.freeze
|
|
16
|
+
RESET = "\e[0m"
|
|
17
|
+
|
|
18
|
+
def initialize(colorize: false)
|
|
19
|
+
@colorize = colorize
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def format(request, collector)
|
|
23
|
+
total = collector.elapsed_ms.to_f
|
|
24
|
+
lines = [header_line(request, collector)] + detail_rows(collector, total)
|
|
25
|
+
lines.join("\n")
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def detail_rows(collector, total)
|
|
31
|
+
return tiered_rows(collector, total) if collector.action_duration_ms.positive?
|
|
32
|
+
|
|
33
|
+
flat_rows(collector, total)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def tiered_rows(collector, total)
|
|
37
|
+
[
|
|
38
|
+
row(" ", "controller", collector.action_duration_ms, total, :controller),
|
|
39
|
+
row(" ", "sql", collector.sql_duration_ms, total, :sql),
|
|
40
|
+
row(" ", "cache", collector.cache_duration_ms, total, :cache),
|
|
41
|
+
row(" ", "view", collector.view_duration_ms, total, :view)
|
|
42
|
+
]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def flat_rows(collector, total)
|
|
46
|
+
[
|
|
47
|
+
row(" ", "sql", collector.sql_duration_ms, total, :sql),
|
|
48
|
+
row(" ", "cache", collector.cache_duration_ms, total, :cache)
|
|
49
|
+
]
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def header_line(request, collector)
|
|
53
|
+
elapsed = collector.elapsed_ms.round
|
|
54
|
+
bar = BAR_CHAR * BAR_WIDTH
|
|
55
|
+
line = "[RequestTrail] #{request.request_method} #{request.path} #{elapsed}ms #{bar}"
|
|
56
|
+
return line unless colorize?
|
|
57
|
+
|
|
58
|
+
"#{COLORS[:header]}#{line}#{RESET}"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def row(indent, label, duration_ms, total_ms, color_key)
|
|
62
|
+
ms = duration_ms.round
|
|
63
|
+
bar = colorized_bar(duration_ms, total_ms, color_key)
|
|
64
|
+
"#{indent}#{label.ljust(11)}#{ms.to_s.rjust(4)}ms #{bar}"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def colorized_bar(duration_ms, total_ms, color_key)
|
|
68
|
+
width = total_ms.positive? ? ((duration_ms.to_f / total_ms) * BAR_WIDTH).round : 0
|
|
69
|
+
bar = BAR_CHAR * width
|
|
70
|
+
return bar unless colorize? && width.positive?
|
|
71
|
+
|
|
72
|
+
"#{COLORS[color_key]}#{bar}#{RESET}"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def colorize?
|
|
76
|
+
@colorize
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -5,9 +5,10 @@ module RequestTrail
|
|
|
5
5
|
SQL_EVENT = "sql.active_record"
|
|
6
6
|
CACHE_READ_EVENT = "cache_read.active_support"
|
|
7
7
|
CACHE_WRITE_EVENT = "cache_write.active_support"
|
|
8
|
+
ACTION_EVENT = "process_action.action_controller"
|
|
8
9
|
|
|
9
10
|
def self.attach
|
|
10
|
-
@attach ||= [sql_subscription, cache_read_subscription, cache_write_subscription]
|
|
11
|
+
@attach ||= [sql_subscription, cache_read_subscription, cache_write_subscription, action_subscription]
|
|
11
12
|
end
|
|
12
13
|
|
|
13
14
|
def self.detach
|
|
@@ -40,5 +41,15 @@ module RequestTrail
|
|
|
40
41
|
Collector.current&.record_cache_write(duration_ms: event.duration)
|
|
41
42
|
end
|
|
42
43
|
end
|
|
44
|
+
|
|
45
|
+
private_class_method def self.action_subscription
|
|
46
|
+
ActiveSupport::Notifications.subscribe(ACTION_EVENT) do |*args|
|
|
47
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
|
48
|
+
Collector.current&.record_action(
|
|
49
|
+
duration_ms: event.duration,
|
|
50
|
+
view_duration_ms: event.payload.fetch(:view_runtime, 0.0)
|
|
51
|
+
)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
43
54
|
end
|
|
44
55
|
end
|
data/lib/request_trail.rb
CHANGED
|
@@ -5,6 +5,7 @@ require_relative "request_trail/configuration"
|
|
|
5
5
|
require_relative "request_trail/collector"
|
|
6
6
|
require_relative "request_trail/subscriber"
|
|
7
7
|
require_relative "request_trail/formatter"
|
|
8
|
+
require_relative "request_trail/formatters/flame_graph"
|
|
8
9
|
require_relative "request_trail/middleware"
|
|
9
10
|
require_relative "request_trail/railtie" if defined?(Rails::Railtie)
|
|
10
11
|
|
|
@@ -21,12 +22,11 @@ module RequestTrail
|
|
|
21
22
|
end
|
|
22
23
|
|
|
23
24
|
def formatter
|
|
24
|
-
|
|
25
|
+
configuration.formatter
|
|
25
26
|
end
|
|
26
27
|
|
|
27
28
|
def reset!
|
|
28
29
|
@configuration = nil
|
|
29
|
-
@formatter = nil
|
|
30
30
|
end
|
|
31
31
|
end
|
|
32
32
|
end
|
|
@@ -9,6 +9,8 @@ module RequestTrail
|
|
|
9
9
|
@cache_misses: Integer
|
|
10
10
|
@cache_writes: Integer
|
|
11
11
|
@cache_duration_ms: Float
|
|
12
|
+
@action_duration_ms: Float
|
|
13
|
+
@view_duration_ms: Float
|
|
12
14
|
|
|
13
15
|
attr_reader sql_count: Integer
|
|
14
16
|
attr_reader sql_duration_ms: Float
|
|
@@ -16,6 +18,8 @@ module RequestTrail
|
|
|
16
18
|
attr_reader cache_misses: Integer
|
|
17
19
|
attr_reader cache_writes: Integer
|
|
18
20
|
attr_reader cache_duration_ms: Float
|
|
21
|
+
attr_reader action_duration_ms: Float
|
|
22
|
+
attr_reader view_duration_ms: Float
|
|
19
23
|
|
|
20
24
|
def self.current: () -> Collector?
|
|
21
25
|
def self.start: () -> Collector
|
|
@@ -25,6 +29,7 @@ module RequestTrail
|
|
|
25
29
|
def record_sql: (Float duration_ms) -> Float
|
|
26
30
|
def record_cache_read: (hit: bool, duration_ms: Float) -> Float
|
|
27
31
|
def record_cache_write: (duration_ms: Float) -> Float
|
|
32
|
+
def record_action: (duration_ms: Float, view_duration_ms: Float) -> Float
|
|
28
33
|
def elapsed_ms: () -> (Float | Integer)
|
|
29
34
|
end
|
|
30
35
|
end
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
module RequestTrail
|
|
2
2
|
class Configuration
|
|
3
3
|
attr_writer logger: ::Logger
|
|
4
|
+
attr_writer formatter: untyped
|
|
4
5
|
attr_accessor enabled: bool
|
|
5
6
|
attr_accessor log_level: Symbol
|
|
6
7
|
attr_accessor threshold_ms: Numeric
|
|
7
8
|
|
|
8
9
|
def initialize: () -> void
|
|
9
10
|
def logger: () -> ::Logger
|
|
11
|
+
def formatter: () -> untyped
|
|
10
12
|
end
|
|
11
13
|
end
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
module RequestTrail
|
|
2
2
|
class Formatter
|
|
3
3
|
def format: (::Rack::Request request, Collector collector) -> String
|
|
4
|
+
|
|
5
|
+
private
|
|
6
|
+
|
|
7
|
+
def tiered_format: (String header, Collector collector) -> String
|
|
8
|
+
def sql_summary: (Collector collector) -> String
|
|
9
|
+
def cache_summary: (Collector collector) -> String
|
|
10
|
+
def sql_count_label: (Collector collector) -> String
|
|
11
|
+
def cache_detail: (Collector collector) -> String
|
|
4
12
|
end
|
|
5
13
|
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module RequestTrail
|
|
2
|
+
module Formatters
|
|
3
|
+
class FlameGraph
|
|
4
|
+
BAR_WIDTH: Integer
|
|
5
|
+
BAR_CHAR: String
|
|
6
|
+
COLORS: Hash[Symbol, String]
|
|
7
|
+
RESET: String
|
|
8
|
+
|
|
9
|
+
def initialize: (?colorize: bool) -> void
|
|
10
|
+
def format: (::Rack::Request request, Collector collector) -> String
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def detail_rows: (Collector collector, Float total) -> Array[String]
|
|
15
|
+
def tiered_rows: (Collector collector, Float total) -> Array[String]
|
|
16
|
+
def flat_rows: (Collector collector, Float total) -> Array[String]
|
|
17
|
+
def header_line: (::Rack::Request request, Collector collector) -> String
|
|
18
|
+
def row: (String indent, String label, Float duration_ms, Float total_ms, Symbol color_key) -> String
|
|
19
|
+
def colorized_bar: (Float duration_ms, Float total_ms, Symbol color_key) -> String
|
|
20
|
+
def colorize?: () -> bool
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -3,6 +3,7 @@ module RequestTrail
|
|
|
3
3
|
SQL_EVENT: String
|
|
4
4
|
CACHE_READ_EVENT: String
|
|
5
5
|
CACHE_WRITE_EVENT: String
|
|
6
|
+
ACTION_EVENT: String
|
|
6
7
|
|
|
7
8
|
self.@attach: Array[untyped]?
|
|
8
9
|
|
|
@@ -14,5 +15,6 @@ module RequestTrail
|
|
|
14
15
|
def self.sql_subscription: () -> untyped
|
|
15
16
|
def self.cache_read_subscription: () -> untyped
|
|
16
17
|
def self.cache_write_subscription: () -> untyped
|
|
18
|
+
def self.action_subscription: () -> untyped
|
|
17
19
|
end
|
|
18
20
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: request_trail
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Chuck Smith
|
|
@@ -58,6 +58,7 @@ files:
|
|
|
58
58
|
- lib/request_trail/collector.rb
|
|
59
59
|
- lib/request_trail/configuration.rb
|
|
60
60
|
- lib/request_trail/formatter.rb
|
|
61
|
+
- lib/request_trail/formatters/flame_graph.rb
|
|
61
62
|
- lib/request_trail/middleware.rb
|
|
62
63
|
- lib/request_trail/railtie.rb
|
|
63
64
|
- lib/request_trail/subscriber.rb
|
|
@@ -66,6 +67,7 @@ files:
|
|
|
66
67
|
- sig/request_trail/collector.rbs
|
|
67
68
|
- sig/request_trail/configuration.rbs
|
|
68
69
|
- sig/request_trail/formatter.rbs
|
|
70
|
+
- sig/request_trail/formatters/flame_graph.rbs
|
|
69
71
|
- sig/request_trail/middleware.rbs
|
|
70
72
|
- sig/request_trail/subscriber.rbs
|
|
71
73
|
homepage: https://github.com/eclectic-coding/request-trail
|