ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

如何用 Rails error reporter 把应用错误上报到外部服务并编写自定义 subscriber

2026/9/10 4:37:04 拓冰建站 浏览量
如何用 Rails error reporter 把应用错误上报到外部服务并编写自定义 subscriber 如何用 Rails error reporter 把应用错误上报到外部服务并编写自定义 subscriber【免费下载链接】railsRuby on Rails项目地址: https://gitcode.com/GitHub_Trending/rai/rails这篇文章解决一个具体的集成任务把 Rails 应用中的错误未捕获的 HTTP 请求、Job、rails runner调用中抛出的错误以及手动上报的错误自动发送到你的外部错误上报服务如 Sentry、Honeybadger 或自建服务并且通过自己编写的 subscriber 完成与服务端的对接。完成后你不需要再写begin/rescue加手动 notify 的样板代码也不需要为捕获未处理错误额外插入 Rack middleware——Rails 会把所有执行包裹在 error reporter 中未处理错误会自动流经你注册的 subscriber。以下事实依据来自 guides/source/error_reporting.md、activesupport/lib/active_support/error_reporter.rb 和 railties/lib/rails/application/bootstrap.rb。Rails.error 是什么启动时已经做了什么Rails.error返回ActiveSupport::ErrorReporter实例定义见 railties/lib/rails.rb。在标准 Rails 应用中启动时有一个:initialize_error_reporterinitializerrailties/lib/rails/application/bootstrap.rb自动完成初始化不需要额外配置当config.consider_all_requests_local为真本地开发时设置Rails.error.debug_mode true否则设置Rails.error.logger Rails.logger注册一个内置 context middleware给每份报告的 context 注入rails: { version:, app_revision:, environment: }。Rails 会把 HTTP 请求、Active Job 和rails runner等所有执行包裹在 error reporter 中因此应用内任何未处理的错误都会自动通过 subscriber 上报。两个需要知道的边界来自指南Rails 会向报告 context 自动注入两个键请求类报告注入:controller控制器实例Active Job 报告注入:jobJob 实例。对于 HTTP 请求出现在ActionDispatch::ExceptionWrapper.rescue_responses中的错误如 404不会被上报因为它们不产生 500通常不是需要处理的 bug。使用 error reporter 是可选的其他捕获错误的方式仍然有效。编写自定义 subscriber要对接外部服务需要一个subscriber。subscriber 可以是任何带report方法的 Ruby 对象。错误发生时或手动上报时reporter 会带着错误对象和选项调用这个方法签名为report(Exception, handled: Boolean, severity: (:error OR :warning OR :info), context: Hash, source: String)指南给出的示例是把 subscriber 放在 initializer 中config/initializers/error_subscriber.rb路径为你的应用仓库# config/initializers/error_subscriber.rb class ErrorSubscriber def report(error, handled:, severity:, context:, source: nil) MyErrorReportingService.report_error(error, context: context, handled: handled, level: severity) end end其中MyErrorReportingService是你自己的上报客户端需要替换为实际的服务调用。注意report方法的参数含义直接来自上报 APIhandled表示错误是否已被处理severity是严重级别context是附加信息如请求、用户详情source标记错误来源默认application内部库可能使用其他值如redis_cache_store.active_supportsubscriber 可以用它过滤不关心的错误。实现上有两条硬约束activesupport/lib/active_support/error_reporter.rb 中subscribe会检查对象是否响应#report否则抛出ArgumentError: Error subscribers must respond to #report文档明确要求 subscriber 的report方法should never抛错。从源码看subscriber 抛错时reporter 会把该异常按 fatal 级别写入 logger若配置了 logger否则直接重新抛出——即一个会抛错的 subscriber 可能反过来打断应用请求。注册、注销与暂停 subscriber定义好类之后用Rails.error.subscribe注册实例Rails.error.subscribe(ErrorSubscriber.new)可以注册任意多个 subscriberRails 按注册顺序依次调用它们。注销用Rails.error.unsubscribe可以传实例或类subscriber ErrorSubscriber.new Rails.error.unsubscribe(subscriber) # or Rails.error.unsubscribe(ErrorSubscriber)如果某个依赖注册了你不想要的 subscriber可以用unsubscribe移除它再替换。临时暂停某个 subscriber 对错误的响应用Rails.error.disable同样接受实例或类Rails.error.disable(ErrorSubscriber) do 1 1 # TypeError will not be reported via the ErrorSubscriber end一个环境相关的注意点指南原文Rails error reporter 在任何环境下都会调用已注册的 subscriber但许多错误上报服务默认只在 production 上报错误。因此需要按环境自行配置并测试你的上报设置不要假设“开发环境没收到上报”是 reporter 的行为。上报错误自动与手动 API自动上报不需要你写任何代码——未处理错误在包裹的执行中被捕获后自动流经 subscriber。手动上报有四个方法行为差异决定你在业务代码里选哪个Rails.error.handle报告块内错误并吞掉它块外代码继续执行。无错误时返回块的返回值出错时返回nil可通过fallback:覆盖result Rails.error.handle do 1 1 # raises TypeError end result # nil 1 1 # This will be executed user Rails.error.handle(fallback: - { User.anonymous }) do User.find(params[:id]) endRails.error.record报告后重新抛出错误后续代码不会执行Rails.error.record do 1 1 # raises TypeError end 1 1 # This wont be executedRails.error.report手动上报一个已有的错误对象必须传入Exception实例否则抛出ArgumentError。传入的其他选项会原样透传给所有 subscriberbegin # code rescue StandardError e Rails.error.report(e) endRails.error.unexpected报告“不应发生”的意外错误接受异常实例或字符串。行为随环境不同production 中报告后返回nil、执行继续development/test 中错误会被包装成新的错误类并抛给开发者调试。def edit if published? Rails.error.unexpected([BUG] Attempting to edit a published article, that shouldnt be possible) false end # ... end上报选项#handle、#record、#report都支持handledBoolean默认true#record会设为false、severity:error/:warning/:info之一#handle默认:warning#record默认:error、contextHash、sourceString默认applicationRails.error.handle(context: { user_id: user.id }, severity: :info) do # ... end按错误类过滤#handle和#record可以只上报特定类的错误其他错误照常抛出Rails.error.handle(IOError) do 1 1 # raises TypeError end 1 1 # TypeErrors are not IOErrors, so this will *not* be executed给错误附加上下文单次上报的上下文通过context选项传入。全局上下文用Rails.error.set_context之后每次上报的context选项会与它合并Rails.error.set_context(section: checkout, user_id: user.id)指南给出的合并示例文档示例用于确认 subscriber 实际收到的内容Rails.error.set_context(a: 1) Rails.error.handle(context: { b: 2 }) { raise } # The reported context will be: {:a1, :b2}当多个 subscriber 共享同一份上下文比如一个给 APM、一个给日志服务时用 context middleware 统一注入而不是在每个 subscriber 里重复同样的查找逻辑。middleware 接收与Rails.error.report相同的参数可以修改 context 并返回新的 context hash它在set_context设置的全局上下文应用之后运行class MyErrorContextMiddleware def call(error, context:, handled:, severity:, source:) context.merge({ foo: :bar }) end end Rails.error.add_middleware(MyErrorContextMiddleware.new) # 追加到 context middleware 栈 Rails.error.report(error, context: { bar: :baz }) # The reported context will include { bar: :baz, foo: :bar }middleware 栈要求对象响应#call否则add_middleware抛出ArgumentError。gem 也可以用它给宿主应用的所有错误附加通用上下文而不要求应用修改自己的 subscriber——“设置上下文”和“把数据发给错误追踪服务”两个职责应分别放在 middleware 和 subscriber 中。验证接线是否正确没有单独的“健康检查”命令验证方式就是你控制的那一段subscriber 的report方法。在report中把error、handled、severity、context、source输出到你的服务或日志然后在本地触发一次已知错误例如Rails.error.handle { raise TypeError }本地consider_all_requests_local为真时 reporter 处于 debug_modeunexpected会抛给开发者而非静默上报。核对 subscriber 收到的context是否符合文档示例的合并规则全局set_context的键 单次context选项的键 Rails 内置 middleware 注入的rails信息version/app_revision/environment 请求场景下的:controller或 Job 场景下的:job。确认外部服务侧真的收到了上报并分别在 development 与 production 行为上做检查——如前所述reporter 本身不区分环境区分环境的是你的服务或配置。如果你在写一个错误上报库可选分支以 gem 形式提供错误上报时在 Railtie 的 initializer 里注册 subscriber这是指南给出的库集成方式module MySdk class Railtie ::Rails::Railtie initializer my_sdk.error_subscribe do Rails.error.subscribe(MyErrorSubscriber.new) end end end两条限制必须遵守report方法不应抛错且 subscriber 抛错时 reporter 的行为是写入 fatal 日志有 logger 时或重新抛出。如果你的库同时保留了 Rack middleware 等其他错误捕获机制错误可能被重复上报。指南的建议是二选一移除其他机制或让 report 逻辑跳过自己已经见过的错误。参考资料guides/source/error_reporting.md、activesupport/lib/active_support/error_reporter.rb、railties/lib/rails/application/bootstrap.rb、railties/lib/rails.rb。【免费下载链接】railsRuby on Rails项目地址: https://gitcode.com/GitHub_Trending/rai/rails创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考