Rails 4: ActiveSupport::BacktraceCleaner, towards better documentation
The official documentation for the class can be found at http://api.rubyonrails.org/classes/ActiveSupport/BacktraceCleaner.html.
The problem
However, trying to implement the proposed example code throws an error.
# the original code example as of 2013-10-11 bc = BacktraceCleaner.new bc.add_filter { |line| line.gsub(Rails.root, '') } bc.add_silencer { |line| line =~ /mongrel|rubygems/ } bc.clean(exception.backtrace) # will strip the Rails.root prefix and skip any lines from mongrel or rubygems # => TypeError: wrong argument type Pathname (expected Regexp)
Hmpfh. The problem here is the line
bc.add_filter { |line| line.gsub(Rails.root, '') }
"String".gsub
expects a pattern
as shown at http://www.ruby-doc.org/core-2.0.0/String.html#method-i-gsub. Rails.root
, however, returns something of type Pathname
.
Corrections
The solution is very easy: Just append
.to_s
The correct example snippet should then be:
bc = ActiveSupport::BacktraceCleaner.new bc.add_filter { |line| line.gsub(Rails.root.to_s, '') } bc.add_silencer { |line| line =~ /mongrel|rubygems/ } bc.clean(exception.backtrace) # will strip the Rails.root prefix and skip any lines from mongrel or rubygems
Real-world examples
Another thing to note about the BacktraceCleaner
is that filters are applied first, then silencers. That means if you want to remove for example all trace lines which do not belong to your app, and at the same time remove the long path to your app you might end up with nothing:
First – failed – try
# don't use this bc.add_filter { |line| line.gsub(Rails.root.to_s, '') } bc.add_silencer { |line| line.index(Rails.root.to_s).nil? }
This code first cleans the root path from all lines, then removes all as none contain it anymore.
Working example 1
Instead this is what I did:
bc = ActiveSupport::BacktraceCleaner.new bc.add_filter { |line| line.gsub(Rails.root.to_s, '<root>') } bc.add_silencer { |line| line.index('<root>').nil? and line.index('/') == 0 } bc.add_silencer { |line| line.index('<root>/vendor/') == 0 } e.set_backtrace(bc.clean(e.backtrace))
I replace the root path with a placeholder <root>
. The first silencer removes all lines that are do not contain the <root>
part, but only if it is a path. That way console traces or the main method still stay in place. I also added a second silencer that removes all traces of installed gems if installed inside the /vendor
folder.
Working example 2
Interestingly, the docs state
To reconfigure an existing BacktraceCleaner (like the default one in Rails) …
that there is a default backtrace cleaner. It turns out there exists a method
Rails.backtrace_cleaner
which returns the default Rails cleaner. So this is rather what I use now, as it quite nicely fits my purpose:
bc = Rails.backtrace_cleaner e.set_backtrace(bc.clean(e.backtrace))
Currently, their setup can be found at https://github.com/rails/rails/blob/4-0-stable/railties/lib/rails/backtrace_cleaner.rb. This is what the official backtrace cleaner looks like:
class BacktraceCleaner < ActiveSupport::BacktraceCleaner APP_DIRS_PATTERN = /^\/?(app|config|lib|test)/ RENDER_TEMPLATE_PATTERN = /:in `_render_template_\w*'/ def initialize super add_filter { |line| line.sub("#{Rails.root}/", '') } add_filter { |line| line.sub(RENDER_TEMPLATE_PATTERN, '') } add_filter { |line| line.sub('./', '/') } # for tests add_gem_filters add_silencer { |line| line !~ APP_DIRS_PATTERN } end private def add_gem_filters gems_paths = (Gem.path | [Gem.default_dir]).map { |p| Regexp.escape(p) } return if gems_paths.empty? gems_regexp = %r{(#{gems_paths.join('|')})/gems/([^/]+)-([\w.]+)/(.*)} add_filter { |line| line.sub(gems_regexp, '\2 (\3) \4') } end end
This is throwing error,
/config/initializers/backtrace_silencers.rb:9:in `’: undefined local variable or method `exception’ for main:Object (NameError)
Sorry to hear that. Without more information on what you actually used it’s hard to determine the problem.
Also note that the original post is over 2 years old so things might be different now.