上一节(8.1测试)|下一节(8.3调试)8.2日志记录本节简单介绍日志记录模块。日志记录模块日志记录模块是用于记录诊断信息的Python标准库模块。logging模块非常庞大,功能复杂。我们将展示一个简单的例子来说明它的用处。重温异常在这个练习中,我们创建了一个parse()函数,如下所示:strip()ifnotline:continuetry:records.append(split(line,types,names,delimiter))exceptValueErrorase:print("Couldn'tparse:",line)print("Reason:",e)返回记录请看try-except语句,except块中我们应该做什么?是否应该打印一条警告信息?尝试:records.append(split(line,types,names,delimiter))exceptValueErrorase:print("Couldn'tparse:",line)print("Reason:",e)或默默地忽略警告消息?try:records.append(split(line,types,names,delimiter))exceptValueErrorase:pass两种方式都不令人满意,通常,我们需要两种方式(用户可选)。使用日志记录模块可以解决这个问题:#fileparse.pyimportlogginglog=logging.getLogger(__name__)defparse(f,types=None,names=None,delimiter=None):...try:records.append(split(line,types,names,delimiter))exceptValueErrorase:log.warning("Couldn'tparse:%s",line)log.debug("Reason:%s",e)修改代码,使程序可以遇到有问题的时候发出警告信息,或者特殊的Logger对象。Logger对象是使用logging.getLogger(__name__)创建的。日志记录基础创建一个记录器对象。log=logging.getLogger(name)#name是一个字符串日志消息:log.critical(message[,args])log.error(message[,args])log.warning(message[,args])log.info(message[,args])log.debug(message[,args])不同的方法代表不同的严重程度。所有方法都会创建格式化的日志消息。args与%运算符一起使用来创建消息。logmsg=message%args#写入日志日志配置configuration:#main.py...if__name__=='__main__':importlogginglogging.basicConfig(filename='app.log',#日志输出文件level=logging.INFO,#Outputlevel)一般情况下,logging的配置在程序启动时是一次性的(注解:程序启动后不可重新配置)。此配置与日志记录调用是分开的。请注意,日志可以任意配置。您可以调整日志配置的任何方面:例如输出文件、级别、消息格式等,而不必担心影响使用日志模块的代码。Exercise练习8.2:向模块添加日志记录在fileparse.py中,有一些与错误输入引起的异常相关的错误处理。如下所示:#fileparse.pyimportcsvdefparse_csv(lines,select=None,types=None,has_headers=True,delimiter=',',silence_errors=False):'''将一个CSV文件解析成一个记录列表,类型为转换。'''ifselectandnothas_headers:raiseRuntimeError('selectrequirescolumnheaders')rows=csv.reader(lines,delimiter=delimiter)#读取文件头(如果有的话)headers=next(rows)ifhas_headerselse[]#如果选择了特定的列,则为过滤创建索引并设置输出列ifselect:indices=[headers.index(colname)forcolnameinselect]headers=selectrecords=[]forrowno,rowinenumerate(rows,1):ifnotrow:#跳过没有数据的行continue#如果选择了特定的列索引,将它们挑出来ifselect:row=[row[index]forindexinindices]#对行应用类型转换iftypes:尝试:row=[func(val)forfunc,valinzip(types,row)]除了ValueErrorase:ifnotsilence_errors:print(f"Row{rowno}:Couldn'tconvert{row}")print(f"Row{rowno}:Reason{e}")continue#创建字典或元组ifheaders:record=dict(zip(headers,row))else:record=tuple(row)records.append(record)return记录请注意,发出诊断消息的打印语句相对简单,可以用日志记录操作代替。请像下面这样修改代码:#fileparse.pyimportcsvimportlogginglog=logging.getLogger(__name__)defparse_csv(lines,select=None,types=None,has_headers=True,delimiter=',',silence_errors=False):'''将CSV文件解析为具有类型转换的记录列表。'''ifselectandnothas_headers:raiseRuntimeError('selectrequirescolumnheaders')rows=csv.reader(lines,delimiter=delimiter)#读取文件头(如果有的话)headers=next(rows)ifhas_headerselse[]#如果选择了特定的列,则为过滤创建索引并设置输出列ifselect:indices=[headers.index(colname)forcolnameinselect]headers=selectrecords=[]forrowno,rowinenumerate(rows,1):ifnotrow:#跳过没有数据的行continue#如果选择了特定的列索引,将它们挑出来ifselect:row=[row[index]forindexinindices]#Apply类型转换到行iftypes:try:row=[func(val)forfunc,valinzip(types,row)]exceptValueErrorase:ifnotsilence_errors:log.warning("Row%d:Couldn'tconvert%s",rowno,row)log.debug("Row%d:Reason%s",rowno,e)continue#创建字典或元组ifheaders:record=dict(zip(headers,row))else:record=tuple(row)records.append(record)returnrecords一旦修改,尝试在错误的数据上使用这些代码:>>>importreport>>>a=report.read_portfolio('Data/missing.csv')第4行:错误行:['MSFT','','51.23']第7行:错误行:['IBM','','70.44']>>>如果您什么都不做,只会收到上面的日志消息警告级别。输出看起来像简单的打印语句。但是,如果您配置日志记录模块,您将获得有关日志记录级别、模块等的附加信息。请按照以下步骤查看:>>>importlogging>>>logging.basicConfig()>>>a=report.read_portfolio('Data/missing.csv')WARNING:fileparse:Row4:Badrow:['MSFT','','51.23']WARNING:fileparse:Row7:Badrow:['IBM','','70.44']>>>你会发现看不到log.debug()运行输出.请按照以下步骤修改日志级别:>>>logging.getLogger('fileparse').level=logging.DEBUG>>>a=report.read_portfolio('Data/missing.csv')WARNING:fileparse:Row4:错误行:['MSFT','','51.23']DEBUG:fileparse:Row4:Reason:invalidliteralforint()withbase10:''WARNING:fileparse:Row7:Badrow:['IBM','','70.44']DEBUG:fileparse:Row7:Reason:invalidliteralforint()withbase10:''>>>仅保留关键级别的日志消息并关闭其他级别的日志消息。>>>logging.getLogger('fileparse').level=logging.CRITICAL>>>a=report.read_portfolio('Data/missing.csv')>>>练习8.3:向程序添加日志application,你需要一些机制来实现主模块中的日志记录。一种方法使用如下代码:#此文件设置日志记录模块的基本配置。#更改此处的设置以根据需要调整日志记录输出。importlogginglogging.basicConfig(filename='app.log',#日志名称file(省略使用stderr)filemode='w',#文件模式(使用'a'追加)level=logging.WARNING,#Logginglevel(DEBUG,INFO,WARNING,ERROR,orCRITICAL))同样,你需要将日志记录配置代码放在程序启动步骤中。比如放在report.py程序的什么地方?目录|上一节(8.1测试)|NextSection(8.3Debugging)注:完整翻译见https://github.com/codists/practical-python-zh
