programing

루비에서 '예외 => e를 구출하는 것은 왜 나쁜 스타일인가요?

megabox 2023. 6. 8. 19:35
반응형

루비에서 '예외 => e를 구출하는 것은 왜 나쁜 스타일인가요?

Ryan Davis의 Ruby QuickRef는 (설명 없이) 다음과 같이 말합니다.

예외를 복구하지 않습니다.아니면 찌르겠습니다.

왜 안 되나요?옳은 일은 무엇입니까?

TL;DR: 사용StandardError일반적인 예외를 포착하기 위해 대신.발생한 원예다예발경우생한시래외 (예: 예도기록복구경는우하하록복구가외를예),복구▁when경▁rescuing), e원래▁isException아마 괜찮을 겁니다


Exception루비의 예외 계층 구조의 루트입니다. 그래서 당신이rescue Exception당신은 다음과 같은 하위 클래스를 포함하여 모든 것에서 구조합니다.SyntaxError,LoadError,그리고.Interrupt.

조구Interrupt를 사용하여 프로그램을 종료할 수 없습니다.

조구SignalException프로그램이 신호에 올바르게 응답하지 못하도록 합니다.다음을 제외하고는 죽일 수 없을 것입니다.kill -9.

조구SyntaxError는 것을 의미합니다.eval실패하면 자동으로 수행됩니다.

이 모든 것들은 이 프로그램을 실행하고, 시도하거나, 또는kill그것:

loop do
  begin
    sleep 1
    eval "djsakru3924r9eiuorwju3498 += 5u84fior8u8t4ruyf8ihiure"
  rescue Exception
    puts "I refuse to fail or be stopped!"
  end
end

에서 구출하기Exception기본값도 아닙니다.하고있다

begin
  # iceberg!
rescue
  # lifeboats
end

는 에서구 않습다니지하에서 .Exception에서 구출합니다.StandardError다 더 .StandardError 지만구는에서 하는 것.Exception 범위를 좁히는 대신 범위를 넓히고 치명적인 결과를 초래하여 버그 검색을 매우 어렵게 만들 수 있습니다.


만약 당신이 구조하고 싶은 상황이 있다면,StandardError예외가 있는 변수가 필요합니다. 다음 형식을 사용할 수 있습니다.

begin
  # iceberg!
rescue => e
  # lifeboats
end

이 값은 다음과 같습니다.

begin
  # iceberg!
rescue StandardError => e
  # lifeboats
end

몇 안 되는 일반적인 경우 중 하나는 구조하는 것이 제정신인 경우입니다.Exception는 로깅 이 를 즉시 . " " " "/ " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

begin
  # iceberg?
rescue Exception => e
  # do some logging
  raise # not enough lifeboats ;)
end

실제 규칙은 다음과 같습니다.예외를 버리지 마세요.당신의 인용문의 저자의 객관성은 의심스럽다, 그것이 그것으로 끝난다는 사실에서 증명됩니다.

아니면 찌르겠습니다.

물론 신호(기본적으로) 예외가 발생하고 일반적으로 장시간 실행되는 프로세스는 신호를 통해 종료되므로 예외를 포착하고 신호 예외를 종료하지 않으면 프로그램을 중지하기가 매우 어렵습니다.그러니 이러지 마:

#! /usr/bin/ruby

while true do
  begin
    line = STDIN.gets
    # heavy processing
  rescue Exception => e
    puts "caught exception #{e}! ohnoes!"
  end
end

아니, 진짜, 하지 마.작동하는지 확인하려고 실행하지도 마.

