python异常处理之 traceback 解析示例

àì夳堔傛蜴生んèń 2022-05-31 00:42 359阅读 0赞

python异常处理之 traceback 解析示例

其中,cgitb适合在开发的过程中进行调试,而logging适合在线上环境使用,二者都非常方便;

(1)Python中的异常栈跟踪

  1. def func(a, b):
  2. return a / b
  3. if __name__ == '__main__':
  4. import sys
  5. import traceback
  6. try:
  7. func(1, 0)
  8. except Exception as e:
  9. print "print exc"
  10. traceback.print_exc(file=sys.stdout)

输出:

  1. print exc
  2. Traceback (most recent call last):
  3. File "/Users/a6/test_exception_process.py", line 34, in <module>
  4. func(1, 0)
  5. File "/Users/a6/test_exception_process.py", line 29, in func
  6. return a / b
  7. ZeroDivisionError: integer division or modulo by zero

其实traceback.print_exc()函数只是traceback.print_exception()函数的一个简写形式,而它们获取异常相关的数据都是通过sys.exc_info()函数得到的。

  1. def func(a, b):
  2. return a / b
  3. if __name__ == '__main__':
  4. import sys
  5. import traceback
  6. try:
  7. func(1, 0)
  8. except Exception as e:
  9. print "print_exception()"
  10. exc_type, exc_value, exc_tb = sys.exc_info()
  11. print 'the exc type is:', exc_type
  12. print 'the exc value is:', exc_value
  13. print 'the exc tb is:', exc_tb
  14. traceback.print_exception(exc_type, exc_value, exc_tb)

输出:

  1. print_exception()
  2. the exc type is: <type 'exceptions.ZeroDivisionError'>
  3. the exc value is: integer division or modulo by zero
  4. the exc tb is: <traceback object at 0x10acc9dd0>
  5. Traceback (most recent call last):
  6. File "/Users/a6/Downloads/PycharmProjects/speiyou_di_my/hive/shuangshi_haibian/test_exception_process.py", line 49, in <module>
  7. func(1, 0)
  8. File "/Users/a6/Downloads/PycharmProjects/speiyou_di_my/hive/shuangshi_haibian/test_exception_process.py", line 44, in func
  9. return a / b
  10. ZeroDivisionError: integer division or modulo by zero

sys.exc_info()返回的值是一个元组,其中第一个元素,exc_type是异常的对象类型,exc_value是异常的值,exc_tb是一个traceback对象,对象中包含出错的行数、位置等数据。然后通过print_exception函数对这些异常数据进行整理输出。
(2)traceback模块提供了extract_tb函数来更加详细的解释traceback对象所包含的数据:

  1. def func(a, b):
  2. return a / b
  3. if __name__ == '__main__':
  4. import sys
  5. import traceback
  6. try:
  7. func(1, 0)
  8. except:
  9. _, _, exc_tb = sys.exc_info()
  10. for filename, linenum, funcname, source in traceback.extract_tb(exc_tb):
  11. print "%-23s:%s '%s' in %s()" % (filename, linenum, source, funcname)

输出:

  1. /Users/a6/test_exception_process.py:67 'func(1, 0)' in <module>()
  2. /Users/a6/test_exception_process.py:62 'return a / b' in func()

(3)使用cgitb来简化异常调试

如果平时开发喜欢基于log的方式来调试,那么可能经常去做这样的事情,在log里面发现异常之后,因为信息不足,那么会再去额外加一些debug log来把相关变量的值输出。调试完毕之后再把这些debug log去掉。其实没必要这么麻烦,Python库中提供了cgitb模块来帮助做这些事情,它能够输出异常上下文所有相关变量的信息,不必每次自己再去手动加debug log。
cgitb的使用简单的不能想象:

  1. def func(a, b):
  2. return a / b
  3. if __name__ == '__main__':
  4. import cgitb
  5. cgitb.enable(format='text')
  6. import sys
  7. func(1, 0)

