├── lib ├── exception_notifier │ ├── views │ │ └── exception_notifier │ │ │ ├── _backtrace.text.erb │ │ │ ├── _title.text.erb │ │ │ ├── _session.text.erb │ │ │ ├── _request.text.erb │ │ │ ├── _environment.text.erb │ │ │ └── exception_notification.text.erb │ └── notifier.rb └── exception_notifier.rb ├── exception_notification.gemspec └── README /lib/exception_notifier/views/exception_notifier/_backtrace.text.erb: -------------------------------------------------------------------------------- 1 | <%= raw @backtrace.join("\n") %> 2 | -------------------------------------------------------------------------------- /lib/exception_notifier/views/exception_notifier/_title.text.erb: -------------------------------------------------------------------------------- 1 | ------------------------------- 2 | <%= raw title.to_s.humanize %>: 3 | ------------------------------- 4 | -------------------------------------------------------------------------------- /lib/exception_notifier/views/exception_notifier/_session.text.erb: -------------------------------------------------------------------------------- 1 | * session id: <%= raw @request.session['session_id'].inspect.html_safe %> 2 | * data: <%= raw PP.pp(@request.session, "") %> 3 | -------------------------------------------------------------------------------- /lib/exception_notifier/views/exception_notifier/_request.text.erb: -------------------------------------------------------------------------------- 1 | * URL : <%= raw @request.url %> 2 | * IP address: <%= raw @request.remote_ip %> 3 | * Parameters: <%= raw @request.filtered_parameters.inspect %> 4 | * Rails root: <%= raw Rails.root %> 5 | -------------------------------------------------------------------------------- /exception_notification.gemspec: -------------------------------------------------------------------------------- 1 | Gem::Specification.new do |s| 2 | s.name = 'exception_notification' 3 | s.version = '1.0.0' 4 | s.authors = ["Jamis Buck", "Josh Peek"] 5 | s.date = %q{2010-03-13} 6 | s.summary = "Exception notification by email for Rails apps" 7 | s.email = "timocratic@gmail.com" 8 | 9 | s.files = ['README'] + Dir['lib/**/*'] 10 | s.require_path = 'lib' 11 | end 12 | -------------------------------------------------------------------------------- /lib/exception_notifier/views/exception_notifier/_environment.text.erb: -------------------------------------------------------------------------------- 1 | <% filtered_env = @request.filtered_env -%> 2 | <% max = filtered_env.keys.max { |a, b| a.length <=> b.length } -%> 3 | <% filtered_env.keys.sort.each do |key| -%> 4 | * <%= raw("%-*s: %s" % [max.length, key, inspect_object(filtered_env[key])]) %> 5 | <% end -%> 6 | 7 | * Process: <%= raw $$ %> 8 | * Server : <%= raw `hostname -s`.chomp %> 9 | -------------------------------------------------------------------------------- /lib/exception_notifier/views/exception_notifier/exception_notification.text.erb: -------------------------------------------------------------------------------- 1 | A <%= @exception.class %> occurred in <%= @kontroller.controller_name %>#<%= @kontroller.action_name %>: 2 | 3 | <%= raw @exception.message %> 4 | <%= raw @backtrace.first %> 5 | 6 | <% sections = @sections.map do |section| 7 | summary = render(section).strip 8 | unless summary.blank? 9 | title = render("title", :title => section).strip 10 | "#{title}\n\n#{summary.gsub(/^/, " ")}\n\n" 11 | end 12 | end %> 13 | <%= raw sections.join %> 14 | -------------------------------------------------------------------------------- /lib/exception_notifier.rb: -------------------------------------------------------------------------------- 1 | require 'action_dispatch' 2 | require 'exception_notifier/notifier' 3 | 4 | class ExceptionNotifier 5 | def self.default_ignore_exceptions 6 | [].tap do |exceptions| 7 | exceptions << ActiveRecord::RecordNotFound if defined? ActiveRecord 8 | exceptions << AbstractController::ActionNotFound if defined? AbstractController 9 | exceptions << ActionController::RoutingError if defined? ActionController 10 | end 11 | end 12 | 13 | def initialize(app, options = {}) 14 | @app, @options = app, options 15 | @options[:ignore_exceptions] ||= self.class.default_ignore_exceptions 16 | end 17 | 18 | def call(env) 19 | @app.call(env) 20 | rescue Exception => exception 21 | options = (env['exception_notifier.options'] ||= {}) 22 | options.reverse_merge!(@options) 23 | 24 | unless Array.wrap(options[:ignore_exceptions]).include?(exception.class) 25 | Notifier.exception_notification(env, exception).deliver 26 | env['exception_notifier.delivered'] = true 27 | end 28 | 29 | raise exception 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/exception_notifier/notifier.rb: -------------------------------------------------------------------------------- 1 | require 'action_mailer' 2 | require 'pp' 3 | 4 | class ExceptionNotifier 5 | class Notifier < ActionMailer::Base 6 | self.mailer_name = 'exception_notifier' 7 | self.append_view_path "#{File.dirname(__FILE__)}/views" 8 | 9 | class << self 10 | def default_sender_address 11 | %("Exception Notifier" ) 12 | end 13 | 14 | def default_exception_recipients 15 | [] 16 | end 17 | 18 | def default_email_prefix 19 | "[ERROR] " 20 | end 21 | 22 | def default_sections 23 | %w(request session environment backtrace) 24 | end 25 | 26 | def default_options 27 | { :sender_address => default_sender_address, 28 | :exception_recipients => default_exception_recipients, 29 | :email_prefix => default_email_prefix, 30 | :sections => default_sections } 31 | end 32 | end 33 | 34 | class MissingController 35 | def method_missing(*args, &block) 36 | end 37 | end 38 | 39 | def exception_notification(env, exception) 40 | @env = env 41 | @exception = exception 42 | @options = (env['exception_notifier.options'] || {}).reverse_merge(self.class.default_options) 43 | @kontroller = env['action_controller.instance'] || MissingController.new 44 | @request = ActionDispatch::Request.new(env) 45 | @backtrace = clean_backtrace(exception) 46 | @sections = @options[:sections] 47 | data = env['exception_notifier.exception_data'] || {} 48 | 49 | data.each do |name, value| 50 | instance_variable_set("@#{name}", value) 51 | end 52 | 53 | prefix = "#{@options[:email_prefix]}#{@kontroller.controller_name}##{@kontroller.action_name}" 54 | subject = "#{prefix} (#{@exception.class}) #{@exception.message.inspect}" 55 | 56 | mail(:to => @options[:exception_recipients], :from => @options[:sender_address], :subject => subject) do |format| 57 | format.text { render "#{mailer_name}/exception_notification" } 58 | end 59 | end 60 | 61 | private 62 | 63 | def clean_backtrace(exception) 64 | Rails.respond_to?(:backtrace_cleaner) ? 65 | Rails.backtrace_cleaner.send(:filter, exception.backtrace) : 66 | exception.backtrace 67 | end 68 | 69 | helper_method :inspect_object 70 | 71 | def inspect_object(object) 72 | case object 73 | when Hash, Array 74 | object.inspect 75 | when ActionController::Base 76 | "#{object.controller_name}##{object.action_name}" 77 | else 78 | object.to_s 79 | end 80 | end 81 | 82 | end 83 | end 84 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | = Exception Notifier Plugin for Rails 2 | 3 | The Exception Notifier plugin provides a mailer object and a default set of 4 | templates for sending email notifications when errors occur in a Rails 5 | application. The plugin is configurable, allowing programmers to specify: 6 | 7 | * the sender address of the email 8 | * the recipient addresses 9 | * the text used to prefix the subject line 10 | 11 | The email includes information about the current request, session, and 12 | environment, and also gives a backtrace of the exception. 13 | 14 | == Usage 15 | 16 | As of Rails 3 ExceptionNotifier is used as a rack middleware 17 | 18 | Whatever::Application.config.middleware.use ExceptionNotifier, 19 | :email_prefix => "[Whatever] ", 20 | :sender_address => %{"notifier" }, 21 | :exception_recipients => %w{exceptions@example.com} 22 | 23 | == Customization 24 | 25 | By default, the notification email includes four parts: request, session, 26 | environment, and backtrace (in that order). You can customize how each of those 27 | sections are rendered by placing a partial named for that part in your 28 | app/views/exception_notifier directory (e.g., _session.rhtml). Each partial has 29 | access to the following variables: 30 | 31 | * @controller: the controller that caused the error 32 | * @request: the current request object 33 | * @exception: the exception that was raised 34 | * @backtrace: a sanitized version of the exception's backtrace 35 | * @data: a hash of optional data values that were passed to the notifier 36 | * @sections: the array of sections to include in the email 37 | 38 | You can reorder the sections, or exclude sections completely, by altering the 39 | ExceptionNotifier.sections variable. You can even add new sections that 40 | describe application-specific data--just add the section's name to the list 41 | (whereever you'd like), and define the corresponding partial. Then, if your 42 | new section requires information that isn't available by default, make sure 43 | it is made available to the email using the exception_data macro: 44 | 45 | class ApplicationController < ActionController::Base 46 | before_filter :log_additional_data 47 | ... 48 | protected 49 | def log_additional_data 50 | request.env["exception_notifier.exception_data"] = { 51 | :document => @document, 52 | :person => @person 53 | } 54 | end 55 | ... 56 | end 57 | 58 | In the above case, @document and @person would be made available to the email 59 | renderer, allowing your new section(s) to access and display them. See the 60 | existing sections defined by the plugin for examples of how to write your own. 61 | 62 | == Notification 63 | 64 | After an exception notification has been delivered the rack environment variable 65 | 'exception_notifier.delivered' will be set to +true+. 66 | 67 | == Rails 2.3 stable and earlier 68 | 69 | If you are running Rails 2.3 then see the branch for that: 70 | 71 | http://github.com/rails/exception_notification/tree/2-3-stable 72 | 73 | If you are running pre-rack Rails then see this tag: 74 | 75 | http://github.com/rails/exception_notification/tree/pre-2-3 76 | 77 | == Support and tickets 78 | 79 | https://rails.lighthouseapp.com/projects/8995-rails-plugins 80 | 81 | Copyright (c) 2005 Jamis Buck, released under the MIT license --------------------------------------------------------------------------------