그러나 스레드 서버가 있고 모든 예외를 원하지 않는다고 가정합니다.

  1. 무시됨(기본값)
  2. 합니다. (이 " " "라고 말하면 합니다.thread.abort_on_exception = true).

그러면 연결 처리 스레드에서 이를 완벽하게 수용할 수 있습니다.

begin
  # do stuff
rescue Exception => e
  myLogger.error("uncaught #{e} exception while handling connection: #{e.message}")
    myLogger.error("Stack trace: #{backtrace.map {|l| "  #{l}\n"}.join}")
end

위의 내용은 Ruby의 기본 예외 처리기의 변형으로 사용할 수 있으며 프로그램을 종료하지 않는다는 장점이 있습니다.레일즈는 요청 처리기에서 이 작업을 수행합니다.

신호 예외는 메인 스레드에서 발생합니다.백그라운드 스레드는 그것들을 얻지 못하므로, 그곳에서 그것들을 잡으려고 노력하는 것은 의미가 없습니다.

이 기능은 문제가 발생할 때마다 프로그램이 단순히 중지되지 않도록 하는 프로덕션 환경에서 특히 유용합니다.그런 다음 로그에 스택 덤프를 저장하고 코드에 추가하여 콜 체인 아래에서 보다 우아한 방식으로 특정 예외를 처리할 수 있습니다.

또한 거의 동일한 효과를 가진 또 다른 루비 관용구가 있다는 것에 주목하십시오.

a = do_something rescue "something else"

줄에서 만약에 이줄면 약에만.do_something예외를 제기하고, 그것은 루비에 의해 잡히고, 버려집니다.a됨 됨할"something else".

일반적으로 걱정할 필요가 없다는 것을 알고 있는 특별한 경우를 제외하고는 그렇게 하지 마십시오.한 가지 예:

debugger rescue nil

debugger함수는 코드에서 중단점을 설정하는 데 꽤 좋은 방법이지만 디버거와 레일즈 외부에서 실행되는 경우 예외가 발생합니다.이제 이론적으로 디버그 코드를 프로그램에 방치해서는 안 됩니다(pff! 아무도 그렇게 하지 않습니다!). 하지만 어떤 이유로 디버거를 계속 실행하지 않고 잠시 동안 디버거를 실행하고 싶을 수도 있습니다.

참고:

  1. 신호 예외를 포착하고 무시하는 다른 사용자의 프로그램을 실행한 경우(위 코드 참조):

    • Linux의에 리스에, 셸서서, 입력에를 합니다.pgrep ruby또는ps | grep ruby가 되는PID를 , "PID"를 실행합니다.kill -9 <PID>.
    • Windows에서 작업 관리자(--)CTRLSHIFTESC를 사용하고 "프로세스" 탭으로 이동하여 프로세스를 찾은 다음 마우스 오른쪽 단추로 클릭하고 "프로세스 종료"를 선택합니다.
  2. 어떤 이유로든 이러한 무시 예외 블록으로 가득 찬 다른 사람의 프로그램으로 작업하는 경우, 이를 메인 라인의 맨 위에 배치하는 것이 한 가지 가능한 cop-out입니다.

    %W/INT QUIT TERM/.each { |sig| trap sig,"SYSTEM_DEFAULT" }
    

    이로 인해 프로그램은 정리 없이 예외 처리기를 즉시 종료하고 우회하여 정상 종료 신호에 응답합니다.따라서 데이터 손실이나 이와 유사한 원인이 될 수 있습니다.조심해!

  3. 필요한 경우:

    begin
      do_something
    rescue Exception => e
      critical_cleanup
      raise
    end
    

    실제로 다음을 수행할 수 있습니다.

    begin
      do_something
    ensure
      critical_cleanup
    end
    

    두번째경는우에,는에,critical cleanup예외가 발생했는지 여부에 관계없이 매번 호출됩니다.

TL;DR

하지 마rescue Exception => e(및 예외를 다시 적용하지 않음) - 또는 다리에서 운전할 수 있습니다.


여러분이 차를 타고 있다고 가정해 보겠습니다(Ruby를 실행합니다.에 무선 을 사용하는 새로운 이은 최에무이업장새휠다설니습치했을은링스템시어스티근된착이드선이시스템그레이▁(▁you(▁uses▁system▁with▁wheelwhich다▁a▁the은니▁steering▁installed템습▁upgrade▁recently▁new시최스했치).eval은 한 것을 .), 하만지당신프머래중구망을쳤는것을몰다다랐니습문이명한로은그▁),다.

당신은 다리 위에 있고, 당신이 난간 쪽으로 조금 가고 있다는 것을 깨닫고, 왼쪽으로 돌면 됩니다.

def turn_left
  self.turn left:
end

웁스! 그건 아마 좋지 않을 거야™, 운 좋게도, 루비는.SyntaxError.

차가 즉시 멈추어야 합니다. 그렇죠?

아니.

begin
  #...
  eval self.steering_wheel
  #...
rescue Exception => e
  self.beep
  self.log "Caught #{e}.", :warn
  self.log "Logged Error - Continuing Process.", :info
end

삐삐삐

경고: 구문 오류 예외가 발생했습니다.

정보: 기록된 오류 - 계속 진행 중입니다.

잘못되었다는 을 알아차리고 휴식 시간을 세게 .^C:Interrupt)

삐삐삐

경고: 인터럽트 예외가 발생했습니다.

정보: 기록된 오류 - 계속 진행 중입니다.

네 - 별로 도움이 되지 않았습니다.에서 꽤 .killing:SignalException).

삐삐삐

경고: 신호 예외 예외가 발생했습니다.

정보: 기록된 오류 - 계속 진행 중입니다.

.kill -9), 합니다. (시키지 않았기 수 . 의 차 에 있는 그앞) 그리고 당신의 차 뒤에 있는 컴퓨터는 그 앞 좌석으로 돌진합니다.콜라 반 캔이 종이 위로 쏟아집니다.뒤에 있는 식료품들은 으깨져 있고, 대부분 계란 노른자와 우유로 덮여 있습니다.자동차는 심각한 수리와 청소가 필요합니다. (데이터 손실)

보험(백업)이 있기를 바랍니다.아 네 - 에어백이 팽창하지 않았기 때문에, 아마 다쳤을 것입니다(해고 등).


하지만 잠깐!많은 이유가 있습니다.rescue Exception => e!

예를 들어, 당신이 그 차라고 가정하고, 만약 차가 안전 정지 운동량을 초과한다면 에어백이 팽창하는지 확인하려고 합니다.

 begin 
    # do driving stuff
 rescue Exception => e
    self.airbags.inflate if self.exceeding_safe_stopping_momentum?
    raise
 end

다음은 이 규칙의 예외입니다.잡으실 수 있습니다Exception 예외를 다시 적용하는 경우에만 가능합니다.그래서, 더 나은 규칙은 절대 삼키지 않는 것입니다.Exception오류를 항상 다시 확인합니다.

하지만 Ruby와 같은 언어로 구조를 추가하는 것은 모두 잊기 쉬우며, 문제를 다시 제기하기 직전에 구조 성명을 넣는 것은 약간 비건조하게 느껴집니다.그리고 당신은 잊기를 원하지 않습니다.raise 오류를 을 빌어요.만약 그렇다면, 그 오류를 찾는 데 행운을 빌어요.

고맙게도, 루비는 굉장해요, 당신은 그냥 그것을 사용할 수 있어요.ensure키워드: 코드가 실행되는지 확인합니다.ensure키워드는 어떤 경우에도 코드를 실행합니다. 예외가 던져진 경우, 그렇지 않은 경우, 유일한 예외는 세상이 끝나는 경우(또는 다른 예상치 못한 이벤트)입니다.

 begin 
    # do driving stuff
 ensure
    self.airbags.inflate if self.exceeding_safe_stopping_momentum?
 end

쾅! 그리고 그 코드는 어쨌든 실행되어야 합니다.이 사용해야 하는 유일한 는 당이사하유이는유한일입니다.rescue Exception => e예외에 대한 액세스가 필요한 경우 또는 예외에 대해 코드만 실행하려는 경우입니다.그리고 오류를 다시 제기하는 것을 기억하세요.매번.

참고: @Niall이 지적했듯이 항상 실행되도록 합니다.이는 문제가 발생하더라도 프로그램이 거짓말을 하고 예외를 발생시키지 않을 수 있기 때문에 유용합니다.에어백을 부풀리는 것과 같은 중요한 작업에서는 어떤 일이 있어도 반드시 그렇게 해야 합니다.그렇기 때문에 예외를 두었는지 아닌지, 차가 멈출 때마다 확인하는 것이 좋습니다.에어백을 부풀리는 것은 대부분의 프로그래밍 상황에서 약간의 드문 작업이지만, 이것은 실제로 대부분의 청소 작업에서 매우 일반적입니다.

왜냐하면 이것은 모든 예외를 포착하기 때문입니다.프로그램이 복구될 가능성은 거의 없습니다.

복구 방법을 알고 있는 예외만 처리해야 합니다.특정 유형의 예외가 예상되지 않는 경우 이를 처리하지 않고 큰 소리로 충돌한 다음(로그에 세부 정보 쓰기) 로그를 진단하고 코드를 수정합니다.

예외를 삼키는 것은 좋지 않습니다. 이러지 마십시오.

이는 처리 방법을 모르는 예외를 포착해서는 안 되는 규칙의 특정 사례입니다.처리 방법을 모르는 경우에는 항상 시스템의 다른 부분에서 이를 포착하여 처리하도록 하는 것이 좋습니다.

이 블로그 게시물은 이를 완벽하게 설명합니다.Ruby의 예외 대 표준 오류: 뭐가 달라요?

예외를 복구하면 안 되는 이유

예외 구조의 문제는 실제로 예외에서 상속되는 모든 예외를 복구한다는 것입니다.그 말은... 그들 모두야!

루비에서 내부적으로 사용하는 예외가 있기 때문에 문제가 됩니다.그것들은 당신의 앱과 아무 관련이 없으며, 그것들을 삼키면 나쁜 일이 일어날 것입니다.

다음은 몇 가지 중요한 사항입니다.

  • 신호 예외:인터럽트 - 이를 복구하면 control-c를 눌러 앱을 종료할 수 없습니다.

  • 스크립트 오류:구문 오류 - 구문 오류를 삼키면 puts("Forgot something")와 같은 작업이 자동으로 실패합니다.

  • 메모리 오류 없음 - RAM을 모두 사용한 후 프로그램이 계속 실행되면 어떻게 되는지 알고 싶으십니까?나 역시 그렇지 않다.

begin
  do_something()
rescue Exception => e
  # Don't do this. This will swallow every single exception. Nothing gets past it. 
end

이러한 시스템 수준의 예외를 완전히 무시하고 싶지는 않을 것입니다.모든 응용 프로그램 수준 오류만 탐지하려고 합니다.예외로 인해 코드가 발생했습니다.

다행히도, 이것에 대한 쉬운 방법이 있습니다.

대신 표준 오류 복구

주의해야 할 모든 예외는 StandardError에서 상속됩니다.이들은 우리의 오랜 친구들입니다.

NoMethodError - 존재하지 않는 메서드를 호출하려고 할 때 발생합니다.

유형 오류 - 1 + ""와 같은 것으로 인해 발생합니다.

런타임 오류 - 누가 좋은 오래된 런타임 오류를 잊을 수 있습니까?

이러한 오류를 복구하려면 StandardError를 복구해야 합니다.당신은 다음과 같은 것을 작성함으로써 그것을 할 수 있습니다.

begin
  do_something()
rescue StandardError => e
  # Only your app's exceptions are swallowed. Things like SyntaxErrror are left alone. 
end

하지만 루비 덕분에 훨씬 더 쉽게 사용할 수 있었습니다.

예외 클래스를 전혀 지정하지 않으면 Ruby는 표준 오류를 의미한다고 가정합니다.따라서 아래 코드는 위 코드와 동일합니다.

begin
  do_something()
rescue => e
  # This is the same as rescuing StandardError
end

언급URL : https://stackoverflow.com/questions/10048173/why-is-it-bad-style-to-rescue-exception-e-in-ruby

반응형