Peter Marklund

Peter Marklund's Home

2007-08-12

Rails Deployment: Dealing with 404s

You probably use Jamis Buck's Exception Notifier plugin to be notified by email of exceptions thrown on your production server. Typically though you don't want to be notified of 404s, at least not when they are caused by an RSS reader requesting an RSS feed that no longer exists, or some spam robot or search engine fetching URLs fetching URLs no longer supported. When the Exception Notifier plugin catches an exception and it is one of ActiveRecord::RecordNotFound, ActionController::UnknownController, or ActionController::UnknownAction, it figures that it is a 404. By default then no email is sent and instead the method render_404 is invoked which in turn renders public/404.html. This is quite appropriate. Unofortunately though you can't trace in the log what the request looked like that caused the 404. The other limitation is that if no route matches a n incoming request, then an ActionController::RoutingError is thrown, yielding a 500 and an exception email. To get consistency in how 404s are dealt with, i.e. make sure they are always fully logged and tracable, but never cause email notifications, I override the render_404 method from the Exception Notifier plugin in my ApplicationController:

  # Overriding the version in ExceptionNotifiable so that we can log the environment and figure
  # out where the request is coming from.
  def render_404
    logger.error("render_404 invoked for request_uri=#{request.request_uri} and env=#{request.env.inspect}")
    super
  end

I then add a catch all route at the end of my routes.rb file:

  map.connect '*anything', :controller => 'application', :action => 'render_404'

4 comment(s)

Comments

Jay said 2009-01-20 08:03:

Thanks for the log_error override. This worked like a charm!
--------------------------------------------------------------------------------

Peter Marklund said 2008-08-27 02:28:

Another, and it seems the intended way to customize error logging is to override the log_error method in your application controller. I did that to avoid having fat traces from 404s pollute my log file: # Overriding the method in rescue.rb so that we don't pollute the log with stack traces from # 404 requests (RoutingError). def log_error(exception) #:doc: ActiveSupport::Deprecation.silence do if ActionView::TemplateError === exception || ActionController::RoutingError === exception logger.fatal(exception.to_s) else logger.fatal( "\n\n#{exception.class} (#{exception.message}):\n " + clean_backtrace(exception).join("\n ") + "\n\n" ) end end end
--------------------------------------------------------------------------------

Peter Marklund said 2007-08-13 00:24:

Ah, excellent, so then the catch all route won't be needed anymore. Thanks Jarkko.
--------------------------------------------------------------------------------

Jarkko Laine said 2007-08-13 00:12:

"The other limitation is that if no route matches a n incoming request, then an ActionController::RoutingError is thrown, yielding a 500 and an exception email." Actually, the latest version of the plugin does treat RoutingErrors as 404: http://svn.rubyonrails.org/rails/plugins/exception_notification/lib/exception_notifiable.rb So far, I've added it in exception_notifiable.rb by hand.
--------------------------------------------------------------------------------