request_trail 0.1.0 → 0.2.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 +9 -1
- data/README.md +6 -1
- data/ROADMAP.md +0 -8
- data/lib/request_trail/collector.rb +16 -1
- data/lib/request_trail/formatter.rb +9 -3
- data/lib/request_trail/subscriber.rb +29 -6
- data/lib/request_trail/version.rb +1 -1
- data/sig/request_trail/collector.rbs +30 -0
- data/sig/request_trail/configuration.rbs +11 -0
- data/sig/request_trail/formatter.rbs +5 -0
- data/sig/request_trail/middleware.rbs +6 -0
- data/sig/request_trail/subscriber.rbs +18 -0
- metadata +6 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 170ab890527afbaf7d18005ceca802f6a561795724cce97e1f9740f100dcc1ea
|
|
4
|
+
data.tar.gz: 91e793b4cf8bf1b086ac2908177bd618e8aabd71a37f239e45b115c9f986290e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 59cceaa66b3ff236a3a9bab1637f78db87455c5beb1e8687bc89f459053803fd12b52131862923b1d26561cb6c349dee2c970e0cdd421216445758cd1ea6fc21
|
|
7
|
+
data.tar.gz: e8969dd6df420ff10c3e3a2879ace0a187797633e98764ddf152b28e1d435a4a0099825afb8af64859d8dd1eff6d9403251e9b88c4f249251efe12995f1dc0af
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.2.0] - 2026-06-11
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Cache tracing via `cache_read.active_support` and `cache_write.active_support` notifications — records hit/miss/write counts and cumulative duration per request
|
|
8
|
+
- Summary line now includes a cache segment: `[RequestTrail] GET /orders 142ms | SQL: 7/38.3ms | Cache: 4 hits, 1 miss, 2.0ms`
|
|
9
|
+
|
|
3
10
|
## [0.1.0] - 2026-06-11
|
|
4
11
|
|
|
5
12
|
### Added
|
|
@@ -12,5 +19,6 @@
|
|
|
12
19
|
- `RequestTrail::Subscriber` — attach/detach API for notification subscriptions
|
|
13
20
|
- `RequestTrail::Collector` — thread-safe per-request event accumulator
|
|
14
21
|
|
|
15
|
-
[Unreleased]: https://github.com/eclectic-coding/request-trail/compare/v0.
|
|
22
|
+
[Unreleased]: https://github.com/eclectic-coding/request-trail/compare/v0.2.0...HEAD
|
|
23
|
+
[0.2.0]: https://github.com/eclectic-coding/request-trail/releases/tag/v0.2.0
|
|
16
24
|
[0.1.0]: https://github.com/eclectic-coding/request-trail/releases/tag/v0.1.0
|
data/README.md
CHANGED
|
@@ -39,9 +39,14 @@ gem install request_trail
|
|
|
39
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
40
|
|
|
41
41
|
```
|
|
42
|
-
[RequestTrail] GET /orders 142ms | SQL: 7
|
|
42
|
+
[RequestTrail] GET /orders 142ms | SQL: 7/38.3ms | Cache: 4 hits, 1 miss, 2.0ms
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
+
The summary line shows:
|
|
46
|
+
- **Total request time** in milliseconds
|
|
47
|
+
- **SQL** — query count and cumulative Active Record time
|
|
48
|
+
- **Cache** — hit/miss/write counts and cumulative cache time
|
|
49
|
+
|
|
45
50
|
### Configuration
|
|
46
51
|
|
|
47
52
|
Add an initializer to customize behavior:
|
data/ROADMAP.md
CHANGED
|
@@ -2,14 +2,6 @@
|
|
|
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.2.0 — Cache Tracing
|
|
6
|
-
|
|
7
|
-
- Subscribe to `cache_read`, `cache_write`, `cache_fetch_hit`, and `cache_delete` notifications
|
|
8
|
-
- Add cache hit/miss/write counts and cumulative time to the summary line:
|
|
9
|
-
```
|
|
10
|
-
[RequestTrail] GET /orders 142ms | SQL: 7/38ms | Cache: 4 hits, 1 miss, 2ms
|
|
11
|
-
```
|
|
12
|
-
|
|
13
5
|
## 0.3.0 — Controller & View Tracing
|
|
14
6
|
|
|
15
7
|
- Subscribe to `process_action.action_controller` and `render_template.action_view`
|
|
@@ -4,7 +4,8 @@ module RequestTrail
|
|
|
4
4
|
class Collector
|
|
5
5
|
THREAD_KEY = :request_trail_collector
|
|
6
6
|
|
|
7
|
-
attr_reader :sql_count, :sql_duration_ms
|
|
7
|
+
attr_reader :sql_count, :sql_duration_ms,
|
|
8
|
+
:cache_hits, :cache_misses, :cache_writes, :cache_duration_ms
|
|
8
9
|
|
|
9
10
|
def self.current
|
|
10
11
|
Thread.current[THREAD_KEY]
|
|
@@ -22,6 +23,10 @@ module RequestTrail
|
|
|
22
23
|
@started_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
23
24
|
@sql_count = 0
|
|
24
25
|
@sql_duration_ms = 0.0
|
|
26
|
+
@cache_hits = 0
|
|
27
|
+
@cache_misses = 0
|
|
28
|
+
@cache_writes = 0
|
|
29
|
+
@cache_duration_ms = 0.0
|
|
25
30
|
end
|
|
26
31
|
|
|
27
32
|
def record_sql(duration_ms)
|
|
@@ -29,6 +34,16 @@ module RequestTrail
|
|
|
29
34
|
@sql_duration_ms += duration_ms
|
|
30
35
|
end
|
|
31
36
|
|
|
37
|
+
def record_cache_read(hit:, duration_ms:)
|
|
38
|
+
hit ? @cache_hits += 1 : @cache_misses += 1
|
|
39
|
+
@cache_duration_ms += duration_ms
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def record_cache_write(duration_ms:)
|
|
43
|
+
@cache_writes += 1
|
|
44
|
+
@cache_duration_ms += duration_ms
|
|
45
|
+
end
|
|
46
|
+
|
|
32
47
|
def elapsed_ms
|
|
33
48
|
elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - @started_at
|
|
34
49
|
(elapsed * 1000).round(2)
|
|
@@ -4,14 +4,20 @@ module RequestTrail
|
|
|
4
4
|
class Formatter
|
|
5
5
|
def format(request, collector)
|
|
6
6
|
header = "[RequestTrail] #{request.request_method} #{request.path}"
|
|
7
|
-
"#{header} #{collector.elapsed_ms.round}ms | #{sql_summary(collector)}"
|
|
7
|
+
"#{header} #{collector.elapsed_ms.round}ms | #{sql_summary(collector)} | #{cache_summary(collector)}"
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
private
|
|
11
11
|
|
|
12
12
|
def sql_summary(collector)
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
"SQL: #{collector.sql_count}/#{collector.sql_duration_ms.round(1)}ms"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def cache_summary(collector)
|
|
17
|
+
hit_label = collector.cache_hits == 1 ? "hit" : "hits"
|
|
18
|
+
miss_label = collector.cache_misses == 1 ? "miss" : "misses"
|
|
19
|
+
duration = collector.cache_duration_ms.round(1)
|
|
20
|
+
"Cache: #{collector.cache_hits} #{hit_label}, #{collector.cache_misses} #{miss_label}, #{duration}ms"
|
|
15
21
|
end
|
|
16
22
|
end
|
|
17
23
|
end
|
|
@@ -2,20 +2,43 @@
|
|
|
2
2
|
|
|
3
3
|
module RequestTrail
|
|
4
4
|
class Subscriber
|
|
5
|
-
SQL_EVENT
|
|
5
|
+
SQL_EVENT = "sql.active_record"
|
|
6
|
+
CACHE_READ_EVENT = "cache_read.active_support"
|
|
7
|
+
CACHE_WRITE_EVENT = "cache_write.active_support"
|
|
6
8
|
|
|
7
9
|
def self.attach
|
|
8
|
-
@attach ||=
|
|
9
|
-
event = ActiveSupport::Notifications::Event.new(*args)
|
|
10
|
-
Collector.current&.record_sql(event.duration)
|
|
11
|
-
end
|
|
10
|
+
@attach ||= [sql_subscription, cache_read_subscription, cache_write_subscription]
|
|
12
11
|
end
|
|
13
12
|
|
|
14
13
|
def self.detach
|
|
15
14
|
return unless @attach
|
|
16
15
|
|
|
17
|
-
ActiveSupport::Notifications.unsubscribe(
|
|
16
|
+
@attach&.each { |sub| ActiveSupport::Notifications.unsubscribe(sub) }
|
|
18
17
|
@attach = nil
|
|
19
18
|
end
|
|
19
|
+
|
|
20
|
+
private_class_method def self.sql_subscription
|
|
21
|
+
ActiveSupport::Notifications.subscribe(SQL_EVENT) do |*args|
|
|
22
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
|
23
|
+
Collector.current&.record_sql(event.duration)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private_class_method def self.cache_read_subscription
|
|
28
|
+
ActiveSupport::Notifications.subscribe(CACHE_READ_EVENT) do |*args|
|
|
29
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
|
30
|
+
Collector.current&.record_cache_read(
|
|
31
|
+
hit: event.payload.fetch(:hit, false),
|
|
32
|
+
duration_ms: event.duration
|
|
33
|
+
)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private_class_method def self.cache_write_subscription
|
|
38
|
+
ActiveSupport::Notifications.subscribe(CACHE_WRITE_EVENT) do |*args|
|
|
39
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
|
40
|
+
Collector.current&.record_cache_write(duration_ms: event.duration)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
20
43
|
end
|
|
21
44
|
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module RequestTrail
|
|
2
|
+
class Collector
|
|
3
|
+
THREAD_KEY: Symbol
|
|
4
|
+
|
|
5
|
+
@started_at: Float
|
|
6
|
+
@sql_count: Integer
|
|
7
|
+
@sql_duration_ms: Float
|
|
8
|
+
@cache_hits: Integer
|
|
9
|
+
@cache_misses: Integer
|
|
10
|
+
@cache_writes: Integer
|
|
11
|
+
@cache_duration_ms: Float
|
|
12
|
+
|
|
13
|
+
attr_reader sql_count: Integer
|
|
14
|
+
attr_reader sql_duration_ms: Float
|
|
15
|
+
attr_reader cache_hits: Integer
|
|
16
|
+
attr_reader cache_misses: Integer
|
|
17
|
+
attr_reader cache_writes: Integer
|
|
18
|
+
attr_reader cache_duration_ms: Float
|
|
19
|
+
|
|
20
|
+
def self.current: () -> Collector?
|
|
21
|
+
def self.start: () -> Collector
|
|
22
|
+
def self.stop: () -> nil
|
|
23
|
+
|
|
24
|
+
def initialize: () -> void
|
|
25
|
+
def record_sql: (Float duration_ms) -> Float
|
|
26
|
+
def record_cache_read: (hit: bool, duration_ms: Float) -> Float
|
|
27
|
+
def record_cache_write: (duration_ms: Float) -> Float
|
|
28
|
+
def elapsed_ms: () -> (Float | Integer)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module RequestTrail
|
|
2
|
+
class Subscriber
|
|
3
|
+
SQL_EVENT: String
|
|
4
|
+
CACHE_READ_EVENT: String
|
|
5
|
+
CACHE_WRITE_EVENT: String
|
|
6
|
+
|
|
7
|
+
self.@attach: Array[untyped]?
|
|
8
|
+
|
|
9
|
+
def self.attach: () -> Array[untyped]
|
|
10
|
+
def self.detach: () -> nil
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def self.sql_subscription: () -> untyped
|
|
15
|
+
def self.cache_read_subscription: () -> untyped
|
|
16
|
+
def self.cache_write_subscription: () -> untyped
|
|
17
|
+
end
|
|
18
|
+
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.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Chuck Smith
|
|
@@ -63,6 +63,11 @@ files:
|
|
|
63
63
|
- lib/request_trail/subscriber.rb
|
|
64
64
|
- lib/request_trail/version.rb
|
|
65
65
|
- sig/request_trail.rbs
|
|
66
|
+
- sig/request_trail/collector.rbs
|
|
67
|
+
- sig/request_trail/configuration.rbs
|
|
68
|
+
- sig/request_trail/formatter.rbs
|
|
69
|
+
- sig/request_trail/middleware.rbs
|
|
70
|
+
- sig/request_trail/subscriber.rbs
|
|
66
71
|
homepage: https://github.com/eclectic-coding/request-trail
|
|
67
72
|
licenses:
|
|
68
73
|
- MIT
|