Ottieni la traccia dello stack corrente in Ruby senza sollevare un'eccezione


139

Voglio registrare il backtrace corrente (stacktrace) in un'app Rails 3 senza che si verifichi un'eccezione. Qualche idea su come?

Perché lo voglio? Sto cercando di tracciare le chiamate effettuate quando Rails cerca un modello in modo da poter scegliere una parte del processo da sovrascrivere (perché voglio cambiare il percorso di visualizzazione per un mio controller di sottoclasse in particolare).

Mi piacerebbe chiamarlo dal file: gems\actionpack-3.2.3\lib\action_dispatch\middleware\templates\rescues\missing_template.erb. So che non è la migliore pratica, ma so che è a valle dello stack da cui si verifica la ricerca di modelli.


4
Soluzione sporca: sollevare un'eccezione lì, salvarla immediatamente e accedere e.backtrace. L'ho visto in uno dei progetti con cui sto lavorando. Non è l'approccio migliore, ma funziona. Spero di sentire una soluzione migliore da qualcun altro, però.
KL-7,

Risposte:


185

Puoi usare Kernel#caller:

# /tmp/caller.rb

def foo 
  puts caller # Kernel#caller returns an array of strings
end

def bar 
  foo 
end

def baz 
  bar 
end

baz

Produzione:

caller.rb:8:in `bar'
caller.rb:12:in `baz'
caller.rb:15:in `<main>'

Non è Kernel.caller- con un punto? Kernel.new.callernon è definito qui
ecoologico

8
No, tecnicamente callerè un metodo di istanza. Poiché il Kernelmodulo è incluso in ogni classe Ruby (tranne BasicObjectin 1.9), è disponibile come metodo di istanza su qualsiasi oggetto (è privato, però). Non puoi chiamarlo Kernel.new.callersemplicemente perché non puoi creare un'istanza di un modulo (non ha un newmetodo).
KL-7,

questo supporta un parametro per saltare qualsiasi numero di chiamanti; vedi: stackoverflow.com/a/3829269/520567
akostadinov

7
Per un uso grazioso della stampa - Rails.logger.debug caller.join("\n")oppure puts caller.join("\n"). Grazie.
Jignesh Gohel,

20

Prova a usare

Thread.current.backtrace

1
Il vantaggio di questa risposta è che include il metodo corrente nella backtrace, mentre Kernel#calleresclude il metodo corrente. Ad esempio, MyClass.new.returns_caller => ["(irb):42:in 'irb_binding'",...] non è utile come MyClass.new.returns_thread_backtrace => ["(irb):38:in 'backtrace'","(irb):38:in 'returns_thread_backtrace'","(irb):43:in 'irb_binding'",...]
stwr667,

6

Lo uso per mostrare una pagina di errore personalizzata quando vengono sollevate eccezioni.

rescue_from Exception do |exception|
  logger.error exception.class
  logger.error exception.message
  logger.error exception.backtrace.join "\n"
  @exception = exception


  # ExceptionNotifier::Notifier.exception_notification env, @exception

  respond_to do |format|
    if [AbstractController::ActionNotFound, ActiveRecord::RecordNotFound, ActionController::RoutingError, ActionController::UnknownAction].include?(exception.class)
      format.html { render :template => "errors/404", :status => 404 }
      format.js   { render :nothing => true, :status => 404 }
      format.xml  { render :nothing => true, :status => 404 }
    elsif exception.class == CanCan::AccessDenied
      format.html {
        render :template => "errors/401", :status => 401 #, :layout => 'application'
      }
      # format.js   { render :json => { :errors => [exception.message] }, :status => 401 }
      # format.js   { render :js => 'alert("Hello 401")' }
      format.js   { render :template => 'errors/401.js.erb' }

    else
      ExceptionNotifier::Notifier.exception_notification(env, exception).deliver        
      format.html { render :template => "errors/500", :status => 500 } #, :layout => 'im2/application' }
      # format.js   { render :nothing => true, :status => 500 }
      format.js   { render :template => 'errors/500.js.erb' }

    end
  end
end
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.