2015-04-07 35 views
5

Ho un pezzo di middleware Rack che carica un tenant, tramite sottodominio, e applica alcune impostazioni predefinite. Il middleware, anche se non è bello, fa abbastanza bene il suo lavoro. Tuttavia, quando un'eccezione viene lanciata all'interno dell'app, il middleware "intercetta" la traccia dello stack completo. Quando dico trap intendo che nasconde la traccia di stack prevista.Traccia stack "trapping" middleware rack

Ecco un esempio.

sto un'eccezione in un'azione di un controller in questo modo:

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

ci si aspetta che l'analisi dello stack sarebbe fare riferimento a questa posizione, ma non è così. Invece fa riferimento a una riga nel middleware.

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' 

Perché succede? Hai mai visto qualcosa di simile?

Ecco il middleware:

# 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 

Sono in esecuzione rubino 2.2.0p0 e rails 4.1.8.

Ho cercato le reti per questo ma non ho trovato nulla, probabilmente perché non sto cercando la cosa giusta.

Qualche idea sul perché questo sta accadendo e su cosa sto facendo male?

Cheers!

risposta

7

ho finalmente trovato la soluzione a questo. Si scopre che l'ultima riga in quella che è considerata la mia applicazione è nel middleware. Stavo eseguendo il resto del codice in un motore di rotaie locale situato in una directory components. Tutto quello che dovevamo fare era creare un nuovo silenziatore per BacktraceCleaner. I componenti di avviso dir sono ora inclusi.

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

Se siete interessati qui è un problema che ho postato sul progetto rotaie su come replicare questo in dettaglio. https://github.com/rails/rails/issues/22265

0

Non stai facendo nulla di sbagliato. Ma un sacco di middleware intercetta le eccezioni per fare pulizia, incluso il middleware che Rack inserisce automaticamente nella modalità di sviluppo. V'è uno specifico middleware Rack inserita in fase di sviluppo che cattura le eccezioni non gestite fanno e dare una pagina HTML ragionevole invece di un dump dello stack grezzo (che spesso non vedrete affatto con i server di applicazioni comuni.)

  • Puoi prendi l'eccezione tu stesso mettendo un inizio/salvataggio/fine attorno al tuo livello più alto. Ricorda di catturare "Eccezione", non solo il "salvataggio" predefinito senza argomenti, se vuoi ottenere tutto. E potresti voler rilanciare l'eccezione se hai intenzione di lasciare questo codice.
  • Puoi eseguire la modalità di produzione, che potrebbe impedire l'inserimento automatico del middleware da parte di Rack.
  • È possibile scoprire quale middleware è inserito (in Rails: "rake middleware") e quindi rimuovere manualmente il middleware (in Rails "config.middleware.delete" o "config.middleware.disable").

Ci sono probabilmente altri metodi.

3

Il middleware sembra buono. Penso che tu abbia un problema con l'impostazione backtrace_cleaner. Forse il pulitore viene scavalcato da una gemma di terze parti. Prova mettere un punto di interruzione (debugger) nel metodo azione di controllo prima della risurrezione di errore e di stampa:

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

per vedere i luoghi di origine di tutti i silenziatori che spogliano le tracce non-app. Per impostazione predefinita dovrebbe utilizzare solo Rails :: BacktraceCleaner che trova a railties-4.1.8/lib/rails/backtrace_cleaner.rb

Per vedere direttamente il codice sorgente silenziatore:

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

+ info Da 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