Obtenga el seguimiento actual de la pila en Ruby sin generar una excepción

139

Quiero registrar la traza inversa actual (stacktrace) en una aplicación Rails 3 sin que ocurra una excepción. ¿Alguna idea de cómo?

¿Por qué quiero esto? Estoy tratando de rastrear las llamadas que se realizan cuando Rails busca una plantilla para poder elegir una parte del proceso para anular (porque quiero cambiar la ruta de vista para un controlador mío subclasificado en particular).

Me gustaría llamarlo desde el archivo: gems\actionpack-3.2.3\lib\action_dispatch\middleware\templates\rescues\missing_template.erb. Sé que esa no es la mejor práctica, pero sé que está aguas abajo de la pila desde donde ocurre la búsqueda de plantillas.

JellicleCat
fuente
44
Solución sucia: plantee una excepción allí, rescátela de inmediato e inicie sesión e.backtrace. Lo he visto en uno de los proyectos con los que estoy trabajando. No es el mejor enfoque, pero funciona. Sin embargo, espero escuchar una mejor solución de otra persona.
KL-7

Respuestas:

185

Puedes usar 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

Salida:

caller.rb:8:in `bar'
caller.rb:12:in `baz'
caller.rb:15:in `<main>'
KL-7
fuente
¿No es así Kernel.caller, con un punto? Kernel.new.callerno está definido aquí
ecoologic
8
No, técnicamente calleres un método de instancia. Como el Kernelmódulo se incluye en cada clase de Ruby (excepto BasicObjecten 1.9), está disponible como método de instancia en cualquier objeto (aunque es privado). No puede llamarlo Kernel.new.callersimplemente porque no puede crear una instancia de un módulo (no tiene newmétodo).
KL-7
este admite un parámetro para omitir cualquier número de llamadas; ver: stackoverflow.com/a/3829269/520567
akostadinov
77
Para un bonito uso de impresión, Rails.logger.debug caller.join("\n")o puts caller.join("\n"). Gracias.
Jignesh Gohel
20

Intenta usar

Thread.current.backtrace
Rajat Bansal
fuente
1
La ventaja de esta respuesta es que incluye el método actual en la traza inversa, mientras que Kernel#calleromite el método actual. Por ejemplo, MyClass.new.returns_caller => ["(irb):42:in 'irb_binding'",...] no es tan útil como MyClass.new.returns_thread_backtrace => ["(irb):38:in 'backtrace'","(irb):38:in 'returns_thread_backtrace'","(irb):43:in 'irb_binding'",...]
stwr667
6

Lo uso para mostrar una página de error personalizada cuando se generan excepciones.

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
fuente