Получить текущую трассировку стека в Ruby, не вызывая исключения

139

Я хочу записать текущую обратную трассировку (stacktrace) в приложении Rails 3 без исключений. Есть идеи как?

Зачем мне это? Я пытаюсь отследить вызовы, которые выполняются, когда Rails ищет шаблон, чтобы я мог выбрать часть процесса, которую нужно переопределить (потому что я хочу изменить путь представления для конкретного моего подклассного контроллера).

Я хотел бы назвать его из файла: gems\actionpack-3.2.3\lib\action_dispatch\middleware\templates\rescues\missing_template.erb. Я знаю, что это не лучшая практика, но я знаю, что это нисходящий поток от стека, где происходит поиск шаблонов.

JellicleCat
источник
4
Грязное решение: создать исключение, немедленно спасти его и записать в журнал e.backtrace. Я видел это в одном из проектов, с которыми я работаю. Не самый хороший подход, но он работает. Надеюсь услышать лучшее решение от кого-то еще.
KL-7

Ответы:

185

Вы можете использовать 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

Вывод:

caller.rb:8:in `bar'
caller.rb:12:in `baz'
caller.rb:15:in `<main>'
KL-7
источник
Не правда ли Kernel.caller- с точкой? Kernel.new.callerздесь не определено
ecoologic
8
Нет, технически callerэто метод экземпляра. Поскольку Kernelмодуль включен в каждый класс Ruby (кроме BasicObjectверсии 1.9), он доступен как метод экземпляра для любого объекта (хотя он и закрытый). Вы не можете вызвать его так Kernel.new.callerпросто, потому что не можете создать экземпляр модуля (у него нет newметода).
KL-7
этот поддерживает параметр, чтобы пропустить любое количество абонентов; см .: stackoverflow.com/a/3829269/520567
akostadinov
7
Для красивого использования печати - Rails.logger.debug caller.join("\n")или puts caller.join("\n"). Спасибо.
Джинеш Гоэль
20

Попробуйте использовать

Thread.current.backtrace
Раджат Бансал
источник
1
Преимущество этого ответа состоит в том, что он включает текущий метод в обратную трассировку, тогда Kernel#callerкак текущий метод не учитывается. Например MyClass.new.returns_caller => ["(irb):42:in 'irb_binding'",...] , не так полезно, как MyClass.new.returns_thread_backtrace => ["(irb):38:in 'backtrace'","(irb):38:in 'returns_thread_backtrace'","(irb):43:in 'irb_binding'",...]
stwr667
6

Я использую это, чтобы показать пользовательскую страницу ошибки при возникновении исключения.

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
Стефано Лампис
источник