输出:

  1. /System/Library/Frameworks/Python.framework/Versions/2.7/bin/python2.7 /Users/a6/test_exception_process.py
  2. <type 'exceptions.ZeroDivisionError'>
  3. Python 2.7.10: /System/Library/Frameworks/Python.framework/Versions/2.7/Resources/Python.app/Contents/MacOS/Python
  4. Thu Feb 1 16:58:32 2018
  5. A problem occurred in a Python script. Here is the sequence of
  6. function calls leading up to the error, in the order they occurred.
  7. /Users/a6/test_exception_process.py in <module>()
  8. 79 cgitb.enable(format='text')
  9. 80 import sys
  10. 81 func(1, 0)
  11. 82
  12. 83
  13. func = <function func>
  14. /Users/a6/test_exception_process.py in func(a=1, b=0)
  15. 73
  16. 74 def func(a, b):
  17. 75 return a / b
  18. 76 if __name__ == '__main__':
  19. 77 import cgitb
  20. a = 1
  21. b = 0
  22. <type 'exceptions.ZeroDivisionError'>: integer division or modulo by zero
  23. __class__ = <type 'exceptions.ZeroDivisionError'>
  24. __delattr__ = <method-wrapper '__delattr__' of exceptions.ZeroDivisionError object>
  25. __dict__ = {}
  26. __doc__ = 'Second argument to a division or modulo operation was zero.'
  27. __format__ = <built-in method __format__ of exceptions.ZeroDivisionError object>
  28. __getattribute__ = <method-wrapper '__getattribute__' of exceptions.ZeroDivisionError object>
  29. __getitem__ = <method-wrapper '__getitem__' of exceptions.ZeroDivisionError object>
  30. __getslice__ = <method-wrapper '__getslice__' of exceptions.ZeroDivisionError object>
  31. __hash__ = <method-wrapper '__hash__' of exceptions.ZeroDivisionError object>
  32. __init__ = <method-wrapper '__init__' of exceptions.ZeroDivisionError object>
  33. __new__ = <built-in method __new__ of type object>
  34. __reduce__ = <built-in method __reduce__ of exceptions.ZeroDivisionError object>
  35. __reduce_ex__ = <built-in method __reduce_ex__ of exceptions.ZeroDivisionError object>
  36. __repr__ = <method-wrapper '__repr__' of exceptions.ZeroDivisionError object>
  37. __setattr__ = <method-wrapper '__setattr__' of exceptions.ZeroDivisionError object>
  38. __setstate__ = <built-in method __setstate__ of exceptions.ZeroDivisionError object>
  39. __sizeof__ = <built-in method __sizeof__ of exceptions.ZeroDivisionError object>
  40. __str__ = <method-wrapper '__str__' of exceptions.ZeroDivisionError object>
  41. __subclasshook__ = <built-in method __subclasshook__ of type object>
  42. __unicode__ = <built-in method __unicode__ of exceptions.ZeroDivisionError object>
  43. args = ('integer division or modulo by zero',)
  44. message = 'integer division or modulo by zero'
  45. The above is a description of an error in a Python program. Here is
  46. the original traceback:
  47. Traceback (most recent call last):
  48. File "/Users/a6/test_exception_process.py", line 81, in <module>
  49. func(1, 0)
  50. File "/Users/a6/test_exception_process.py", line 75, in func
  51. return a / b
  52. ZeroDivisionError: integer division or modulo by zero
  53. Process finished with exit code 1

完全不必再去log.debug(“a=%d” % a)了,个人感觉cgitb在线上环境不适合使用,适合在开发的过程中进行调试,非常的方便。
也许你会问,cgitb为什么会这么屌?能获取这么详细的出错信息?其实它的工作原理同它的使用方式一样的简单,它只是覆盖了默认的sys.excepthook函数,sys.excepthook是一个默认的全局异常拦截器,可以尝试去自行对它修改:

  1. def func(a, b):
  2. return a / b
  3. def my_exception_handler(exc_type, exc_value, exc_tb):
  4. print "i caught the exception:", exc_type
  5. while exc_tb:
  6. print "the line no:", exc_tb.tb_lineno
  7. print "the frame locals:", exc_tb.tb_frame.f_locals
  8. exc_tb = exc_tb.tb_next
  9. if __name__ == '__main__':
  10. import sys
  11. sys.excepthook = my_exception_handler
  12. func(1, 0)

输出:

  1. i caught the exception: <type 'exceptions.ZeroDivisionError'>
  2. the line no: 99
  3. the frame locals: {'my_exception_handler': <function my_exception_handler at 0x1069800c8>, '__builtins__': <module '__builtin__' (built-in)>, '__file__': '/Users/a6/Downloads/PycharmProjects/speiyou_di_my/hive/shuangshi_haibian/test_exception_process.py', '__package__': None, 'sys': <module 'sys' (built-in)>, 'func': <function func at 0x10697ac08>, '__name__': '__main__', '__doc__': None}
  4. the line no: 87
  5. the frame locals: {'a': 1, 'b': 0}

(4)使用logging模块来记录异常
在使用Java的时候,用log4j记录异常很简单,只要把Exception对象传递给log.error方法就可以了,但是在Python中就不行了,如果直接传递异常对象给log.error,那么只会在log里面出现一行异常对象的值。
在Python中正确的记录Log方式应该是这样的:

  1. logging.exception(ex)
  2. logging.error(ex, exc_info=1) # 指名输出栈踪迹, logging.exception的内部也是包了一层此做法
  3. logging.critical(ex, exc_info=1) # 更加严重的错误级别

