2023-01-12 更新
程序运行过程的监控非常重要,一旦出错,可以通过日志追溯问题原因。我们知道 print()
语句的作用,在开发时输出想要的信息,等开发后删除,简单有效,但是这种方法对于复杂的系统就力不从心了。所以,我们需要引入 Python 的日志模块 logging 。相较于 print()
,它的优势是,可以对消息进行级别过滤,并按特定格式输出到任意位置,包括终端、文本,甚至还能作为邮件内容发送到远程服务器。
time_str = time.strftime("%Y%m%d_%H%M%S", time.localtime())
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s %(levelname)s %(filename)s %(lineno)d %(message)s',
filename='%s.log' % time_str
)
1 结构
在一个项目中,直接用 logging.getLogger()
调出来的都是 root logger 。 logging.getLogger()
还接受一个名称参数,名称与 python 模块格式一样用 .
隔开,表示父子关系,例如 logging.getLogger('parent.child')
获取的 logger 是 root logger 下的 parent logger 下的 child logger 。
2 级别
可供选择的过滤级别有 DEBUG 、 INFO 、 WARNING 、 ERROR 、 CRITICAL 五种,默认 WARNING 。
3 附属 handler filter formatter
如果说程序主体是油井,那么 logger 是油罐, handler 是输油管道,filter 是输油管道起点处的滤网, formatter 是输油管道终点处的仪表。一个油罐可接多条输油管道;每条输油管道可设多张滤网,但只装一只仪表。
这些附属都会被子 logger 继承,也会被子 logger 里的同名对象覆盖。
常用的 handler 有两个,一是用于标准输出的 StreamHandler ,二是用于文件输出的 FileHandler 。
logger = logging.getLogger()
format = "%(asctime)s %(levelname)s %(filename)s %(lineno)d %(message)s"
sh = logging.StreamHandler()
sh.setFormatter(logging.Formatter(format))
sh.setLevel(logging.DEBUG)
fh = logging.FileHandler('python.log')
fh.setFormatter(logging.Formatter(format))
fh.setLevel(logging.INFO)
logger.addHandler(sh)
logger.addHandler(fh)
4 示例
官网给出的示例运行报错 ConfigParser.NoSectionError: No section:'formatter'
原因竟然是, fileConfig 只接受绝对路径,不接受相对路径。由于 log 文件名通常自带时间,无法写死。勉强的解决方案是,放弃程序运行之初的少量 log ,直到生成具体时间创建 log 文件,再将剩余的 log 写入文件。
5 异常
logging.exception('...')
。换行问题就别管了。