Gem ExceptionTrack - 捕捉 Rails 应用运行期的异常,并存储到数据库

huacnlee for Rails Engine Gem · 2017年02月17日 · 最后由 jaynlive 回复于 2017年05月15日 · 7453 次阅读
本帖已被管理员设置为精华贴

Ruby China 很早以前就基于 exception_notification 实现了线上应用异常捕捉功能,并将捕捉到的错误信息存储到数据库里面,以便于可以在后台查看。

实际上 exception_notification 感觉是推荐用通知的方式(Email, Slack, WebHook ...)

但根据我这几年长期使用来看,通知会变成“狼来了”,因为一些异常我们不可能完全修复:

  • 有时候异常是由于恶意的扫描程序(但不影响)带来的;
  • 已知,但不好修复,或暂时无法修复的异常,或不影响使用且不好修复的异常;
  • 网络 IO 导致的偶发性异常,例如请求一个 GitHub 的地址,邮件发送失败;
  • ... 其他一些不影响系统运作的非核心功能异常;

这些,我们可以需要记录下来,对整个系统的线上状况有清晰的认识,但不能发通知。

所以 Ruby China 将异常信息存放倒数据库里面,并在管理后台做了个界面可以供我时不时的查阅。


基于上面的实践经验,我将它做成了 Gem,以方便其他项目也可以这么做。

安装

gem 'exception-track'

然后

$ bundle install

并执行 exception_track:install 生成配置文件和 Migration 文件

$ rails g exception_track:install
# 然后合并数据库
$ rails db:migrate

修改 routes.rb

Rails.application.routes.draw do
  mount ExceptionTrack::Engine => "/exception-track"
end

一般我们可能会让这个功能仅管理员可用,那么你可以这么做:

# lib/admin_constraint.rb
class AdminConstraint
  def matches?(request)
    return false if !request.session[:user_id]
    user = User.find(request.session[:user_id])
    user && user.admin?
  end
end

# config/router.rb
require 'admin_constraint'
mount ExceptionTrack::Engine => "/exception-track", constraints: AdminConstraint.new

如果是 Devise 的话,可以这么干来限制权限

Rails.application.routes.draw do
  authenticate :user, ->(u) { u.admin? } do
    mount ExceptionTrack::Engine => "/exception-track"
  end
end

然后你就可以访问 http://localhost:3000/exception-track 查看 ExceptionTrack 的界面了(当然没数据,你需要故意制造点异常出来)

配置只在 production 环境才记录异常到数据库

默认情况下,ExceptionTrack 在 development, production 两个环境下将异常写入到数据库,如果你不希望 development 写信息(因为 rails console 已经能看到了,默认那样是为了演示),你可以调整一下配置:

config/initializers/exception-track.rb

ExceptionTrack.configure do
  self.environments = %i(production)
end

# 同时,你还可以在这个文件配置 ExceptionNotification
require 'exception_notification/sidekiq'
ExceptionNotification.configure do |config|
  # ignored_exceptions 是 Ruby China 的配置,可以参考一下
  config.ignored_exceptions += %w(ActionView::TemplateError
                                  ActionController::InvalidAuthenticityToken
                                  ActionController::BadRequest
                                  ActionView::MissingTemplate
                                  ActionController::UrlGenerationError
                                  ActionController::UnknownFormat)
end

关于 ExceptionNotification 的更多配置,请详细阅读 ExceptionNotification 的文档

实现理念

这是一个简单而实用的东西,目的是为了一般性的项目不再反复搞 ExceptionNotification 的基础配置那一套,也不用自己实现一个界面(虽然实现成本不高,但我不想再另外一个项目里面重做一次相同的事情)。

而据我的使用情况来看,绝大多数项目有这么个简简单单的界面,就够了,你只需要做的是,定期查阅异常列表,尽可能的修复它们,然后清空列表(往后反复重复这个动作)

项目地址

https://github.com/rails-engine/exception-track

我们用 rollbar,不过还是给个赞

lgn21st 将本帖设为了精华贴。 02月19日 00:29

我用 exception_notification 过滤不必要的异常邮件的发送,现在有这个 Gem 还是挺方便的,比查看错误邮件方便多了

@huacnlee 这么好的东西从 Rails 4.2 开始支持的啊,能从 Rails 4.0 开始支持吗? 😆

#4 楼 @lifuzho 不知道能不能用,精力有限,要保持向下兼容需要费神一些。 😪 😶

先把 gemspec 调整一下,你用用看,版本 0.1.3

sentry 才是标配吧

@huacnlee 改了 gemspec 之后确实可以用了,不过 will_paginatekaminari 有冲突 😂

#8 楼 @lifuzho 不冲突,可以同时用的,我才尝试过。

谢谢分享。

ps: 我们把登录用户的 log 也保存到数据库里的😄

能像 airbrake 那样手动抛异常么?

yingce 回复

Ruby 直接抛异常就可以了呀

huacnlee 回复

也对 自定义几个异常就行了 哈哈

@huacnlee 有没有像 HoneyBadger 或者 NewRelic 一样,手动调记录异常的? (因为有时候我们业务会自己捕获异常做一些处理), 类似于 HoneyBadger.notify_exception(e) NewRelic::Agent.notice_error(e)

undefined method `per' for #ExceptionTrack::Log::ActiveRecord_Relation:0x007fd993ff1f30 这个错咋整,rails 版本 5.0.1

ad583255925 回复
gem 'kaminari'
huacnlee 回复

我是用 'will_paginate'的

huacnlee 回复
class ActiveRecord::Relation
  def per *per
    per_page *per
  end

  def total_count *total_count
    total_entries *total_count
  end
end

姑且这样解决了

huacnlee 回复

我能加什么参数让这个 gem 不依赖 kaminari 吗

ad583255925 回复

基本上不能。你可以选择自己 fork,然后去掉 kaminari,自己修改代码中的分页。颇劳神。

awking 回复

恩,我 gem 下下来改完放本地装了

ad583255925 回复

此方法可以解决

# config/initializers/will_paginate.rb
if defined?(WillPaginate)
 ActiveSupport.on_load :active_record do
   module WillPaginate
     module ActiveRecord
       module RelationMethods
         def per(value = nil) per_page(value) end
         def total_count() count end
       end
     end
     module CollectionMethods
       alias_method :num_pages, :total_pages
     end
   end
 end
end
需要 登录 后方可回复, 如果你还没有账号请 注册新账号