【问题标题】:Rack middleware "trapping" stack trace机架中间件“捕获”堆栈跟踪
【发布时间】:2015-04-07 18:03:01
【问题描述】:

我有一个 Rack 中间件,它通过子域加载租户,并应用一些默认设置。中间件虽然不漂亮,但它的工作是否足够好。但是,当应用程序中抛出异常时,中间件会“捕获”完整的堆栈跟踪。当我说陷阱时,我的意思是它隐藏了预期的堆栈跟踪。

这是一个例子。

我在控制器操作中抛出异常,如下所示:

def index
  throw "Exception in a Rails controller action"
  @taxonomies = Spree::Taxonomy.all
end

您会期望堆栈跟踪会引用此位置,但事实并非如此。相反,它引用了中间件中的一行。

Completed 500 Internal Server Error in 139ms

UncaughtThrowError (uncaught throw "Exception in a Rails controller action"):
lib/tenant_manager/middleware/loader.rb:42:in `call'

为什么会这样?你以前见过这样的事情吗?

这是中间件:

# lib/tenant_manager/middleware/loader.rb
module TenantManager
  module Middleware
    class Loader
    # Middleware to detect an tenant via subdomain early in
    # the request process
    #
    # Usage:
    #   # config/application.rb
    #   config.middleware.use TenantManager::Middleware::Loader
    #
    # A scaled down version of https://github.com/radar/houser

      def initialize(app)
        @app = app
      end

      def call(env)
        domain_parts = env['HTTP_HOST'].split('.')
        if domain_parts.length > 2
          subdomain = domain_parts.first
          tenant = Leafer::Tenant.find_by_database(subdomain)
          if tenant
            ENV['CURRENT_TENANT_ID'] = tenant.id.to_s
            ENV['RAILS_CACHE_ID'] = tenant.database
            Spree::Image.change_paths tenant.database
            Apartment::Tenant.process(tenant.database) do
              country = Spree::Country.find_by_name('United States')
              Spree.config do |config|
                config.default_country_id = country.id if country.present?
                config.track_inventory_levels = false
              end
              Spree::Auth::Config.set(:registration_step => false)
            end
          end
        else
          ENV['CURRENT_TENANT_ID'] = nil
          ENV['RAILS_CACHE_ID'] = ""
        end
        @app.call(env)
      end

    end
  end
end

我正在运行 ruby​​ 2.2.0p0rails 4.1.8

我已经在网上搜索过这个但找不到任何东西,可能是因为我没有寻找正确的东西。

关于为什么会发生这种情况以及我做错了什么有什么想法吗?

干杯!

【问题讨论】:

    标签: ruby-on-rails rack spree middleware


    【解决方案1】:

    我终于找到了解决办法。事实证明,我的应用程序的最后一行在中间件中。我在位于components 目录中的本地rails 引擎中运行其余代码。我们需要做的就是为BacktraceCleaner 创建一个新的消音器。注意组件目录现在包括在内。

    # config/initializers/backtrace_silencers.rb
    Rails.backtrace_cleaner.remove_silencers!
    Rails.backtrace_cleaner.add_silencer { |line| line !~ /^\/(app|config|lib|test|components)/}
    

    如果您对此感兴趣,我在 rails 项目上发布了一个关于如何详细复制它的问题。 https://github.com/rails/rails/issues/22265

    【讨论】:

      【解决方案2】:

      您的中间件看起来不错。我认为您的 backtrace_cleaner 设置有问题。也许清洁器被第 3 方宝石覆盖。尝试在引发错误之前在控制器操作方法中放置一个断点(调试器),然后打印:

      puts env['action_dispatch.backtrace_cleaner'].instance_variable_get(:@silencers).map(&:source_location).map{|l| l.join(':')}
      

      查看去除非应用程序痕迹的所有消音器的源位置。默认情况下,它应该只使用位于 railties-4.1.8/lib/rails/backtrace_cleaner.rb 的 Rails::BacktraceCleaner

      直接看消音器源码:

      puts env['action_dispatch.backtrace_cleaner'].instance_variable_get(:@silencers).map{|s| RubyVM::InstructionSequence.disasm s }
      

      查看更多来自https://github.com/rails/rails/blob/master/railties/lib/rails/backtrace_cleaner.rb https://github.com/rails/rails/blob/master/activesupport/lib/active_support/backtrace_cleaner.rb

      【讨论】:

        【解决方案3】:

        你没有做错任何事。但是很多中间件为了进行清理会捕获异常,包括 Rack 在开发模式下自动插入的中间件。在开发中插入了一个特定的 Rack 中间件,它将捕获未捕获的异常并提供合理的 HTML 页面,而不是原始堆栈转储(您通常在普通应用服务器中根本看不到。)

        • 您可以通过在顶层周围放置开始/救援/结束来自己捕获异常。如果您想获取所有内容,请记住捕获“异常”,而不仅仅是没有 arg 的默认“救援”。如果您要保留此代码,您可能需要重新抛出异常。
        • 您可以在生产模式下运行——这可能会阻止 Rack 自动插入中间件。
        • 您可以找出插入的中间件(在 Rails 中:“rake 中间件”),然后手动删除中间件(在 Rails 中“config.middleware.delete”或“config.middleware.disable”)。

        可能还有其他方法。

        【讨论】:

          猜你喜欢
          • 2012-03-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-09-10
          • 2011-08-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多