request_trail 0.4.0 → 0.5.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 +12 -1
- data/README.md +50 -0
- data/ROADMAP.md +0 -9
- data/lib/generators/request_trail/install/install_generator.rb +18 -0
- data/lib/generators/request_trail/install/templates/request_trail.rb.tt +12 -0
- data/lib/request_trail/configuration.rb +18 -1
- data/lib/request_trail/formatter.rb +2 -0
- data/lib/request_trail/formatters/base.rb +17 -0
- data/lib/request_trail/formatters/flame_graph.rb +6 -3
- data/lib/request_trail/middleware.rb +2 -0
- data/lib/request_trail/version.rb +1 -1
- data/lib/request_trail.rb +1 -0
- data/sig/request_trail/configuration.rbs +6 -2
- data/sig/request_trail/formatters/base.rbs +7 -0
- data/sig/request_trail/formatters/flame_graph.rbs +1 -1
- metadata +5 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b098702d0968a3cfbdc8b4147ab87bae1d24854a32d82b604993d17c75d367ba
|
|
4
|
+
data.tar.gz: 3a8b148a3a2eb1f7d23f938fbf2b6a70d4776c97a003bacd7ac3fd55989b3c16
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4c3bd3e29cae157cce0bdf0f3a3aa243abc0e01bc830f7891f8c0604665246d77cbdc055e693a6f9919c4f788ee2bc9ba5e64739f26c4decd8c3a917e73a3ebe
|
|
7
|
+
data.tar.gz: f68396f353c7e5ff0debf97ead1ea725e3163a014525f7837bc62774d253872955bd6f6e3c9d82b97e20d88bea3d2f58104a46b74c314b31902d68fb4ac16cc1
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.5.0] - 2026-06-13
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- `config.ignore_paths` — skip tracing for specific paths; accepts strings (exact match) or regexes (e.g. `["/health", /^\/assets/]`)
|
|
8
|
+
- `config.sample_rate` — trace only N% of requests; accepts a float between `0.0` and `1.0` (default `1.0` = 100%)
|
|
9
|
+
- `RequestTrail::Formatters::Base` — mixin that documents the formatter duck-type contract; include it in custom formatters and implement `#format(request, collector) -> String`
|
|
10
|
+
- `FlameGraph` colour overrides — pass `colors: { controller: "\e[36m" }` to `FlameGraph.new` to replace per-layer ANSI codes; unspecified layers keep their defaults
|
|
11
|
+
- Rails install generator — run `rails generate request_trail:install` to scaffold `config/initializers/request_trail.rb` pre-populated with all options and their defaults
|
|
12
|
+
|
|
3
13
|
## [0.4.0] - 2026-06-12
|
|
4
14
|
|
|
5
15
|
### Added
|
|
@@ -40,7 +50,8 @@
|
|
|
40
50
|
- `RequestTrail::Subscriber` — attach/detach API for notification subscriptions
|
|
41
51
|
- `RequestTrail::Collector` — thread-safe per-request event accumulator
|
|
42
52
|
|
|
43
|
-
[Unreleased]: https://github.com/eclectic-coding/request-trail/compare/v0.
|
|
53
|
+
[Unreleased]: https://github.com/eclectic-coding/request-trail/compare/v0.5.0...HEAD
|
|
54
|
+
[0.5.0]: https://github.com/eclectic-coding/request-trail/releases/tag/v0.5.0
|
|
44
55
|
[0.4.0]: https://github.com/eclectic-coding/request-trail/releases/tag/v0.4.0
|
|
45
56
|
[0.3.0]: https://github.com/eclectic-coding/request-trail/releases/tag/v0.3.0
|
|
46
57
|
[0.2.0]: https://github.com/eclectic-coding/request-trail/releases/tag/v0.2.0
|
data/README.md
CHANGED
|
@@ -12,6 +12,7 @@ Middleware that traces a request through all the layers (middleware, controller,
|
|
|
12
12
|
|
|
13
13
|
- [Installation](#installation)
|
|
14
14
|
- [Usage](#usage)
|
|
15
|
+
- [Custom formatters](#custom-formatters)
|
|
15
16
|
- [Development](#development)
|
|
16
17
|
- [Contributing](#contributing)
|
|
17
18
|
- [License](#license)
|
|
@@ -76,6 +77,49 @@ Output (with ANSI colour when stdout is a TTY):
|
|
|
76
77
|
|
|
77
78
|
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).
|
|
78
79
|
|
|
80
|
+
Override any layer's ANSI code with the `colors:` option:
|
|
81
|
+
|
|
82
|
+
```ruby
|
|
83
|
+
RequestTrail::Formatters::FlameGraph.new(
|
|
84
|
+
colorize: true,
|
|
85
|
+
colors: { controller: "\e[36m", sql: "\e[31m" }
|
|
86
|
+
)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Unspecified layers keep their defaults.
|
|
90
|
+
|
|
91
|
+
### Custom formatters
|
|
92
|
+
|
|
93
|
+
Any object that responds to `format(request, collector)` and returns a `String` can be used as a formatter. Include `RequestTrail::Formatters::Base` to make the contract explicit:
|
|
94
|
+
|
|
95
|
+
```ruby
|
|
96
|
+
class MyFormatter
|
|
97
|
+
include RequestTrail::Formatters::Base
|
|
98
|
+
|
|
99
|
+
def format(request, collector)
|
|
100
|
+
"#{request.request_method} #{request.path} took #{collector.elapsed_ms.round}ms"
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
RequestTrail.configure do |config|
|
|
105
|
+
config.formatter = MyFormatter.new
|
|
106
|
+
end
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
`format` receives:
|
|
110
|
+
- `request` — a `Rack::Request` with the current HTTP request
|
|
111
|
+
- `collector` — a `RequestTrail::Collector` exposing `elapsed_ms`, `sql_count`, `sql_duration_ms`, `cache_hits`, `cache_misses`, `cache_duration_ms`, `action_duration_ms`, and `view_duration_ms`
|
|
112
|
+
|
|
113
|
+
### Installation generator
|
|
114
|
+
|
|
115
|
+
Run the generator to scaffold the initializer:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
rails generate request_trail:install
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
This creates `config/initializers/request_trail.rb` pre-populated with all available options and their defaults.
|
|
122
|
+
|
|
79
123
|
### Configuration
|
|
80
124
|
|
|
81
125
|
Add an initializer to customize behavior:
|
|
@@ -88,6 +132,12 @@ RequestTrail.configure do |config|
|
|
|
88
132
|
config.threshold_ms = 200 # only log requests slower than this (0 = log all)
|
|
89
133
|
config.logger = nil # defaults to Rails.logger
|
|
90
134
|
config.formatter = RequestTrail::Formatters::FlameGraph.new # optional
|
|
135
|
+
|
|
136
|
+
# skip tracing for specific paths (strings = exact match, regexes = pattern match)
|
|
137
|
+
config.ignore_paths = ["/health", "/up", /^\/assets/]
|
|
138
|
+
|
|
139
|
+
# trace only N% of requests — useful in high-traffic production environments
|
|
140
|
+
config.sample_rate = 0.1 # 0.0 = never, 1.0 = always (default)
|
|
91
141
|
end
|
|
92
142
|
```
|
|
93
143
|
|
data/ROADMAP.md
CHANGED
|
@@ -2,15 +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.5.0 — Filtering & Sampling
|
|
6
|
-
|
|
7
|
-
- Path filters: skip tracing for `/assets`, `/health`, or custom regex patterns
|
|
8
|
-
- Slow-request mode: only emit summaries above `threshold_ms`
|
|
9
|
-
- Sampling: trace only N% of requests (useful in production)
|
|
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`)
|
|
13
|
-
|
|
14
5
|
## 0.6.0 — Structured Output & Integrations
|
|
15
6
|
|
|
16
7
|
- JSON formatter for log aggregators (Datadog, Splunk, etc.)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
return unless defined?(Rails::Generators)
|
|
4
|
+
|
|
5
|
+
require "rails/generators"
|
|
6
|
+
|
|
7
|
+
module RequestTrail
|
|
8
|
+
module Generators
|
|
9
|
+
class InstallGenerator < Rails::Generators::Base
|
|
10
|
+
source_root File.expand_path("templates", __dir__)
|
|
11
|
+
desc "Creates a RequestTrail initializer in config/initializers"
|
|
12
|
+
|
|
13
|
+
def copy_initializer
|
|
14
|
+
template "request_trail.rb.tt", "config/initializers/request_trail.rb"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RequestTrail.configure do |config|
|
|
4
|
+
config.enabled = true # set to false to disable entirely
|
|
5
|
+
config.log_level = :info # :debug, :info, :warn
|
|
6
|
+
config.threshold_ms = 0 # only log requests slower than this (0 = log all)
|
|
7
|
+
config.ignore_paths = [] # e.g. ["/health", /^\/assets/]
|
|
8
|
+
config.sample_rate = 1.0 # 0.0–1.0; 1.0 = trace every request
|
|
9
|
+
|
|
10
|
+
# config.logger = Rails.logger # defaults to Rails.logger
|
|
11
|
+
# config.formatter = RequestTrail::Formatters::FlameGraph.new(colorize: true)
|
|
12
|
+
end
|
|
@@ -5,12 +5,22 @@ require "logger"
|
|
|
5
5
|
module RequestTrail
|
|
6
6
|
class Configuration
|
|
7
7
|
attr_writer :logger, :formatter
|
|
8
|
-
attr_accessor :enabled, :log_level, :threshold_ms
|
|
8
|
+
attr_accessor :enabled, :log_level, :threshold_ms, :ignore_paths, :sample_rate
|
|
9
9
|
|
|
10
10
|
def initialize
|
|
11
11
|
@enabled = true
|
|
12
12
|
@log_level = :info
|
|
13
13
|
@threshold_ms = 0
|
|
14
|
+
@ignore_paths = []
|
|
15
|
+
@sample_rate = 1.0
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def sampled?
|
|
19
|
+
rand < sample_rate
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def ignored_path?(path)
|
|
23
|
+
ignore_paths.any? { |pattern| path_matches?(pattern, path) }
|
|
14
24
|
end
|
|
15
25
|
|
|
16
26
|
def logger
|
|
@@ -23,6 +33,13 @@ module RequestTrail
|
|
|
23
33
|
|
|
24
34
|
private
|
|
25
35
|
|
|
36
|
+
def path_matches?(pattern, path)
|
|
37
|
+
case pattern
|
|
38
|
+
when Regexp then pattern.match?(path)
|
|
39
|
+
else pattern == path
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
26
43
|
def rails_logger
|
|
27
44
|
Rails.logger if defined?(Rails) && Rails.respond_to?(:logger) && Rails.logger
|
|
28
45
|
end
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
module RequestTrail
|
|
4
4
|
class Formatter
|
|
5
|
+
include Formatters::Base
|
|
6
|
+
|
|
5
7
|
def format(request, collector)
|
|
6
8
|
header = "[RequestTrail] #{request.request_method} #{request.path} #{collector.elapsed_ms.round}ms"
|
|
7
9
|
return tiered_format(header, collector) if collector.action_duration_ms.positive?
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RequestTrail
|
|
4
|
+
module Formatters
|
|
5
|
+
# Mixin that documents the formatter duck-type contract.
|
|
6
|
+
# Include it in custom formatters to make the interface explicit;
|
|
7
|
+
# the only required method is #format(request, collector) -> String.
|
|
8
|
+
module Base
|
|
9
|
+
# @param request [Rack::Request]
|
|
10
|
+
# @param collector [RequestTrail::Collector]
|
|
11
|
+
# @return [String] the log line(s) to emit
|
|
12
|
+
def format(_request, _collector)
|
|
13
|
+
raise NotImplementedError, "#{self.class}#format must return a String"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
module RequestTrail
|
|
4
4
|
module Formatters
|
|
5
5
|
class FlameGraph
|
|
6
|
+
include Base
|
|
7
|
+
|
|
6
8
|
BAR_WIDTH = 36
|
|
7
9
|
BAR_CHAR = "█"
|
|
8
10
|
|
|
@@ -15,8 +17,9 @@ module RequestTrail
|
|
|
15
17
|
}.freeze
|
|
16
18
|
RESET = "\e[0m"
|
|
17
19
|
|
|
18
|
-
def initialize(colorize: false)
|
|
20
|
+
def initialize(colorize: false, colors: {})
|
|
19
21
|
@colorize = colorize
|
|
22
|
+
@colors = COLORS.merge(colors)
|
|
20
23
|
end
|
|
21
24
|
|
|
22
25
|
def format(request, collector)
|
|
@@ -55,7 +58,7 @@ module RequestTrail
|
|
|
55
58
|
line = "[RequestTrail] #{request.request_method} #{request.path} #{elapsed}ms #{bar}"
|
|
56
59
|
return line unless colorize?
|
|
57
60
|
|
|
58
|
-
"#{
|
|
61
|
+
"#{@colors[:header]}#{line}#{RESET}"
|
|
59
62
|
end
|
|
60
63
|
|
|
61
64
|
def row(indent, label, duration_ms, total_ms, color_key)
|
|
@@ -69,7 +72,7 @@ module RequestTrail
|
|
|
69
72
|
bar = BAR_CHAR * width
|
|
70
73
|
return bar unless colorize? && width.positive?
|
|
71
74
|
|
|
72
|
-
"#{
|
|
75
|
+
"#{@colors[color_key]}#{bar}#{RESET}"
|
|
73
76
|
end
|
|
74
77
|
|
|
75
78
|
def colorize?
|
|
@@ -8,6 +8,8 @@ module RequestTrail
|
|
|
8
8
|
|
|
9
9
|
def call(env)
|
|
10
10
|
return @app.call(env) unless RequestTrail.configuration.enabled
|
|
11
|
+
return @app.call(env) if RequestTrail.configuration.ignored_path?(env["PATH_INFO"])
|
|
12
|
+
return @app.call(env) unless RequestTrail.configuration.sampled?
|
|
11
13
|
|
|
12
14
|
Collector.start
|
|
13
15
|
response = @app.call(env)
|
data/lib/request_trail.rb
CHANGED
|
@@ -4,6 +4,7 @@ require_relative "request_trail/version"
|
|
|
4
4
|
require_relative "request_trail/configuration"
|
|
5
5
|
require_relative "request_trail/collector"
|
|
6
6
|
require_relative "request_trail/subscriber"
|
|
7
|
+
require_relative "request_trail/formatters/base"
|
|
7
8
|
require_relative "request_trail/formatter"
|
|
8
9
|
require_relative "request_trail/formatters/flame_graph"
|
|
9
10
|
require_relative "request_trail/middleware"
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
module RequestTrail
|
|
2
2
|
class Configuration
|
|
3
3
|
attr_writer logger: ::Logger
|
|
4
|
-
attr_writer formatter:
|
|
4
|
+
attr_writer formatter: Formatters::Base
|
|
5
5
|
attr_accessor enabled: bool
|
|
6
6
|
attr_accessor log_level: Symbol
|
|
7
7
|
attr_accessor threshold_ms: Numeric
|
|
8
|
+
attr_accessor ignore_paths: Array[String | Regexp]
|
|
9
|
+
attr_accessor sample_rate: Float
|
|
8
10
|
|
|
9
11
|
def initialize: () -> void
|
|
10
12
|
def logger: () -> ::Logger
|
|
11
|
-
def formatter: () ->
|
|
13
|
+
def formatter: () -> Formatters::Base
|
|
14
|
+
def ignored_path?: (String path) -> bool
|
|
15
|
+
def sampled?: () -> bool
|
|
12
16
|
end
|
|
13
17
|
end
|
|
@@ -6,7 +6,7 @@ module RequestTrail
|
|
|
6
6
|
COLORS: Hash[Symbol, String]
|
|
7
7
|
RESET: String
|
|
8
8
|
|
|
9
|
-
def initialize: (?colorize: bool) -> void
|
|
9
|
+
def initialize: (?colorize: bool, ?colors: Hash[Symbol, String]) -> void
|
|
10
10
|
def format: (::Rack::Request request, Collector collector) -> String
|
|
11
11
|
|
|
12
12
|
private
|
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.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Chuck Smith
|
|
@@ -54,10 +54,13 @@ files:
|
|
|
54
54
|
- ROADMAP.md
|
|
55
55
|
- Rakefile
|
|
56
56
|
- codecov.yml
|
|
57
|
+
- lib/generators/request_trail/install/install_generator.rb
|
|
58
|
+
- lib/generators/request_trail/install/templates/request_trail.rb.tt
|
|
57
59
|
- lib/request_trail.rb
|
|
58
60
|
- lib/request_trail/collector.rb
|
|
59
61
|
- lib/request_trail/configuration.rb
|
|
60
62
|
- lib/request_trail/formatter.rb
|
|
63
|
+
- lib/request_trail/formatters/base.rb
|
|
61
64
|
- lib/request_trail/formatters/flame_graph.rb
|
|
62
65
|
- lib/request_trail/middleware.rb
|
|
63
66
|
- lib/request_trail/railtie.rb
|
|
@@ -67,6 +70,7 @@ files:
|
|
|
67
70
|
- sig/request_trail/collector.rbs
|
|
68
71
|
- sig/request_trail/configuration.rbs
|
|
69
72
|
- sig/request_trail/formatter.rbs
|
|
73
|
+
- sig/request_trail/formatters/base.rbs
|
|
70
74
|
- sig/request_trail/formatters/flame_graph.rbs
|
|
71
75
|
- sig/request_trail/middleware.rbs
|
|
72
76
|
- sig/request_trail/subscriber.rbs
|