具体示例如下:

  1. import logging
  2. import os
  3. from datetime import datetime
  4. class LoggingInstance():
  5. def __init__(self):
  6. self.log_dir="logging.log"
  7. def make_log(self):
  8. # 初始化日志文件
  9. if not os.path.exists(self.log_dir):
  10. os.mkdir(self.log_dir)
  11. logging.basicConfig(level=logging.INFO,
  12. filename="%s/log.%s" % (self.log_dir, datetime.now().strftime("%Y%m%d")), filemode='a',
  13. format='%(asctime)s [%(levelname)s] [%(filename)s] [%(funcName)s] [%(lineno)d] %(message)s')
  14. logger = logging.getLogger(__name__)
  15. self.target_date = datetime.strptime(datetime.now().strftime("%Y%m%d"), "%Y%m%d")
  16. logger.info("init success, target_date=%s", self.target_date)
  17. def func(self, a, b):
  18. return a / b
  19. if __name__ == '__main__':
  20. obj_logging_instance=LoggingInstance()
  21. obj_logging_instance.make_log()
  22. import sys
  23. import traceback
  24. try:
  25. obj_logging_instance.func(1, 0)
  26. except Exception as ex:
  27. print "print using exc logging"
  28. logging.info("分割线 @@@@@@@ target_date=%s ",datetime.now())
  29. logging.exception(ex) # 抛出异常方法一
  30. logging.info("分割线 @@@@@@@ target_date=%s ",datetime.now())
  31. logging.error(ex,exc_info=1) # 抛出异常方法二
  32. logging.info("分割线 @@@@@@@ target_date=%s ",datetime.now())
  33. logging.warning(ex,exc_info=1) # 抛出异常方法三
  34. logging.info("分割线 @@@@@@@ target_date=%s ",datetime.now())
  35. logging.fatal(ex,exc_info=1) # 抛出异常方法四
  36. logging.info("分割线 @@@@@@@ target_date=%s ",datetime.now())
  37. #traceback.print_exc() # 可以在输出窗口输出错误信息

输出日志文件logging.log文件内容如下:

  1. 2018-02-01 17:25:03,999 [INFO] [test_exception_process.py] [make_log] [156] init success, target_date=2018-02-01 00:00:00
  2. 2018-02-01 17:25:03,999 [INFO] [test_exception_process.py] [<module>] [169] 分割线 @@@@@@@ target_date=2018-02-01 17:25:03.999508
  3. 2018-02-01 17:25:03,999 [ERROR] [test_exception_process.py] [<module>] [170] integer division or modulo by zero
  4. Traceback (most recent call last):
  5. File "/Users/a6/test_exception_process.py", line 166, in <module>
  6. obj_logging_instance.func(1, 0)
  7. File "/Users/a6/test_exception_process.py", line 158, in func
  8. return a / b
  9. ZeroDivisionError: integer division or modulo by zero
  10. 2018-02-01 17:25:03,999 [INFO] [test_exception_process.py] [<module>] [171] 分割线 @@@@@@@ target_date=2018-02-01 17:25:03.999858
  11. 2018-02-01 17:25:03,999 [ERROR] [test_exception_process.py] [<module>] [172] integer division or modulo by zero
  12. Traceback (most recent call last):
  13. File "/Users/a6/test_exception_process.py", line 166, in <module>
  14. obj_logging_instance.func(1, 0)
  15. File "/Users/a6/test_exception_process.py", line 158, in func
  16. return a / b
  17. ZeroDivisionError: integer division or modulo by zero
  18. 2018-02-01 17:25:04,000 [INFO] [test_exception_process.py] [<module>] [173] 分割线 @@@@@@@ target_date=2018-02-01 17:25:04.000040
  19. 2018-02-01 17:25:04,000 [WARNING] [test_exception_process.py] [<module>] [174] integer division or modulo by zero
  20. Traceback (most recent call last):
  21. File "/Users/a6/test_exception_process.py", line 166, in <module>
  22. obj_logging_instance.func(1, 0)
  23. File "/Users/a6test_exception_process.py", line 158, in func
  24. return a / b
  25. ZeroDivisionError: integer division or modulo by zero
  26. 2018-02-01 17:25:04,000 [INFO] [test_exception_process.py] [<module>] [175] 分割线 @@@@@@@ target_date=2018-02-01 17:25:04.000206
  27. 2018-02-01 17:25:04,000 [CRITICAL] [test_exception_process.py] [<module>] [176] integer division or modulo by zero
  28. Traceback (most recent call last):
  29. File "/Users/a6/test_exception_process.py", line 166, in <module>
  30. obj_logging_instance.func(1, 0)
  31. File "/Users/a6/test_exception_process.py", line 158, in func
  32. return a / b
  33. ZeroDivisionError: integer division or modulo by zero
  34. 2018-02-01 17:25:04,000 [INFO] [test_exception_process.py] [<module>] [177] 分割线 @@@@@@@ target_date=2018-02-01 17:25:04.000367

参考网址:http://blog.csdn.net/wbiblem/article/details/73432506

发表评论

表情:
评论列表 (有 0 条评论,359人围观)

还没有评论,来说两句吧...

相关阅读