原因
实际工作中遇到的问题,使用gevent
协程来提高性能,但发现携程中的异常堆栈信息抓取不到
第一次尝试
try :
result = { }
gevent. joinall( [
gevent. spawn( self. desktop_statistics_info, result) ,
gevent. spawn( self. user_statistics_info, result) ,
gevent. spawn( self. client_statistics_info, result) ,
gevent. spawn( self. iaas_statistics_info, result) ,
gevent. spawn( self. desktop_attach_user_statistics_info, result)
] )
except Exception as e:
logger. error( traceback. format_exc( ) )
问题:使用try\exception
来捕获,然后使用traceback
模块来记录错误堆栈信息,发现在我的项目中gevent.joinall
不会将异常抛出来,查阅资料发现,携程会在内部消化整个异常信息,不会对外暴露,提示我们可以在外部判断exception
抓取
第二次尝试
tasks = gevent. joinall( [
gevent. spawn( self. desktop_statistics_info, result) ,
gevent. spawn( self. user_statistics_info, result) ,
gevent. spawn( self. client_statistics_info, result) ,
gevent. spawn( self. iaas_statistics_info, result) ,
gevent. spawn( self. desktop_attach_user_statistics_info, result)
] )
for task in tasks:
if task. exception:
logger. error( task. exception)
问题:task.exception
仅包含了错误的内容,并未包含整个错误的堆栈信息,因此该日志记录对于解决bug
没有太大帮助,所以我需要找到整个异常堆栈信息,并记录日志。通过对task
对象打断点,发现task
内部包含了exc_info
这个错误对象,其实就是sys.exc_info()
,会记录整个异常堆栈,该对象不做分析,不了解的自行查询。接下来的工作就是如何从sys.exc_info()
提取到有效的堆栈信息,并记录日志。在网上找了一圈没找到,只能自行摸索。
第三次尝试
result = { }
tasks = gevent. joinall( [
gevent. spawn( self. desktop_statistics_info, result) ,
gevent. spawn( self. user_statistics_info, result) ,
gevent. spawn( self. client_statistics_info, result) ,
gevent. spawn( self. iaas_statistics_info, result) ,
gevent. spawn( self. desktop_attach_user_statistics_info, result)
] )
for task in tasks:
if task. exception:
with open ( "xxxxx/xxxxx/xxx/log" , "a" ) as log:
traceback. print_exception( * task. exc_info, file = log)
首先查看了traceback
源码,发现有一个print_exception
的方法,支持传递三个参数etype, value, tb
,正好是sys.exc_info()
返回的元组,直接掉用traceback.print_exception(*task.exc_info)
,发现完美的在控制台输出了错误的堆栈信息,但是print_exception
以看就是只能输出在控制台,我们要如何输出到日志文件中呢? 在次进入到print_exception
方法中,发现该方法还支持一个file
的参数,开始以为直接传递一个文件路径即可,看到报错信息后发现file
接受的是一个文件对象,即:with open("xxx", "a") as f:
,此处为何使用a
打开文件,每个人需求不一样,自己思考。传递项目日志文件对象后发现,错误堆栈信息被完美的记录进去了,至此需求已经得到解决。 由于项目的日志文件路径是动态生成的,所以此处直接将路径写死,不便于后期的维护,所以只能硬着头皮往下继续看print_exception
内部是如何将错误堆栈信息获取以及写入指定的文件的,只要找到exc_info
怎么解析的,写入文件不就so easy
。
最后一次尝试
def exc_info_logger ( exc_type, exc_value, exc_tb, errmsg= "" ) :
"""主要用来记录gevent错误堆栈信息"""
err_info = errmsg + "\n"
for line in traceback. TracebackException( exc_type, exc_value, exc_tb) . format ( chain= True ) :
err_info += line
logger. error( err_info)
result = { }
tasks = gevent. joinall( [
gevent. spawn( self. desktop_statistics_info, result) ,
gevent. spawn( self. user_statistics_info, result) ,
gevent. spawn( self. client_statistics_info, result) ,
gevent. spawn( self. iaas_statistics_info, result) ,
gevent. spawn( self. desktop_attach_user_statistics_info, result)
] )
for task in tasks:
if task. exception:
exc_info_logger( * task. exc_info, errmsg= "Data statistics interface abnormal" )
通过看print_exception
源码,发现其解析exc_info
主要用到了TracebackException
这个类,这就简单了,我们直接导入该TracebackException
类,来解析我们从gevent
中获取到的exc_info
即可,然后将解析的错误堆栈信息写入我们的日志文件就完美了。