How to log an entire request (headers, body, etc) for a certain url?

12,840

Solution 1

It looks like request.headers returns a hash, but in fact, it returns an instance of Http::Headers that doesn't have a keys method defined.

But a Http::Headers responds to env which returns the original env hash. Therefore the following works:

http_request_header_keys = request.headers.env.keys.select do |header_name| 
  header_name.match("^HTTP.*")
end

Or you can just iterate over all key-value-pairs and copy them into another hash:

http_envs = {}.tap do |envs|
  request.headers.each do |key, value|
    envs[key] = value if key.downcase.starts_with?('http')
  end
end

logger.info <<-LOG.squish
  Received     #{request.method.inspect} 
  to           #{request.url.inspect} 
  from         #{request.remote_ip.inspect}.  
  Processing 
  with headers #{http_envs.inspect} 
  and params   #{params.inspect}"
LOG

To wrap this up:

around_action :log_everything, only: :index

def index
  # ...
end

private
def log_everything
  log_headers
  yield
ensure
  log_response
end

def log_headers
  http_envs = {}.tap do |envs|
    request.headers.each do |key, value|
      envs[key] = value if key.downcase.starts_with?('http')
    end
  end

  logger.info "Received #{request.method.inspect} to #{request.url.inspect} from #{request.remote_ip.inspect}. Processing with headers #{http_envs.inspect} and params #{params.inspect}"
end

def log_response
  logger.info "Responding with #{response.status.inspect} => #{response.body.inspect}"
end

Solution 2

I'm using this to get the full headers:

request.headers.env.select do |k, _| 
  k.downcase.start_with?('http') ||
  k.in?(ActionDispatch::Http::Headers::CGI_VARIABLES)
end
Share:
12,840

Related videos on Youtube

Admin
Author by

Admin

Updated on September 28, 2022

Comments

  • Admin
    Admin 2 months

    I need to log all the requests including HTTP headers, bodies, etc to a certain url. I've tried this code:

    def index
      global_request_logging
    end
    
    private
    
    def global_request_logging 
        http_request_header_keys = request.headers.keys.select{|header_name| header_name.match("^HTTP.*")}
        http_request_headers = request.headers.select{|header_name, header_value| http_request_header_keys.index(header_name)}
        logger.info "Received #{request.method.inspect} to #{request.url.inspect} from #{request.remote_ip.inspect}.  Processing with headers #{http_request_headers.inspect} and params #{params.inspect}"
        begin 
          yield 
        ensure 
          logger.info "Responding with #{response.status.inspect} => #{response.body.inspect}"
        end 
      end 
    

    But it said request.headers doesn't contain a method named keys. Also I figure there should be an easier way or standard to do it. Preferably, not to use a gem.

  • Admin
    Admin almost 8 years
    Well, that's a half of the solution. How about logging the body?
  • spickermann
    spickermann almost 8 years
    @jawanam : I updated my answer with an example of an around_action