RequestTagger

I did what I did

Can't say "hey man, I'm just a kid."

--Nickelback

Inject a request ID tag into all ActiveRecord queries and HTTP requests made within your [Rails] application.

Any web service requests or database queries your application makes in a given request can be tied together by coalescing your log files in your favourite log aggregator, giving a full picture of every request on your system.

An incoming HTTP header is used as the ID for all subsequent requests.

SQL queries are prepended with a comment that will look something like this:

/* request-id: abc123 */ SELECT * FROM ...

HTTP requests will include an extra header:

X-Request-Id: abc123

The implementation has borrowed ideas from RSpec's allow_any_instance_of and webmock's stub_request. Their source code was used as an invaluable reference during development. Thanks to the developers of both libraries for their hard work.

Installation

Add this line to your application's Gemfile:

gem 'request_tagger'

And then rebuild your bundle:

$ bundle install

Usage

The only things you need to do are create an initializer:

# config/initializers/request_tagger.rb
RequestTagger.start

and include the RequestTagger::TagRequests module in your base controller:

class ApplicationController < ActionController::Base
  include RequestTagger::TagRequests
end

You can pass the following options to RequestTagger.start (values shown are the defaults):

RequestTagger.start(
  tag_sql: true, # Tag all ActiveRecord SQL queries
  tag_http: true, # Tag all HTTP requests
  http_tag_name: 'X-Request-Id', # Header to use for outbound requests
  sql_tag_name: 'request-id', # Identifier to use in SQL tags
  header: 'HTTP_X_REQUEST_ID' # Header to watch for inbound requests*
)

* Note that an inbound HTTP header e.g. X-Request-Id will be transformed by Rack to HTTP_X_REQUEST_ID so take this into account when setting the header option.

An example usage would be the $request_id variable provided by nginx:

location / {
    proxy_set_header X-Request-Id $request_id;
}

Setting a request ID manually

If you want to manually assign the request ID to be used in tags, just add overwrite the following method in your base controller:

  private

  def __request_tagger__set_request_id__
    RequestTagger.request_id = 'my-custom-request-id'
  end

Caveats

  • Only web requests made by Net::HTTP are intercepted. Most popular HTTP libraries use this at their core, including Faraday and HTTParty so this should cover the vast majority of cases but feel free to submit a pull request to add more drivers.
  • Since Net::HTTP and ActiveRecord are monkeypatched in similar ways to how RSpec and webmock operate, you probably do not want to enable RequestTagger in your test environment. Use unless Rails.env.test? in your initializer.

Development

Clone the repository and submit a pull requests to fix any bugs or add any new features.

Write tests for any new code you write and ensure all tests pass before submitting:

$ bin/rspec

Please also run Rubocop and fix any issues before making a pull request:

$ bin/rubocop

License

RequestTagger is licensed under the MIT license. Do whatever you like with the code, just give credit where it's due.