Automatically Logging Exceptions in Ruby

Automatically Logging Exceptions in Ruby

If you want to take a walk on the wild side, try this:

class Exception
alias real_init initialize
def initialize(*args)
real_init *args
# log the error (self) or its args here
end
end

This will intercept the creation of new exception objects at the moment of creation.

How to log exceptions automatically in Rails?

I ended up using rescue_from in the ApplicationController, logging the exception message and backtrace, and then using params[:controller] and params[:action] to determine what controller/action to redirect to.

For example, if PostsController#show exception'ed out, I would redirect to PostsController#index from within rescue_from. It's been working so far, so it doesn't seem to be a bad thing to do redirect_to from within rescue_from. Time will let me know, I'm sure! (Just need to make sure that my redirects don't cause some infinite loops!)

And just in someone is interested in this hack (try at your own risk!):

class ApplicationController < ActionController::Base

def determine_redirect_to_path(controller,action)
...
end

rescue_from StandardError do |exception|
backtrace_size = exception.backtrace.size
if backtrace_size >= 2 then max_range = 2
elsif backtrace_size >= 1 then max_range = 1
end
if max_range > 0
s = "rescued_from:: #{params[:controller]}##{params[:action]}: #{exception.inspect}\n#{exception.backtrace[0..max_range].to_s}\n"
logger.error s
end
redirect_to determine_redirect_to_path(params[:controller],params[:action])
end
end

Ruby: where does ruby log its errors (location of errors / exceptions)?

There are no log files. When errors occur, the Ruby standard library will write its messages to the standard error stream. You can redirect this to a file if you'd like.

Of course, you can do whatever you want in your own code. It's absolutely possible to open a file and log messages to it. There are many logging libraries that provide this functionality.

How can I log Rails errors into a separate log file?

For example, to log all ActiveRecord::Base errors in a file called log/exceptions.log

new_logger = Logger.new('log/exceptions.log')
new_logger.level = Logger::ERROR
new_logger.error('THIS IS A NEW EXCEPTION!')

ActiveRecord::Base.logger = new_logger

For controllers and view(because ActionView logger doesn't have it's own logger, so it depends on the ActionController logger):

ActionController::Base.logger = new_logger

Ruby on Rails 3.1 - Logging exception in the database

For seemingly outdated gems such as exception_logger, I find it helpful to check out other users' forks of the gem using GitHub's network feature. You'll usually find that someone has updated or continued to maintain the gem even though the original author has not. For example, the network for exception_logger is quite active, especially this particular fork by QuBiT.

Check out The Ruby Toolbox - Exception Notification for a list of widely used "exception notification" tools. Unfortunately most of them are a bit outdated, but the technique described above for finding more active forks applies as well.

If you aren't opposed to a paid solution, Airbrake and Exceptional both work well (although Airbrake's new pricing structure make them quite prohibitive for a small project; Exceptional is reasonably priced).

Unhandled Exceptions in Ruby

Ruby doesn't have generic handlers, but instead you wrap the code which might generate exceptions. For example:

begin
# ... Do stuff
rescue => e
$stderr.puts("[%s] %s" % [ e.class, e ])
$stderr.puts(e.backtrace.join("\n"))
end

Where that rescues all standard exceptions and displays some diagnostic output. You can do whatever you want in the rescue block. Any uncaught exceptions will bubble up to your top-level automatically.

This must be the top-level code for your Ruby application.

The closest thing to what you're talking about in Ruby is the at_exit handler you can define, but this runs in every exit scenario, not just uncaught exceptions.

Why is it bad style to `rescue Exception = e` in Ruby?

TL;DR: Use StandardError instead for general exception catching. When the original exception is re-raised (e.g. when rescuing to log the exception only), rescuing Exception is probably okay.


Exception is the root of Ruby's exception hierarchy, so when you rescue Exception you rescue from everything, including subclasses such as SyntaxError, LoadError, and Interrupt.

Rescuing Interrupt prevents the user from using CTRLC to exit the program.

Rescuing SignalException prevents the program from responding correctly to signals. It will be unkillable except by kill -9.

Rescuing SyntaxError means that evals that fail will do so silently.

All of these can be shown by running this program, and trying to CTRLC or kill it:

loop do
begin
sleep 1
eval "djsakru3924r9eiuorwju3498 += 5u84fior8u8t4ruyf8ihiure"
rescue Exception
puts "I refuse to fail or be stopped!"
end
end

Rescuing from Exception isn't even the default. Doing

begin
# iceberg!
rescue
# lifeboats
end

does not rescue from Exception, it rescues from StandardError. You should generally specify something more specific than the default StandardError, but rescuing from Exception broadens the scope rather than narrowing it, and can have catastrophic results and make bug-hunting extremely difficult.


If you have a situation where you do want to rescue from StandardError and you need a variable with the exception, you can use this form:

begin
# iceberg!
rescue => e
# lifeboats
end

which is equivalent to:

begin
# iceberg!
rescue StandardError => e
# lifeboats
end

One of the few common cases where it’s sane to rescue from Exception is for logging/reporting purposes, in which case you should immediately re-raise the exception:

begin
# iceberg?
rescue Exception => e
# do some logging
raise # not enough lifeboats ;)
end


Related Topics



Leave a reply



Submit