机架中间件 "trapping" 堆栈跟踪
Rack middleware "trapping" stack trace
我有一个 Rack 中间件,它通过子域加载一个租户,并应用一些默认设置。中间件虽然不漂亮,但已经足够好了。但是,当应用程序中抛出异常时,中间件 "traps" 完整的堆栈跟踪。当我说陷阱时,我的意思是它隐藏了预期的堆栈跟踪。
这是一个例子。
我在控制器操作中抛出异常,如下所示:
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
我是运行ruby2.2.0p0
和rails 4.1.8
.
我已经在网上搜索了这个但找不到任何东西,可能是因为我没有搜索正确的东西。
关于为什么会发生这种情况以及我做错了什么有什么想法吗?
干杯!
你没有做错任何事。但是很多中间件会捕获异常以进行清理,包括 Rack 在开发模式下自动插入的中间件。在开发中插入了一个特定的 Rack 中间件,它将捕获未捕获的异常并提供合理的 HTML 页面而不是原始堆栈转储(对于常见的应用程序服务器,您通常根本看不到。)
- 您可以通过在顶层放置 begin/rescue/end 来自己捕获异常。如果您想获得所有内容,请记住捕捉 "Exception",而不仅仅是没有 arg 的默认 "rescue"。如果您要保留此代码,您可能需要重新抛出异常。
- 您可以 运行 在生产模式下——这可能会阻止 Rack 自动插入中间件。
- 您可以找出插入的中间件(in Rails: "rake middleware"),然后手动删除中间件(in Rails "config.middleware.delete" or "config.middleware.disable").
可能还有其他方法。
你的中间件看起来不错。我认为您的 backtrace_cleaner 设置有问题。也许清洁工被第三方覆盖 gem。尝试在引发错误之前在控制器操作方法中放置一个断点(调试器),然后打印:
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
我终于找到了解决办法。事实证明,被认为是我的应用程序的最后一行是在中间件中。我是 运行 位于 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
我有一个 Rack 中间件,它通过子域加载一个租户,并应用一些默认设置。中间件虽然不漂亮,但已经足够好了。但是,当应用程序中抛出异常时,中间件 "traps" 完整的堆栈跟踪。当我说陷阱时,我的意思是它隐藏了预期的堆栈跟踪。
这是一个例子。
我在控制器操作中抛出异常,如下所示:
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
我是运行ruby2.2.0p0
和rails 4.1.8
.
我已经在网上搜索了这个但找不到任何东西,可能是因为我没有搜索正确的东西。
关于为什么会发生这种情况以及我做错了什么有什么想法吗?
干杯!
你没有做错任何事。但是很多中间件会捕获异常以进行清理,包括 Rack 在开发模式下自动插入的中间件。在开发中插入了一个特定的 Rack 中间件,它将捕获未捕获的异常并提供合理的 HTML 页面而不是原始堆栈转储(对于常见的应用程序服务器,您通常根本看不到。)
- 您可以通过在顶层放置 begin/rescue/end 来自己捕获异常。如果您想获得所有内容,请记住捕捉 "Exception",而不仅仅是没有 arg 的默认 "rescue"。如果您要保留此代码,您可能需要重新抛出异常。
- 您可以 运行 在生产模式下——这可能会阻止 Rack 自动插入中间件。
- 您可以找出插入的中间件(in Rails: "rake middleware"),然后手动删除中间件(in Rails "config.middleware.delete" or "config.middleware.disable").
可能还有其他方法。
你的中间件看起来不错。我认为您的 backtrace_cleaner 设置有问题。也许清洁工被第三方覆盖 gem。尝试在引发错误之前在控制器操作方法中放置一个断点(调试器),然后打印:
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
我终于找到了解决办法。事实证明,被认为是我的应用程序的最后一行是在中间件中。我是 运行 位于 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