class Rack::ShowExceptions

Rack::ShowExceptions catches all exceptions raised from the app it wraps. It shows a useful backtrace with the sourcefile and clickable context, the whole Rack environment and the request data.

Be careful when you use this on public-facing sites as it could reveal information helpful to attackers.

Constants

CONTEXT

Public Class Methods

new(app) click to toggle source
# File lib/rack/showexceptions.rb, line 18
def initialize(app)
  @app = app
  @template = ERB.new(TEMPLATE)
end

Public Instance Methods

call(env) click to toggle source
# File lib/rack/showexceptions.rb, line 23
def call(env)
  @app.call(env)
rescue StandardError, LoadError, SyntaxError => e
  backtrace = pretty(env, e)
  [500,
   {"Content-Type" => "text/html",
    "Content-Length" => backtrace.join.size.to_s},
   backtrace]
end
pretty(env, exception) click to toggle source
# File lib/rack/showexceptions.rb, line 33
def pretty(env, exception)
  req = Rack::Request.new(env)
  path = (req.script_name + req.path_info).squeeze("/")

  frames = exception.backtrace.map { |line|
    frame = OpenStruct.new
    if line =~ %r(.*?):(\d+)(:in `(.*)')?/
      frame.filename = $1
      frame.lineno = $2.to_i
      frame.function = $4

      begin
        lineno = frame.lineno-1
        lines = ::File.readlines(frame.filename)
        frame.pre_context_lineno = [lineno-CONTEXT, 0].max
        frame.pre_context = lines[frame.pre_context_lineno...lineno]
        frame.context_line = lines[lineno].chomp
        frame.post_context_lineno = [lineno+CONTEXT, lines.size].min
        frame.post_context = lines[lineno+1..frame.post_context_lineno]
      rescue
      end

      frame
    else
      nil
    end
  }.compact

  env["rack.errors"].puts "#{exception.class}: #{exception.message}"
  env["rack.errors"].puts exception.backtrace.map { |l| "\t" + l }
  env["rack.errors"].flush

  [@template.result(binding)]
end