Ruby on Rails+Grape应用全局记录所有异常至Sentry等APM的最佳方法
全局记录Rails + Grape应用所有异常的最佳方案
一、Rails 应用全局异常捕获配置
核心思路是覆盖Rails的异常处理链路,同时兼容原有业务逻辑,确保所有捕获/未捕获异常都能被APM工具记录。
1. 全局rescue_from捕获控制器异常
在ApplicationController中配置层级化的异常处理,先保留业务自定义的异常处理规则,最后兜底捕获所有未覆盖的异常并上报:
class ApplicationController < ActionController::Base # 保留原有业务的异常处理规则 rescue_from ActiveRecord::RecordNotFound, with: :handle_not_found # 兜底捕获所有未被处理的异常 rescue_from StandardError do |e| # 执行原有业务错误处理逻辑 handle_generic_error(e) # 上报至APM工具(以Sentry为例,其他工具替换对应方法) Sentry.capture_exception(e) # 如需重新抛出异常,保留该语句 raise e end private def handle_not_found render json: { error: "资源不存在" }, status: :not_found # 上报原本视为"无关注价值"的404异常 Sentry.capture_exception(ActiveRecord::RecordNotFound.new) end def handle_generic_error(e) render json: { error: e.message }, status: :internal_server_error end end
可选过滤:如果仍需排除极少数异常,可在上报前添加判断:
unless e.is_a?(完全无需记录的异常类) Sentry.capture_exception(e) end
2. 利用Rails事件订阅器捕获底层异常
通过ActiveSupport::Notifications订阅控制器事件,能捕获所有控制器层面的异常(包括手动rescue的):
# 在config/initializers/apm_exception_subscriber.rb中添加 ActiveSupport::Notifications.subscribe("process_action.action_controller") do |*args| event = ActiveSupport::Notifications::Event.new(*args) exception = event.payload[:exception_object] Sentry.capture_exception(exception) if exception end
该方案无需修改业务代码,适合全量采集控制器范围内的所有异常。
二、Grape 应用全局异常捕获配置
Grape有独立的异常处理机制,需针对其请求生命周期单独配置。
1. 全局rescue_from覆盖API异常
在Grape的API基类中配置层级化异常处理,确保所有API请求的异常都被捕获上报:
class API < Grape::API format :json # 保留原有业务的校验异常处理 rescue_from Grape::Exceptions::ValidationErrors, with: :handle_validation_errors # 兜底捕获所有未被处理的异常 rescue_from StandardError do |e| handle_api_error(e) # 上报至APM工具 Sentry.capture_exception(e) # 如需重新抛出,保留该语句 raise e end private def handle_validation_errors(e) error!(e.message, 400) # 上报原本视为"无关注价值"的参数校验异常 Sentry.capture_exception(e) end def handle_api_error(e) error!({ error: e.message }, 500) end end
2. 利用Grape中间件拦截全局异常
通过Grape的错误中间件,捕获请求生命周期内的所有异常(包括路由匹配、参数解析阶段):
# 在config/initializers/grape.rb中配置 Grape::Middleware::Error.before do |e| # 上报异常 Sentry.capture_exception(e) # 继续执行原有错误处理逻辑 throw :error, e end
三、跨APM工具适配方案
不同APM工具的上报方法只需简单替换,核心逻辑一致:
- Sentry:
Sentry.capture_exception(e) - Honeybadger:
Honeybadger.notify(e) - NewRelic:
NewRelic::Agent.notice_error(e) - SignalFx: 参考对应SDK的错误上报方法(如
SignalFx::Client.new.report_error(e))
四、避免重复上报的注意事项
- 若同时配置Rails和Grape的全局捕获,可能出现重复上报,可通过标记异常解决:
# Grape中上报时标记 rescue_from StandardError do |e| e.instance_variable_set(:@reported_to_apm, true) Sentry.capture_exception(e) handle_api_error(e) end # Rails中跳过已标记的异常 rescue_from StandardError do |e| Sentry.capture_exception(e) unless e.instance_variable_get(:@reported_to_apm) handle_generic_error(e) end - 手动
rescue的代码块,建议封装统一工具方法替代原生rescue,确保异常被上报:def rescue_and_report(exception_class = StandardError) yield rescue exception_class => e Sentry.capture_exception(e) # 执行原有业务处理逻辑 handle_custom_error(e) # 按需重新抛出 raise e end # 业务代码中使用 rescue_and_report(ActiveRecord::RecordInvalid) do # 业务逻辑 end
内容的提问来源于stack exchange,提问作者David Hempy
相关产品推荐
相关产品推荐

