Rack middleware to help log detailed request information
Pull request Compare This branch is 3 commits behind BLC:master.
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
lib/rack
spec
.gitignore
Gemfile
README.md
Rakefile
rack-request_logger.gemspec

README.md

rack-request_logger is Rack middleware that collects information about incoming requests (and the responses) and provides it to a class of your choosing. This allows for detailed logging or processing of everything that happens at the application server level.

Gemfile

gem 'rack-request_logger', :require => 'rack/request_logger'

Examples

TODO: Add more interesting examples in a samples directory, showing off things like field filtering and logging control

Here are a few simple examples:

Flat File Logging

./config.ru

...
require 'lib/file_request_logger'
use FileRequestLogger
...

lib/file_request_logger.rb

class FileRequestLogger < Rack::RequestLogger
  self.log_handler = self

  def self.write(params)
    File.open('log/requests.log', 'a') do |f|
      f.write "#{params}\n"
    end
  end
end

Database Logging with ActiveRecord/Rails

./config.ru

...
use Rack::RequestLogger
...

config/initializers/request_logging.rb

Rack::RequestLogger.log_handler = RequestLogEntry

db/migrate/0_create_request_log.rb

class CreateRequestLog < ActiveRecord::Migration
  def self.up
    create_table :request_log do |t|

      #These are the default fields provided to the write method

      t.integer :user_id
      t.string  :request_id

      t.string  :url
      t.string  :method
      t.integer :status

      t.text :request_headers
      t.text :request_body

      t.text :response_headers
      t.text :response_body
      t.text :error

      t.datetime :request_time
      t.datetime :response_time
      t.string   :remote_host
    end
  end

  def self.down
    drop_table :request_log
  end
end

app/models/request_log_entry.rb

class RequestLogEntry < ActiveRecord::Base
  set_table_name :request_log
  class << self
    alias :write :create
  end
end

Admittedly hacky way to prevent writing out massive insert statements to the console in development:

config/initializers/request_logging.rb

...
if Rails.env.development?
  class RequestLoglessLogger < ActiveSupport::BufferedLogger
    def add(severity, message = nil, progname = nil, &block)
      message.sub!(/(insert into `request_log`).*/i, '\1 ...') if message
      super
    end
  end
  ActiveRecord::Base.logger = RequestLoglessLogger.new(STDOUT)
end
...

Template Methods

If you are inheriting from the Rack::RequestLogger class, there are several methods that can be overridden to provide more control over what is happening.

# Override to prevent requests from being logged under certain conditions.
# By default, it always returns true.
log_request?(env, request, status, response_headers, response_body)

# The 'params' argument is the hash that will be passed to the handler's write method.
# Modify it as desired in this method.
add_log_params(env, status, response_headers, response_body, params)

# Override these methods to return a modified values of the
# indicated field before they are passed to the handler.
# Useful for filtering sensitive information (e.g. passwords) from your logs.
filter_request_headers(hash)
filter_response_headers(hash)
filter_request_body(string)
filter_response_body(string)

# Override to change where any errors to be logged are pulled from.
# By default, it is "env['request_logger.error'] || env['sinatra.error']".
app_error(env)

Rake Tasks

If you happen to be logging to a DB using ActiveRecord, there are some rake tasks for quickly grabbing latest requests from the command line. These are:

rake log:request        # Print the most recent request
rake log:request:get    # Print the most recent GET request
rake log:request:put    # Print the most recent PUT request
rake log:request:post   # Print the most recent POST request
rake log:request:delete # Print the most recent DELETE request

Each of these will attempt to print the default fields in a sort of sensible order (those from the ActiveRecord example above), and will then also print any custom columns you may have defined. If you have named some columns differently or not logged them at all, things should still work.

In addition, each task accepts an SQL condition to help narrow down your search, like so:

rake log:request["url like '%/api/%'"]