Logging Internal Server Errors «

Code: , , , , , ,
Comments Off on Logging Internal Server Errors

A good rule of thumb for websites is that for every hundred users who runs into a bug, one will let you know. This is when the user can even recognize there’s an error because they get a big “500: Internal Server Error”. You might get more reports if you have a very small userbase who know you, but don’t count on it. You have to log errors if you want to find out about them, so here’s some code to do that in Rails.

By default Rails logs all accesses and errors to production.log, but I decided to save errors to individual files to save the hassle of trying to sift them out of that haystack. I considered logging them to a database table, mailing them, or providing an RSS feed but in the end decided to punt on it. Every added bit of complexity in rescue_error_in_public is another opportunity for something else to go wrong and the error to be lost. It’s easy to write a cron job or other service to pick them up from files and do something useful with them.

Save this file as lib/handle_errors.rb:

module ActionController
module Rescue
def rescue_action_in_public(exception)
case exception
when ::ActionController::RoutingError, ::ActionController::UnknownAction
render_text(IO.read(File.join(RAILS_ROOT, 'public', '404.html')), "404 Not Found")
render_text(IO.read(File.join(RAILS_ROOT, 'public', '500.html')), "500 Internal Error")
if defined? ERROR_PATH
File.open(File.join(ERROR_PATH, Time.now.strftime("exception_%Y-%m-%d_%H:%M:%S_#{rand(99999)}")), 'w') do |file|
try_printing(file, "request") { file.print "Request to #{request.request_uri} from #{request.remote_ip}nn" }
try_printing(file, "params") { file.print "Params #{params.to_yaml}nn" }
try_printing(file, "exception") { file.print(
"#{exception.class} (#{exception.message}):n " +
clean_backtrace(exception).join("n ") +
) }
try_printing(file, "session") { file.print "Session #{session.to_yaml}nn" }

# def rescue_action_locally(exception)
# rescue_action_in_public(exception)
# end

def try_printing file, title
rescue Exception => exception
file.print "Failed to print #{title}: #{exception.class} (#{exception.message})nn"

Edit your config/environments.rb to require 'handle_errors' and, in your specific config/environment files, add ERROR_PATH = '/path/to/save'. You can see this working in development by uncommenting the rescue_action_locally.

Security note: If you're dealing with credit cards or other sensitive financial or personal data, you'll want to scrub that from params and session before logging.