Sending Rails Deprecation Warnings to Sentry.

If you've upgraded a Rails app recently, you are no stranger to deprecation warnings. As I've been going through some of our apps (most of which were running 3.2) and upgrading them, I've wanted a way to get notified of these warnings without having to scan the logs.

We use Sentry to manage our exception handling, so I figured there must be a way to handle these warnings just like exceptions. Since I had to read a little bit of Rails code to figure this out, I figured I'd share what I learned. ActiveSupport allows you to define one or more deprecation behaviors by using ActiveSupport::Deprecation.behavior=. Several options are included by default:

  • :raise - raise an exception instead of warning
  • :stderr - log them to $stderr
  • :log - log them out using Rails.logger
  • :notify - publish an alert using ActiveSupport Notifications
  • :silence - sweep them under a rug and pretend they never existed, until they are truly deprecated and bite you.

Based on the documentation, you can also pass a custom handler class or a proc, basically, anything that responds to call can be used. When a deprecation warning is issued, the list of behaviors is looped through and passed the deprecation message and callstack:

def warn(message = nil, callstack = nil)
  return if silenced

  callstack ||= caller(2)
  deprecation_message(callstack, message).tap do |m|
    behavior.each { |b| b.call(m, callstack) }
  end
end

By default, Rails uses :stderr. I wanted to keep that behavior, but also include Sentry. ActiveSupport Notifications are a perfect way to do that. Using the raven gem to hook into Sentry, here's where I landed:

ActiveSupport::Deprecation.behavior = [:stderr, :notify]
ActiveSupport::Notifications.subscribe('deprecation.rails') do |name, start, finish, id, payload|
  Raven.capture_message("DEPRECATION WARNING", {
    :logger => 'logger',
    :extra  => {
      'message'   => payload[:message],
      'backtrace' => payload[:callstack].join("\n  ")
    },
    :tags   => {
      'environment' => Rails.env
    }
  })
end