require 'mail' require 'erubis' module Rackamole::Alert class Emole # retrieves erb template dir def self.template_root() @template_root ||= File.join( File.dirname(__FILE__), %w[templates] ); end # Send an email notification for particular moled feature. An email will # be sent based on the configuration :email defined on the # Rack::Mole component. This option must specify the to and from addresses and the conditions # that will trigger the email defined by alert_on for the type of # moled features to track via email. The notification will be sent via Mail, # so you will need to make sure it is properly configured for your domain. # NOTE: This is just a notification mechanism. All moled event will be either logged # or persisted in the db regardless. # # === Parameters: # options :: Mole options. The :email key minimaly contains :from for the from address. Must be a valid domain. # :: And a :to, n array of email addresses for recipients to be notified. # args :: The gathered information from the mole. # def self.deliver_alert( logger, options, args ) @options = options @args = args tmpl = File.join( template_root, %w[alert.erb] ) template = Erubis::Eruby.new( IO.read( tmpl ), :trim => true ) body = template.result( binding ) timing = request_time( args ) if args[:type] == Rackamole.perf subject = "Rackamole <#{alert_type( args )}>#{timing ? " #{timing} " : ' '}on #{args[:app_name]}.#{args[:host]} for user #{args[:user_name]}" mail = Mail.new do from options[:email][:from] to options[:email][:to] subject subject body body end mail.deliver! mail rescue => boom logger.error( "Rackamole email alert failed with error `#{boom}" ) boom.backtrace.each { |l| logger.error l } end def self.section( title ) buff = [] buff << "-"*40 buff << "o #{title.capitalize}\n" buff << self.send( title.downcase ) buff << "\n" buff.join( "\n" ) end # ========================================================================= private def self.args @args end def self.humanize( key ) key end def self.feature_type case args[:type] when Rackamole.feature "Feature" when Rackamole.perf "Performance" when Rackamole.fault "Fault" end end # Format args and spit out into buffer def self.spew( key, silent=false ) buff = [] value = args[key] value = request_time( args ) if key == :request_time _spew( buff, '--', (silent ? '' : ' '), key, value, silent ) buff.join( "\n" ) end def self._spew( buff, sep, indent, key, value, silent ) if value.is_a?( Hash ) buff << "#{indent}#{humanize( key )}:" unless silent value.each_pair{ |k,v| _spew( buff, sep, indent+" ", k, v, false ) } elsif value.is_a?( Array ) buff << "#{indent}#{humanize( key )}:" unless silent value.each { |s| _spew( buff, sep, indent+" ", '', s, false ) } else buff << "#{indent}#{humanize( key )}: #{value}" end end # What just happened? def self.what buff = [] case args[:type] when Rackamole.fault buff << spew( :fault ) << spew( :stack ) + "\n" when Rackamole.perf buff << "#{spew( :request_time )} [#{@options[:perf_threshold]}]" end buff << spew( :user_name) << spew( :url ) << spew( :path ) << spew( :status ) buff << spew( :method ) buff << spew( :request_time ) unless args[:type] == Rackamole.perf buff << spew( :ip ) buff.join( "\n" ) end def self.server() [ spew( :host ), spew( :software ), spew( :ruby_version ) ]; end def self.client() [ spew( :machine, true ) ]; end def self.params() [ spew( :params, true ) ]; end def self.session() [ spew( :session, true ) ]; end def self.browser() [ spew( :browser, true ) ]; end def self.headers() [ spew( :headers, true ) ]; end # Dump request time if any... def self.request_time( args ) return '' unless args[:request_time] "%1.2f" % args[:request_time] end # Identify the type of alert... def self.alert_type( args ) case args[:type] when Rackamole.feature "Feature" when Rackamole.perf "Performance" when Rackamole.fault "Fault" end end end end