루비가 잘린 것이 아니라 전체 역 추적을 인쇄하도록하려면 어떻게해야합니까?


170

예외가 발생하면 종종 호출 스택 내부에서 발생합니다. 이런 일이 발생하는 경우, 종종 실제 코드 위반이 숨겨집니다.

tmp.rb:7:in `t': undefined method `bar' for nil:NilClass (NoMethodError)
        from tmp.rb:10:in `s'
        from tmp.rb:13:in `r'
        from tmp.rb:16:in `q'
        from tmp.rb:19:in `p'
        from tmp.rb:22:in `o'
        from tmp.rb:25:in `n'
        from tmp.rb:28:in `m'
        from tmp.rb:31:in `l'
         ... 8 levels...
        from tmp.rb:58:in `c'
        from tmp.rb:61:in `b'
        from tmp.rb:64:in `a'
        from tmp.rb:67

"... 8 레벨 ..."잘림으로 인해 많은 문제가 발생했습니다. 나는 이것에 대한 인터넷 검색에별로 성공하지 못했습니다. 루비에게 덤프에 전체 스택을 포함시키고 싶다고 어떻게 말합니까?


2
대신 명령 줄 에서이 작업을 수행 할 수 있습니까?
Andrew Grimm

답변:


241

예외 # backtrace에는 전체 스택이 있습니다.

def do_division_by_zero; 5 / 0; end
begin
  do_division_by_zero
rescue => exception
  puts exception.backtrace
  raise # always reraise
end

(Peter Cooper의 Ruby Inside 블로그 에서 영감을 받음 )


15
적어도 예제를 완료하기 위해 예외를 다시 말하고 싶습니다.
reto

13
당신이 말을 걸기 만하면됩니다 raise. 제기하려는 실행을 명시 적으로 지정할 필요가 없습니다.
Timo

좋아, 나는 항상 당신이 제기하기 위해 이전 예외를 통과해야한다고 생각했습니다. 나는 그것이 마지막으로 구출 된 예외로 기본값임을 알지 못했습니다.
unflores

코드에서 예외가 발생하지 않으면 어디로 갔는지의 스택 추적을보고 싶습니까?
Alex Levine

170

간단한 원 라이너를 원한다면이 작업을 수행 할 수도 있습니다.

puts caller

2
멋진 트릭. 고마워 나는 그것을 raise인수없이 사용할 수 있다는 것을 몰랐다 . 나는 그것이 한 줄 rescue짜리 라이너로 올바르게 취급 될 것이라는 것을 알지 못했습니다 . 또한 같은 전역 변수를 완전히 무시합니다 $!.
Dmytrii Nagirniak

11
제기 / 구조 필요가 없습니다, 당신은 다음과 같이 Kernel # caller를 사용할 수 있습니다 :puts "this line was reached by #{caller.join("\n")}"
Stephen C

아, 나는이 답변을 게시 한 직후 그 사실을 발견하고 업데이트하는 것을 잊었습니다. 감사합니다
익명의 겁쟁이

y callerJava 스택 추적과 같은 출력을 인쇄하는 데 사용 합니다.
so_mv

caller(0,2)stacktrace에서 두 개의 최신 항목을 반환합니다. 축약 된 스택 트레이스를 출력하기에 좋습니다.
Magne

100

이렇게하면 오류 설명과 깔끔하고 들여 쓰기 된 스택 추적이 생성됩니다.

begin               
 # Some exception throwing code
rescue => e
  puts "Error during processing: #{$!}"
  puts "Backtrace:\n\t#{e.backtrace.join("\n\t")}"
end

49

IRB에는이 끔찍한 "기능"에 대한 설정이 있으며이를 사용자 지정할 수 있습니다.

~/.irbrc다음 행을 포함 하는 파일을 작성하십시오 .

IRB.conf[:BACK_TRACE_LIMIT] = 100

이를 통해 irb적어도 100 개의 스택 프레임을 볼 수 있습니다 . 비 대화식 런타임에 해당하는 설정을 찾을 수 없습니다.

IRB 사용자 정의에 대한 자세한 정보는 Pickaxe 서적 에서 찾을 수 있습니다 .


3
이것은 "... X levels ..."대신 더 많은 역 추적을 표시하는 방법에 대한 질문을 다루기 때문에 허용되는 대답이어야합니다.
nickh

13

콜 스택을위한 하나의 라이너 :

begin; Whatever.you.want; rescue => e; puts e.message; puts; puts e.backtrace; end

모든 보석이없는 콜스 택용 라이너 :

begin; Whatever.you.want; rescue => e; puts e.message; puts; puts e.backtrace.grep_v(/\/gems\//); end

모든 gem이없고 현재 디렉토리에 상대적인 콜 스택을위한 하나의 라이너

begin; Whatever.you.want; rescue => e; puts e.message; puts; puts e.backtrace.grep_v(/\/gems\//).map { |l| l.gsub(`pwd`.strip + '/', '') }; end

2
하나의 라이너는 실제로 여러 문장이있을 때 나쁜 것입니다.
nurettin

3
@nurettin 이것은 빠른 디버깅 목적을위한 것이므로 한 줄로 만들면 대부분 대화 형 쉘에서 쉽게 복사하여 붙여 넣을 수 있습니다
Dorian

@Dorian "대화식 쉘이 왜 유용한가? (쉘 스크립트 제외)"라는 질문에 대해 생각 나게합니다.
Sapphire_Brick

9

중요한 루비 추적을 모방합니다.

begin
  0/0  # or some other nonsense
rescue => e
  puts e.backtrace.join("\n\t")
       .sub("\n\t", ": #{e}#{e.class ? " (#{e.class})" : ''}\n\t")
end

흥미롭게도 '처리되지 않은 예외'를 올바르게 처리하지 않아 'RuntimeError'로보고되지만 위치는 정확합니다.


유감스럽게도 하나의 답변을 해주셔서 유감입니다. 나는 이것을 어디에나 추가한다
Dbz

4

테스트 환경을로드하려고 할 때 (레이크 테스트 또는 자동 테스트를 통해) 이러한 오류가 발생하여 IRB 제안이 도움이되지 않았습니다. 나는 전체 test / test_helper.rb를 begin / rescue 블록에 포장하고 결과를 수정했습니다.

begin
  class ActiveSupport::TestCase
    #awesome stuff
  end
rescue => e
  puts e.backtrace
end

0

[모든 스레드 역 추적을 조사하여 범인을 찾으십시오]
완전히 확장 된 호출 스택에서도 둘 이상의 스레드를 사용할 때 실제 문제 코드 행을 숨길 수 있습니다!

예 : 하나의 스레드가 루비 해시를 반복하고 다른 스레드는 수정하려고합니다. 팔! 예외! 그리고 'busy'해시를 수정하려고 할 때 얻는 스택 추적의 문제점은 해시를 수정하려는 위치까지 함수 체인을 표시하지만 현재 병렬로 반복하는 사람은 표시하지 않는다는 것입니다. 누가 그것을 소유하고 있는지)! 다음은 현재 실행중인 모든 스레드에 대해 스택 추적을 인쇄하여이를 파악하는 방법입니다. 이를 수행하는 방법은 다음과 같습니다.

# This solution was found in comment by @thedarkone on https://github.com/rails/rails/issues/24627
rescue Object => boom

    thread_count = 0
    Thread.list.each do |t|
      thread_count += 1
      err_msg += "--- thread #{thread_count} of total #{Thread.list.size} #{t.object_id} backtrace begin \n"
      # Lets see if we are able to pin down the culprit
      # by collecting backtrace for all existing threads:
      err_msg += t.backtrace.join("\n")
      err_msg += "\n---thread #{thread_count} of total #{Thread.list.size} #{t.object_id} backtrace end \n"
    end

    # and just print it somewhere you like:
    $stderr.puts(err_msg)

    raise # always reraise
end

위의 코드 스 니펫은 실제로 얼마나 많은 스레드를 가지고 있는지 (x-ray와 같이) 얼마나 많은 스레드 수를 보여줄 수 있기 때문에 교육 목적으로도 유용합니다.


0

역 추적 Ruby gem을 사용할 수도 있습니다 (저는 저자입니다).

require 'backtrace'
begin
  # do something dangerous
rescue StandardError => e
  puts Backtrace.new(e)
end

4
보석을 왜 사용하고 싶은지 적어도 설명해 주시겠습니까? 샘플 출력을 보여줄 수 있습니까?
ioquatix
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.