1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00
rails--rails/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb
Aaron Patterson 6d85804bc6 ExceptionWrapper doesn't need to know about env
ExceptionWrapper only cares about the backtrace cleaner, so lets just
pass the cleaner to the wrapper.  It does not need to know that env
exists or what key the backtrace cleaner is stored in
2015-08-06 15:59:44 -07:00

141 lines
4.2 KiB
Ruby

require 'action_dispatch/http/request'
require 'action_dispatch/middleware/exception_wrapper'
require 'action_dispatch/routing/inspector'
require 'action_view'
require 'action_view/base'
require 'pp'
module ActionDispatch
# This middleware is responsible for logging exceptions and
# showing a debugging page in case the request is local.
class DebugExceptions
RESCUES_TEMPLATE_PATH = File.expand_path('../templates', __FILE__)
class DebugView < ActionView::Base
def debug_params(params)
clean_params = params.clone
clean_params.delete("action")
clean_params.delete("controller")
if clean_params.empty?
'None'
else
PP.pp(clean_params, "", 200)
end
end
def debug_headers(headers)
if headers.present?
headers.inspect.gsub(',', ",\n")
else
'None'
end
end
def debug_hash(object)
object.to_hash.sort_by { |k, _| k.to_s }.map { |k, v| "#{k}: #{v.inspect rescue $!.message}" }.join("\n")
end
end
def initialize(app, routes_app = nil)
@app = app
@routes_app = routes_app
end
def call(env)
_, headers, body = response = @app.call(env)
if headers['X-Cascade'] == 'pass'
body.close if body.respond_to?(:close)
raise ActionController::RoutingError, "No route matches [#{env['REQUEST_METHOD']}] #{env['PATH_INFO'].inspect}"
end
response
rescue Exception => exception
raise exception if env['action_dispatch.show_exceptions'] == false
render_exception(env, exception)
end
private
def render_exception(env, exception)
backtrace_cleaner = env['action_dispatch.backtrace_cleaner']
wrapper = ExceptionWrapper.new(backtrace_cleaner, exception)
log_error(env, wrapper)
if env['action_dispatch.show_detailed_exceptions']
request = Request.new(env)
traces = wrapper.traces
trace_to_show = 'Application Trace'
if traces[trace_to_show].empty? && wrapper.rescue_template != 'routing_error'
trace_to_show = 'Full Trace'
end
if source_to_show = traces[trace_to_show].first
source_to_show_id = source_to_show[:id]
end
template = DebugView.new([RESCUES_TEMPLATE_PATH],
request: request,
exception: wrapper.exception,
traces: traces,
show_source_idx: source_to_show_id,
trace_to_show: trace_to_show,
routes_inspector: routes_inspector(exception),
source_extracts: wrapper.source_extracts,
line_number: wrapper.line_number,
file: wrapper.file
)
file = "rescues/#{wrapper.rescue_template}"
if request.xhr?
body = template.render(template: file, layout: false, formats: [:text])
format = "text/plain"
else
body = template.render(template: file, layout: 'rescues/layout')
format = "text/html"
end
render(wrapper.status_code, body, format)
else
raise exception
end
end
def render(status, body, format)
[status, {'Content-Type' => "#{format}; charset=#{Response.default_charset}", 'Content-Length' => body.bytesize.to_s}, [body]]
end
def log_error(env, wrapper)
logger = logger(env)
return unless logger
exception = wrapper.exception
trace = wrapper.application_trace
trace = wrapper.framework_trace if trace.empty?
ActiveSupport::Deprecation.silence do
message = "\n#{exception.class} (#{exception.message}):\n"
message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
message << " " << trace.join("\n ")
logger.fatal("#{message}\n\n")
end
end
def logger(env)
env['action_dispatch.logger'] || stderr_logger
end
def stderr_logger
@stderr_logger ||= ActiveSupport::Logger.new($stderr)
end
def routes_inspector(exception)
if @routes_app.respond_to?(:routes) && (exception.is_a?(ActionController::RoutingError) || exception.is_a?(ActionView::Template::Error))
ActionDispatch::Routing::RoutesInspector.new(@routes_app.routes.routes)
end
end
end
end