class Utopia::Exceptions::Mailer

A middleware which catches all exceptions raised from the app it wraps and sends a useful email with the exception, stacktrace, and contents of the environment.

Constants

DEFAULT_FROM
DEFAULT_SUBJECT
LOCAL_SMTP

A basic local non-authenticated SMTP server.

REQUEST_KEYS

Public Class Methods

new(app, to: "postmaster", from: DEFAULT_FROM, subject: DEFAULT_SUBJECT, delivery_method: LOCAL_SMTP, dump_environment: false) click to toggle source

@param to [String] The address to email error reports to. @param from [String] The from address for error reports. @param subject [String] The subject template which can access attributes defined by `#attributes_for`. @param delivery_method [Object] The delivery method as required by the mail gem. @param dump_environment [Boolean] Attach `env` as `environment.yaml` to the error report.

# File lib/utopia/exceptions/mailer.rb, line 45
def initialize(app, to: "postmaster", from: DEFAULT_FROM, subject: DEFAULT_SUBJECT, delivery_method: LOCAL_SMTP, dump_environment: false)
        @app = app
        
        @to = to
        @from = from
        @subject = subject
        @delivery_method = delivery_method
        @dump_environment = dump_environment
end

Public Instance Methods

call(env) click to toggle source
# File lib/utopia/exceptions/mailer.rb, line 67
def call(env)
        begin
                return @app.call(env)
        rescue => exception
                send_notification exception, env
                
                raise
        end
end
freeze() click to toggle source
Calls superclass method
# File lib/utopia/exceptions/mailer.rb, line 55
def freeze
        return self if frozen?
        
        @to.freeze
        @from.freeze
        @subject.freeze
        @delivery_method.freeze
        @dump_environment.freeze
        
        super
end

Private Instance Methods

attributes_for(exception, env) click to toggle source
# File lib/utopia/exceptions/mailer.rb, line 135
def attributes_for(exception, env)
        {
                exception: exception.class.name,
                pid: $$,
                cwd: Dir.getwd,
        }
end
extract_body(env) click to toggle source
# File lib/utopia/exceptions/mailer.rb, line 175
def extract_body(env)
        if io = env['rack.input']
                io.rewind if io.respond_to?(:rewind)
                io.read
        end
end
generate_backtrace(io, exception, prefix: "Exception") click to toggle source
# File lib/utopia/exceptions/mailer.rb, line 86
def generate_backtrace(io, exception, prefix: "Exception")
        io.puts "#{prefix} #{exception.class.name}: #{exception.to_s}"
        
        if exception.respond_to?(:backtrace)
                io.puts exception.backtrace
        else
                io.puts exception.to_s
        end
        
        if cause = exception.cause
                generate_backtrace(io, cause, prefix: "Caused by")
        end
end
generate_body(exception, env) click to toggle source
# File lib/utopia/exceptions/mailer.rb, line 100
def generate_body(exception, env)
        io = StringIO.new
        
        # Dump out useful rack environment variables:
        request = Rack::Request.new(env)
        
        io.puts "#{request.request_method} #{request.url}"
        
        # TODO embed `rack.input` if it's textual?
        # TODO dump and embed `utopia.variables`?
        
        io.puts
        
        REQUEST_KEYS.each do |key|
                value = request.send(key)
                io.puts "request.#{key}: #{value.inspect}"
        end
        
        request.params.each do |key, value|
                io.puts "request.params.#{key}: #{value.inspect}"
        end
        
        io.puts
        
        env.select{|key,_| key.start_with? 'HTTP_'}.each do |key, value|
                io.puts "#{key}: #{value.inspect}"
        end
        
        io.puts
        
        generate_backtrace(io, exception)
        
        return io.string
end
generate_mail(exception, env) click to toggle source
# File lib/utopia/exceptions/mailer.rb, line 143
def generate_mail(exception, env)
        mail = Mail.new(
                :from => @from,
                :to => @to,
                :subject => @subject % attributes_for(exception, env)
        )
        
        mail.text_part = Mail::Part.new
        mail.text_part.body = generate_body(exception, env)

        if body = extract_body(env) and body.size > 0
                mail.attachments['body.bin'] = body
        end
        
        if @dump_environment
                mail.attachments['environment.yaml'] = YAML.dump(env)
        end

        return mail
end
send_notification(exception, env) click to toggle source
# File lib/utopia/exceptions/mailer.rb, line 164
def send_notification(exception, env)
        mail = generate_mail(exception, env)
        
        mail.delivery_method(*@delivery_method) if @delivery_method
        
        mail.deliver
rescue => mail_exception
        $stderr.puts mail_exception.to_s
        $stderr.puts mail_exception.backtrace
end