require 'erb' module ActionController # The Failsafe middleware is usually the top-most middleware in the Rack # middleware chain. It returns the underlying middleware's response, but if # the underlying middle raises an exception then Failsafe will log the # exception into the Rails log file, and will attempt to return an error # message response. # # Failsafe is a last resort for logging errors and for telling the HTTP # client that something went wrong. Do not confuse this with the # ActionController::Rescue module, which is responsible for catching # exceptions at deeper levels. Unlike Failsafe, which is as simple as # possible, Rescue provides features that allow developers to hook into # the error handling logic, and can customize the error message response # based on the HTTP client's IP. class Failsafe cattr_accessor :error_file_path self.error_file_path = Rails.public_path if defined?(Rails.public_path) def initialize(app) @app = app end def call(env) @app.call(env) rescue Exception => exception # Reraise exception in test environment if defined?(Rails) && Rails.env.test? raise exception else failsafe_response(exception) end end private def failsafe_response(exception) log_failsafe_exception(exception) [500, {'Content-Type' => 'text/html'}, [failsafe_response_body]] rescue Exception => failsafe_error # Logger or IO errors $stderr.puts "Error during failsafe response: #{failsafe_error}" end def failsafe_response_body error_template_path = "#{self.class.error_file_path}/500.html" if File.exist?(error_template_path) begin result = render_template(error_template_path) rescue Exception result = nil end else result = nil end if result.nil? result = "

500 Internal Server Error

" << "If you are the administrator of this website, then please read this web " << "application's log file to find out what went wrong." end result end # The default 500.html uses the h() method. def h(text) # :nodoc: ERB::Util.h(text) end def render_template(filename) ERB.new(File.read(filename)).result(binding) end def log_failsafe_exception(exception) message = "/!\\ FAILSAFE /!\\ #{Time.now}\n Status: 500 Internal Server Error\n" message << " #{exception}\n #{exception.backtrace.join("\n ")}" if exception failsafe_logger.fatal(message) failsafe_logger.flush if failsafe_logger.respond_to?(:flush) end def failsafe_logger if defined?(Rails) && Rails.logger Rails.logger else Logger.new($stderr) end end end end