Ruft den aktuellen Stack-Trace in Ruby ab, ohne eine Ausnahme auszulösen

138

Ich möchte die aktuelle Rückverfolgung (Stacktrace) in einer Rails 3-App ohne Ausnahme protokollieren . Irgendeine Idee wie?

Warum will ich das? Ich versuche, die Aufrufe zu verfolgen, die getätigt werden, wenn Rails nach einer Vorlage sucht, damit ich einen Teil des Prozesses auswählen kann, der überschrieben werden soll (weil ich den Ansichtspfad für einen bestimmten Controller meiner Unterklasse ändern möchte).

Ich möchte es aus der Datei aufrufen : gems\actionpack-3.2.3\lib\action_dispatch\middleware\templates\rescues\missing_template.erb. Ich weiß, dass dies keine bewährte Methode ist, aber ich weiß, dass sie sich hinter dem Stapel befindet, von dem aus die Suche nach Vorlagen erfolgt.

JellicleCat
quelle
4
Schmutzige Lösung: Dort eine Ausnahme auslösen, sofort retten und protokollieren e.backtrace. Ich habe es in einem der Projekte gesehen, mit denen ich arbeite. Nicht der schönste Ansatz, aber es funktioniert. Ich hoffe jedoch, eine bessere Lösung von jemand anderem zu hören.
KL-7

Antworten:

185

Sie können verwenden 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

Ausgabe:

caller.rb:8:in `bar'
caller.rb:12:in `baz'
caller.rb:15:in `<main>'
KL-7
quelle
Ist es nicht Kernel.caller- mit einem Punkt? Kernel.new.callerist hier nicht definiert
ecoologic
8
Nein, technisch gesehen callerhandelt es sich um eine Instanzmethode. Da das KernelModul in jeder Ruby-Klasse enthalten ist (außer BasicObjectin 1.9), ist es als Instanzmethode für jedes Objekt verfügbar (es ist jedoch privat). Sie können es nicht Kernel.new.callereinfach so aufrufen, weil Sie ein Modul nicht instanziieren können (es hat keine newMethode).
KL-7
Dieser unterstützt einen Parameter zum Überspringen einer beliebigen Anzahl von Anrufern. siehe: stackoverflow.com/a/3829269/520567
akostadinov
7
Für hübschen Druckgebrauch - Rails.logger.debug caller.join("\n")oder puts caller.join("\n"). Vielen Dank.
Jignesh Gohel
20

Versuchen Sie es mit

Thread.current.backtrace
Rajat Bansal
quelle
1
Der Vorteil dieser Antwort besteht darin, dass die aktuelle Methode in die Rückverfolgung aufgenommen wird, während Kernel#callerdie aktuelle Methode weggelassen wird . ZB MyClass.new.returns_caller => ["(irb):42:in 'irb_binding'",...] ist nicht so hilfreich wie MyClass.new.returns_thread_backtrace => ["(irb):38:in 'backtrace'","(irb):38:in 'returns_thread_backtrace'","(irb):43:in 'irb_binding'",...]
stwr667
6

Ich benutze dies, um eine benutzerdefinierte Fehlerseite anzuzeigen, wenn eine Ausnahme ausgelöst wird.

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
Stefano Lampis
quelle