RequestTrail

CI Gem Version Downloads Ruby codecov

Middleware that traces a request through all the layers (middleware, controller, ActiveRecord, cache) and dumps a flame-graph-style summary to the log.

Table of Contents

Installation

Add to your application's Gemfile:

bundle add request_trail

Or install directly:

gem install request_trail

Back to top

Usage

Rails

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.

When controller tracing is active, output is tiered:

[RequestTrail] GET /orders 142ms
  controller  104ms
    sql        38ms (7 queries)
    cache       2ms (4 hits, 1 miss)
    view       22ms

Without controller data (plain Rack apps), a single-line summary is emitted:

[RequestTrail] GET /orders 142ms | SQL: 7/38.3ms | Cache: 4 hits, 1 miss, 2.0ms

Flame graph formatter

Opt into the ASCII flame-graph formatter for a visual proportional breakdown:

RequestTrail.configure do |config|
  config.formatter = RequestTrail::Formatters::FlameGraph.new
end

Output (with ANSI colour when stdout is a TTY):

[RequestTrail] GET /orders 142ms ████████████████████████████████████
  controller   104ms ████████████████████████████
    sql         38ms █████████
    cache        2ms
    view        22ms █████

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).

Override any layer's ANSI code with the colors: option:

RequestTrail::Formatters::FlameGraph.new(
  colorize: true,
  colors: { controller: "\e[36m", sql: "\e[31m" }
)

Unspecified layers keep their defaults.

Custom formatters

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:

class MyFormatter
  include RequestTrail::Formatters::Base

  def format(request, collector)
    "#{request.request_method} #{request.path} took #{collector.elapsed_ms.round}ms"
  end
end

RequestTrail.configure do |config|
  config.formatter = MyFormatter.new
end

format receives:

  • request — a Rack::Request with the current HTTP request
  • 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

Installation generator

Run the generator to scaffold the initializer:

rails generate request_trail:install

This creates config/initializers/request_trail.rb pre-populated with all available options and their defaults.

Configuration

Add an initializer to customize behavior:

# config/initializers/request_trail.rb
RequestTrail.configure do |config|
  config.enabled       = true      # set to false to disable entirely
  config.log_level     = :info     # Rails logger level (:debug, :info, :warn)
  config.threshold_ms  = 200       # only log requests slower than this (0 = log all)
  config.logger        = nil       # defaults to Rails.logger
  config.formatter     = RequestTrail::Formatters::FlameGraph.new  # optional

  # skip tracing for specific paths (strings = exact match, regexes = pattern match)
  config.ignore_paths  = ["/health", "/up", /^\/assets/]

  # trace only N% of requests — useful in high-traffic production environments
  config.sample_rate   = 0.1      # 0.0 = never, 1.0 = always (default)
end

Non-Rails (plain Rack)

Insert the middleware manually and attach the subscriber:

require "request_trail"

RequestTrail::Subscriber.attach

use RequestTrail::Middleware
run MyApp

Back to top

Development

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.

Running tests

bundle exec rake spec          # full test suite
bundle exec rspec spec/path/to/file_spec.rb  # single file
bundle exec rspec spec/path/to/file_spec.rb:42  # single example

Dummy app

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.

Start the server:

bundle exec rackup spec/dummy/config.ru --port 3000

Then make a request and tail the log:

curl http://localhost:3000/ping
tail -f spec/dummy/log/request_trail.log

You should see tiered output like:

[RequestTrail] GET /ping 33ms
  controller  3ms
    sql        0.0ms (0 queries)
    cache      0.0ms (0 hits, 0 misses)
    view       2.8ms

Back to top

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/eclectic-coding/request-trail.

Back to top

License

The gem is available as open source under the terms of the MIT License.

Back to top