Skip to content

Instantly share code, notes, and snippets.

@dblock
Created December 10, 2012 01:43
Show Gist options
  • Select an option

  • Save dblock/4247912 to your computer and use it in GitHub Desktop.

Select an option

Save dblock/4247912 to your computer and use it in GitHub Desktop.
API logger with Grape under Rails
class ApiLogger < Grape::Middleware::Base
def before
Rails.logger.info "[api] Requested: #{request_log_data.to_json}\n" +
"[api] #{response_log_data[:description]} #{response_log_data[:source_file]}:#{response_log_data[:source_line]}"
end
private
def request_log_data
request_data = {
method: env['REQUEST_METHOD'],
path: env['PATH_INFO'],
query: env['QUERY_STRING']
}
request_data[:user_id] = current_user.id if current_user
request_data
end
def response_log_data
{
description: env['api.endpoint'].options[:route_options][:description],
source_file: env['api.endpoint'].block.source_location[0][(Rails.root.to_s.length+1)..-1],
source_line: env['api.endpoint'].block.source_location[1]
}
end
end
@onomated
Copy link
Copy Markdown

Thanks a lot dblock! Been looking for a way to accomplish this. How do I apply this in my grape api?

@onomated
Copy link
Copy Markdown

Found the Grape::API#use method (http://rubydoc.info/gems/grape/0.2.1/frameshttp://rubydoc.info/gems/grape/0.2.1/frames).
Applied as follows:

class MyAPI < Grape::API
use ApiLogger
end

@hamin
Copy link
Copy Markdown

hamin commented Oct 28, 2013

@dblock how would you suggest handling post params? I have something like this, but I don't think its very clean. Plus the resulting hash that gets logged isn't as clean as post params being logged in rails:

 def request_log_data
    # Escape the ampersand in the POST data.
    rack_input = env["rack.input"].gets

    if rack_input.present?
      rack_input = rack_input.gsub("&","%26")
      params_data = Rack::Utils.parse_query(rack_input, "&")
    else
      params_data = nil
    end

    request_data = {
      method: env['REQUEST_METHOD'],
      path:   env['PATH_INFO'],
      query:  env['QUERY_STRING'],
      params: params_data
    }
    # request_data[:user_id] = current_user.id if current_user
    request_data
  end

Any suggestions?

@dblock
Copy link
Copy Markdown
Author

dblock commented Oct 29, 2013

What does Rails log for POST params? Maybe we can replicate something similar?

@kushkella
Copy link
Copy Markdown

Are the helper methods such as current_user available in the middleware? I have been trying to call those methods but I always get method not found exception.

@pboling
Copy link
Copy Markdown

pboling commented Jan 14, 2015

@hamin - howdy padner! Do you have any updates to the logger? I am just stumbling onto this now. I've been manually logging params where needed, but I want to implement a global solution now.

Update: I went ahead and kicked its ass. Also @dblock

ruby-grape/grape#894

@aserafin
Copy link
Copy Markdown

Just recently created gem that accomplish something very similar. Just few lines of code in your app and you will get one log line per request with: status code, path, request time, db time and parameters.

Check it out: https://rubygems.org/gems/grape_logging

@mxvanzant
Copy link
Copy Markdown

@aserafin -- can you specify where to put those two lines of code -- Ruby beginner here.

@rubyconvict
Copy link
Copy Markdown

@hamin Yes, dirty... I found a nasty bug in this code:

        # Rack::Utils.parse_query('♥')
        # /.../vendor/bundle/ruby/2.2.0/gems/rack-1.6.4/lib/rack/utils.rb:92: warning: regexp match /.../n against to UTF-8 string
        # => {"♥"=>nil}
        # [2] pry(#<Backbone::ApiLogger>)> Rack::Utils.parse_query('%')
        # ArgumentError: invalid %-encoding (%)
        # from /home/xxx/.rvm/rubies/ruby-2.2.0/lib/ruby/2.2.0/uri/common.rb:382:in `decode_www_form_component'

so the percent sign has to be escaped too, in case somebody wants to use it

rack_input = rack_input.gsub("&", "%26").gsub("%", "%25")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment