프로그래밍/Python

[Python] 의도치 않은 모든 예외 기록 처리하기

채윤아빠 2023. 8. 14. 10:25
728x90
반응형

문제점

try ... catch 를 아무리 잘 이용하더라도 예측하지 못했던 오류가 발생할 수 있습니다.

특히, PyQt 등을 이용한 GUI라면 예측하지 못한 오류로 인하여 GUI가 사라지거나, Flask 등의 웹 서비스라면 서비스가 중단될 수도 있습니다. 이런 경우, 어떤 문제로 인하여 발생한 것인지 확인할 수 있도록 예측하지 못한 모든 예외를 기록하는 방법을 정리해 둡니다.


일반적인 예외 처리

일반적으로 파이썬에서 예외를 처리하기 위해서는 "try... except" 구문을 이용합니다. 다음 예제는 엑셀 시트의 특정 셀에 값을 채워넣는 과정에서 예외를 처리하는 것을 간단하게 보여 줍니다.

    for row in gain_data_list[1:]:
        try:
            sheet.cell(row = row_base, column = col_base + 0, value = float(row[1]))
            sheet.cell(row = row_base, column = col_base + 1, value = float(row[3]))
            sheet.cell(row = row_base, column = col_base + 2, value = float(row[5]))
            sheet.cell(row = row_base, column = col_base + 3, value = float(row[8]))
        except:
            print(f'{row_base} : {row}')
        row_base += 1

위와 같이 예외가 발생할만한 곳에 "try... except" 구문을 모두 이용하여 예외를 처리할 수도 있겠지만, 발생 가능한 곳을 모두 찾아서 "try... except" 구문을 남발하는 것은 코드의 유지보수 측면에서 좋지만은 않습니다.

특히나 Qt를 활용한 GUI나, Flask 등을 이용한 웹 서비스를 개발한 상태에서 미처 파악하지 못한 곳에서 발생한 오류로 인하여 GUI가 갑작스럽게 사라지거나, 웹 서비스가 중단되면 큰 문제가 됩니다.


전체 오류 처리 방법

예상하지 못한 오류가 발생하였을 때, 이를 잡아서 처리하는 방법은 다음과 같습니다.

  • 예상하지 못한 오류를 처리하기 위한 핸들러를 정의합니다.
  • 작성한 핸들러를 "sys.execepthook"에 설정합니다.

다음 핸들러를 참고하시기 바랍니다.

    def custom_excepthook(exc_type, exc_value, exc_traceback):
        # Do not print exception when user cancels the program
        if issubclass(exc_type, KeyboardInterrupt):
            sys.__excepthook__(exc_type, exc_value, exc_traceback)
            return

        global _error_logger, _sigErrorThread
        if (_error_logger == None):
            return

        _error_logger.error("An uncaught exception occurred:")
        _error_logger.error(f"Type: {exc_type}")
        _error_logger.error(f"Value: {exc_value}")

        if exc_traceback:
            format_exception = traceback.format_tb(exc_traceback)
            for line in format_exception:
                _error_logger.error(repr(line))

        #showerror(None, const.TITLE_MAIN_WINDOW, f'An uncaught exception occurred.\n\n./logs/error.log 파일에 상세한 오류 내용이 기록됩니다.')
        if (_sigErrorThread != None):
            _sigErrorThread.emit(f'An uncaught exception occurred.\n상단 캡션바의 "X"를 눌러 진행창을 닫아 주십시오.\n\n./logs/error.log 파일에 상세한 오류 내용이 기록됩니다.')

예외 처리 핸들러로 전달되는 매개변수는 다음과 같습니다.

  • exc_type : 발생한 예외의 클래스 type
  • exc_value : 발생한 예외 객체 (인스턴스)
  • exc_traceback : 발생한 예외에 대한 traceback 정보 (콜백정보)

"exc_traceback" 정보를 이용하여 오류가 발생한 지점을 상세하게 파악할 수 있습니다.

오류가 발생하면 다음과 같은 형태로 출력됩니다.

2023-08-18 13:59:37,029 ERROR 1578] An uncaught exception occurred:
2023-08-18 13:59:37,030 ERROR 1579] Type: <class 'TypeError'>
2023-08-18 13:59:37,033 ERROR 1580] Value: setText(self, a0: str): argument 1 has unexpected type 'int'
2023-08-18 13:59:37,038 ERROR 1585] '  File "main_window.py", line 1602, in \n'
2023-08-18 13:59:37,038 ERROR 1585] '  File "main_window.py", line 1054, in __init__\n'
2023-08-18 13:59:37,039 ERROR 1585] '  File "main_window.py", line 1099, in initUI\n'
2023-08-18 13:59:37,039 ERROR 1585] '  File "qt_utils.py", line 43, in loadWidgetFromConf\n'

참고자료