Pobierz bieżący ślad stosu w Rubim bez zgłaszania wyjątku

139

Chcę rejestrować bieżący ślad śledzenia (stacktrace) w aplikacji Rails 3 bez wystąpienia wyjątku. Każdy pomysł jak?

Dlaczego tego chcę? Próbuję prześledzić wywołania, które są wykonywane, gdy Railsy szukają szablonu, aby móc wybrać część procesu do przesłonięcia (ponieważ chcę zmienić ścieżkę widoku dla określonego mojego kontrolera podklasy).

Chciałbym wywołać ją z pliku: gems\actionpack-3.2.3\lib\action_dispatch\middleware\templates\rescues\missing_template.erb. Wiem, że to nie jest najlepsza praktyka, ale wiem, że jest to poniżej stosu, z którego następuje wyszukiwanie szablonów.

JellicleCat
źródło
4
Brudne rozwiązanie: zgłoś tam wyjątek, natychmiast go uratuj i zarejestruj e.backtrace. Widziałem to w jednym z projektów, nad którymi pracuję. Nie jest to najmilsze podejście, ale działa. Mam jednak nadzieję, że ktoś inny usłyszy lepsze rozwiązanie.
KL-7

Odpowiedzi:

185

Możesz użyć 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

Wynik:

caller.rb:8:in `bar'
caller.rb:12:in `baz'
caller.rb:15:in `<main>'
KL-7
źródło
Czy to nie jest Kernel.caller- z kropką? Kernel.new.callernie jest tutaj zdefiniowane
ecoologic,
8
Nie, technicznie callerjest to metoda instancji. Ponieważ Kernelmoduł jest zawarty w każdej klasie Ruby (z wyjątkiem BasicObject1.9), jest dostępny jako metoda instancji na każdym obiekcie (chociaż jest prywatny). Nie możesz tego wywołać tak Kernel.new.callerpo prostu, ponieważ nie możesz utworzyć instancji modułu (nie ma on newmetody).
KL-7,
ten obsługuje parametr pomijania dowolnej liczby wywołujących; patrz: stackoverflow.com/a/3829269/520567
akostadinov
7
Do ładnego druku - Rails.logger.debug caller.join("\n")lub puts caller.join("\n"). Dzięki.
Jignesh Gohel
20

Spróbuj użyć

Thread.current.backtrace
Rajat Bansal
źródło
1
Zaletą tej odpowiedzi jest to, że zawiera ona bieżącą metodę w śladzie wstecznym, podczas gdy Kernel#callerpomija bieżącą metodę. Np. MyClass.new.returns_caller => ["(irb):42:in 'irb_binding'",...] Nie jest tak pomocny jak MyClass.new.returns_thread_backtrace => ["(irb):38:in 'backtrace'","(irb):38:in 'returns_thread_backtrace'","(irb):43:in 'irb_binding'",...]
stwr667
6

Używam tego, aby wyświetlić niestandardową stronę błędu, gdy zostanie zgłoszony wyjątek.

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
źródło