异常处理并记录日志(多线程情况)
2020/07/16 -
引言
正常情况下,经常会让应用后台运行,这种情况下,不可避免地就会出现异常,那么出现异常之后,同时还需要对异常进行定位,这样能够保证后续的日志查看的时候更方便定位异常,从而修改代码。
异常的传播途径
首先,异常在发生之后,如果不及时在发声位置进行处理,那么他会逐层往上传播,直到有某层能够处理这部分异常的时候,就不再往上传播,当然可以手动引起异常,raise。
这里引发的问题就是,应该再哪一层进行异常的处理呢?我记得我之前的时候看过一篇文章,就是说在最接近异常的地方将异常处理完毕是最好的,这样能够保证上层的逻辑越清晰。
多线程的异常处理
我这里遇到的问题就是,因为需要多线程工作,虽然出现了异常,这可能是某个参数导致的,但是我希望其他的线程依然能进行工作。当前使用的是线程池的形式(每次一个程序结束,就启动这个线程而不是向队列一样取数据),那么就需要在每个函数启动的地方添加异常处理。
但是正常情况下,线程池的工作方式,在异常出现了之后,他推出,并不影响其他的线程工作。他们只是从一个可迭代的列表中进行进行工作而已。
但是我这里的出现的问题好像是,这个程序没有完整的推出,最后导致还有很多和mysql的连接。这个我就有带你不是很理解了。而且,同时,这个定时任务的进程也没有推出。。。最后累积了好几个线程。
然后我测试了一些方案,也没有复现出来这种问题。当时是因为使用了django的定时任务,然后他最后在我定义的logging日志文件中还是打印出来了异常信息。
但是我找不出为什么会发生这种异常的原因。
反正情况就是,因为需要连接数据库,然后这个数据库的连接最后就没有跟mysql断开,可以通过数据库的查询命令看到。
然后最后这个程序也没有退出。
注意这里的几个特点:
1)程序由django的定时任务驱动
2)即使报错,其实也不影响整个程序的分析,只是这个线程挂了而已,不该影响整个程序。
3)程序中存在某种开启的Tcp连接这种东西,虽然不是我显式开启的。
异常处理方式
异常处理的方式呢,我感觉,只要你觉的这个程序可能是异常,那就就一定要打印一些信息,以前的时候,图方便,都直接pass了,而且还是全异常都直接关闭了,这就非常尴尬了,有时候还会造成非常出乎意料的结果,就是报错了自己也不知道。
1)利用traceback库
import traceback
try:
print(1/0)
except Exception:
traceback.print_exc()
traceback.print_exc() #直接打印异常
traceback.format_exc()#返回字符串
traceback.print_exc(file=open(‘log.txt’,’a+’))
2)利用logging的方式 python logging日志捕获代码异常(traceback)
更好的处理方式
对于异常代码的处理形式呢,更好的方式其实应该是使用装饰器的形式(这种情况适用于很多函数可能都是同样的异常处理机制)。
def exception_handler(func):
def inner_function(*args, **kwargs):
try:
func(*args, **kwargs)
except TypeError:
print(f"{func.__name__} only takes numbers as the argument")
return inner_function
@exception_handler
def area_square(length):
print(length * length)
@exception_handler
def area_circle(radius):
print(3.14 * radius * radius)
@exception_handler
def area_rectangle(length, breadth):
print(length * breadth)
area_square(2)
area_circle(2)
area_rectangle(2, 4)
area_square("some_str")
area_circle("some_other_str")
area_rectangle("some_other_rectangle")
代码来自(handling-exceptions-in-python-a-cleaner-way-using-decorators)
然后我又去翻了翻ss的代码,在我印象中他就这么个逻辑来处理异常的。打开了看了,果然,而且,他的处理方式更加复杂, 应该是能应付更多的异常形式。。
问题
我这里发生了一个问题,就是不知道为什么, 他没有报错呢?就是我开的一个线程,他有错,但是没有报错,这个东西没有停止。。。我非常纳闷。。
找到原因了,这个程序也是我在调试django的时候出现的, 整个程序是这么回事,每次docker启动之后,就首先先进行一次定时任务,不管到没到时间,这个程序是在django启动之前启动的,利用&来后台运行,最后的确打印了错误,是在整个这个python结束了之后打印,而且是打印在了docker的日志中。。。
就感觉挺纳闷的。。。