- Preface 前言
- 第1章 引论
- 第2章 编程惯用法
- 第3章 基础语法
- 建议19:有节制地使用 from…import 语句
- 建议20:优先使用 absolute import 来导入模块
- 建议21:i+=1 不等于 ++i
- 建议22:使用 with 自动关闭资源
- 建议23:使用 else 子句简化循环(异常处理)
- 建议24:遵循异常处理的几点基本原则
- 建议25:避免 finally 中可能发生的陷阱
- 建议26:深入理解 None 正确判断对象是否为空
- 建议27:连接字符串应优先使用 join 而不是 +
- 建议28:格式化字符串时尽量使用 .format 方式而不是 %
- 建议29:区别对待可变对象和不可变对象
- 建议30:[]、() 和 {}:一致的容器初始化形式
- 建议31:记住函数传参既不是传值也不是传引用
- 建议32:警惕默认参数潜在的问题
- 建议33:慎用变长参数
- 建议34:深入理解 str() 和 repr() 的区别
- 建议35:分清 staticmethod 和 classmethod 的适用场景
- 第4章 库
- 建议36:掌握字符串的基本用法
- 建议37:按需选择 sort() 或者 sorted()
- 建议38:使用 copy 模块深拷贝对象
- 建议39:使用 Counter 进行计数统计
- 建议40:深入掌握 ConfigParser
- 建议41:使用 argparse 处理命令行参数
- 建议42:使用 pandas 处理大型 CSV 文件
- 建议43:一般情况使用 ElementTree 解析 XML
- 建议44:理解模块 pickle 优劣
- 建议45:序列化的另一个不错的选择 JSON
- 建议46:使用 traceback 获取栈信息
- 建议47:使用 logging 记录日志信息
- 建议48:使用 threading 模块编写多线程程序
- 建议49:使用 Queue 使多线程编程更安全
- 第5章 设计模式
- 第6章 内部机制
- 建议54:理解 built-in objects
- 建议55:init() 不是构造方法
- 建议56:理解名字查找机制
- 建议57:为什么需要 self 参数
- 建议58:理解 MRO 与多继承
- 建议59:理解描述符机制
- 建议60:区别 getattr() 和 getattribute() 方法
- 建议61:使用更为安全的 property
- 建议62:掌握 metaclass
- 建议63:熟悉 Python 对象协议
- 建议64:利用操作符重载实现中缀语法
- 建议65:熟悉 Python 的迭代器协议
- 建议66:熟悉 Python 的生成器
- 建议67:基于生成器的协程及 greenlet
- 建议68:理解 GIL 的局限性
- 建议69:对象的管理与垃圾回收
- 第7章 使用工具辅助项目开发
- 第8章 性能剖析与优化
建议47:使用 logging 记录日志信息
仅仅将栈信息输出到控制台是远远不够的,更为常见的是使用日志保存程序运行过程中的相关信息,如运行时间、描述信息以及错误或者异常发生时候的特定上下文信息。Python中自带的logging模块提供了日志功能,它将logger的level分为5个级别(如表4-3所示),可以通过Logger.setLevel(lvl)来设置,其中DEBUG为最低级别,CRITICAL为最高级别,默认的级别为WARNING。
表4-3 日志级别
logging lib包含以下4个主要对象:
1)logger。logger是程序信息输出的接口,它分散在不同的代码中,使得程序可以在运行的时候记录相应的信息,并根据设置的日志级别或filter来决定哪些信息需要输出,并将这些信息分发到其关联的handler。常用的方法有Logger.setLevel()、Logger.addHandler()、Logger.removeHandler()、Logger.addFilter()、Logger.debug()、Logger.info()、Logger.warning()、Logger.error()、etLogger()等。
2)Handler。Handler用来处理信息的输出,可以将信息输出到控制台、文件或者网络。可以通过Logger.addHandler()来给logger对象添加handler,常用的handler有StreamHandler和FileHandler类。StreamHandler发送错误信息到流,而FileHandler类用于向文件输出日志信息,这两个handler定义在logging的核心模块中。其他的handler定义在logging.handles模块中,如HTTPHandler、SocketHandler。
3)Formatter。决定log信息的格式,格式使用类似于%(< dictionary key >)s的形式来定义,如'%(asctime)s - %(levelname)s - %(message)s',支持的key可以在Python自带的文档LogRecord attributes中查看。
4)Filter。用来决定哪些信息需要输出。可以被handler和logger使用,支持层次关系,比如,如果设置了filter名称为A.B的logger,则该logger和其子logger的信息会被输出,如A.B、A.B.C.
logging.basicConfig([**kwargs])提供对日志系统的基本配置,默认使用StreamHandler和Formatter并添加到root logger,该方法自Python2.4开始可以接受字典参数,支持的字典参数如表4-4所示。
表4-4 字典参数格式类型
我们通过修改上一节的例子来看如何结合traceback和logging,记录程序运行过程中的异常。
import traceback import sys import logging gList = ['a','b','c','d','e','f','g'] logging.basicConfig( # 配置日志的输出方式及格式 level=logging.DEBUG, filename='log.txt', filemode='w', format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s', ) def f(): gList[5] logging.info('[INFO]:calling method g() in f()')# 记录正常的信息 return g() def g(): logging.info('[INFO]:calling method h() in g()') return h() def h(): logging.info('[INFO]:Delete element in gList in h()') del gList[2] logging.info('[INFO]:calling method i() in h()') return i() def i(): logging.info('[INFO]:Append element i to gList in i()') gList.append('i') print gList[7] if __name__ == '__main__': logging.debug('Information during calling f():') try: f() except IndexError as ex: print "Sorry,Exception occured,you accessed an element out of range" #traceback.print_exc() ty,tv,tb = sys.exc_info() logging.error("[ERROR]:Sorry,Exception occured,you accessed an element out of range")# 记录异常错误信息 logging.critical('object info:%s' %ex) logging.critical('Error Type:{0},Error Information:{1}'.format(ty, tv)) # 记录异常的类型和对应的值 logging.critical(''.join(traceback.format_tb(tb)))# 记录具体的trace 信息 sys.exit(1)
修改程序后在控制台上对用户仅显示错误提示信息“Sorry,Exception occured,you accessed an element out of range”,而开发人员如果需要debug可以在日志文件中找到具体运行过程中的信息。
# 为了节省篇幅仅显示部分日志 2013-06-26 12:05:18,923 traceexample.py[line:41] CRITICAL object info:list index out of range 2013-06-26 12:05:18,923 traceexample.py[line:42] CRITICAL Error Type:<type 'exceptions.IndexError'>,Error Information:list index out of range 2013-06-26 12:05:18,924 traceexample.py[line:43] CRITICAL File "traceexample.py", line 35, in <module> f() File "traceexample.py", line 15, in f return g() File "traceexample.py", line 19, in g return h() File "traceexample.py", line 25, in h return i() File "traceexample.py", line 30, in i print gList[7]
上面的代码中控制运行输出到console上用的是print(),但这种方法比较原始,logging模块提供了能够同时控制输出到console和文件的方法。下面的例子中通过添加StreamHandler并设置日志级别为logging.ERROR,可以在控制台上输出错误信息。
console = logging.StreamHandler() console.setLevel(logging.ERROR) formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s') console.setFormatter(formatter) logging.getLogger('').addHandler(console)
为了使Logging使用更为简单可控,logging支持loggin.config进行配置,支持dictConfig和fileConfig两种形式,其中fileConfig是基于configparser()函数进行解析,必须包含的内容为[loggers]、[handlers]和[formatters]。具体例子示意如下:
[loggers] keys=root [logger_root] level=DEBUG handlers=hand01 [handlers] keys=hand01 [handler_hand01] class=StreamHandler level=INFO formatter=form01 args=(sys.stderr,) [formatters] keys=form01 [formatter_form01] format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s datefmt=%a, %d %b %Y %H:%M:%S
最后关于logging的使用,提以下几点建议:
1)尽量为logging取一个名字而不是采用默认,这样当在不同的模块中使用的时候,其他模块只需要使用以下代码就可以方便地使用同一个logger,因为它本质上符合单例模式。
import logging logging.basicConfig(level=logging.DEBUG) logger = logging.getLogger( __name__ )
2)为了方便地找出问题所在,logging的名字建议以模块或者class来命名。Logging名称遵循按“.”划分的继承规则,根是root logger,logger a.b的父logger对象为a。
3)Logging只是线程安全的,不支持多进程写入同一个日子文件,因此对于多个进程,需要配置不同的日志文件。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论