在软件开发过程中, 写日志是非常好的习惯. 一年前我不知道日志都有哪些logging level, 半年前我的日志写的一塌糊涂, 关键的地方不知道写个日志, 出错了, 日志的内容也是看得一脸懵逼, 这是个惨痛的经历. 那就总结一下打日志的best practice.
日志的重要性
首先是能够定位到问题, 特别是web开发, 操作到某一步, 突然程序崩溃了, 看一眼日志就能找到是在哪个函数执行的时候崩溃的, 问题能够立马定位. 如果这个软件是你自己开发的, 日志写的粗略一些, 自己也能通过蛛丝马迹摸索到原因, 但如果是好几个开发人员一起合作的话, 别人未必能够熟悉你写的逻辑, 因此在关键节点打上让刚接手的人都能看懂的日志就高效很多啦.
使用python logging 标准库
这个标准库提供了灵活的打日志方法和许多class, 使用标准库最主要的目的就是能够在python 模块的角度打日志, 能够让自己的项目与第三方的项目的日志灵活地进行整合.
在logging的标准库中有如下四个类:
- Loggers 类, 在代码中打日志的主要接口(最常用的)
- Handlers 类, 起到分发日志事件到特定的目的地
- Filters 类, 为Logger 和Handler类提供筛选的功能
- Formatters 类, 按照设置的样式去格式化日志输出形式
写一个最简单的示例, 来理解一下
import logging
logging.getLogger(__name__)
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s pid:%(process)d %(levelname)s %(module)s - %(message)s')
logging.debug("debug logging")
logging.info("info logging")
在上面的代码中, getLogger(name)
最好传入带有层级关系的参数(如python的模块信息), foo.bar.baz
是foo.bar
的子类, 便于logger更好的组织日志信息. python中 __name__
携带模块信息, 一般都会把__name__
作为参数传给getLogger()
.
basicConfig
提供了快速的setup一个logger的方法, 自动创建了handler和formatter, 一般推荐使用这个方式, 当然如果想要让日志有更多自定义的输出(打印颜色等), 则需要去设置formatter, 可以自己去写formatter, 当然社区里也有很多开源的可以使用.
以上的logging输出如下
2017-12-15 22:04:51,248 pid:18721 DEBUG test_only - debug logging
2017-12-15 22:04:51,248 pid:18721 INFO test_only - info logging
basicConfig
的format参数规定了输出的形式, 需要什么信息, 从LogRecord Attribute 里面去找就可以了.
软实力之如何写日志
基本功
logging的脚手架有了, 就可以愉快地写日志了.
python的日志级别从低到高分为debug, info, warning, error, critical. 一般日志打为INFO级别, 因为线上的环境debug级别很少打开, 关键的地方还是要打INFO级别的日志.
- DEBUG
大部分情况下, 你不需要写的非常的详细, 比如数据量大的, 比如触发频率高的(for loop里面打印的信息), 会造成刷屏的情况, 让你找不到想看的信息. 这种类型的信息可以打到DEBUG级别, 只有在你调试的时候才会打开来看. - INFO
日常事务都是放到INFO级别, 比如用户登录成功了, 页面的跳转, 操作的不同阶段等. 我一般会在执行某个功能的时候打一个日志, 执行完毕之后再写一下成功执行的信息, 如果是写入文件的话, 不妨打印一下路径, 如果是获取参数值之类的, 也可以在日志中提获取到什么值. 尽量让日志能够详细一些. - WARNING
WARNING级别的日志用的比较少, 这个级别的日志通常反映出是个值得注意的问题, 但并不构成error. - ERROR
ERROR级别使用的也非常普遍, 比如打开了一个不存在的文件, 需要提示error, 比如文件损坏, 试图去读取信息, 也会返回error信息.
平时的开发中很少接触到CRITICAL级别的日志, 触发这个级别的事务都是及其严重的, 比如内存不够了, 机器宕了等.
traceback
发生问题的时候, 光有日志, 没有traceback还是会对错误很抽象, 如果有上下文的错误日志, 就能够更加准确的了解问题.
try:
open('/path/to/does/not/exist', 'r')
except Exception as ex:
logger.error('Failed to open file', exc_info=True)
如上所示, 加上exc_info=True
就可以把traceback信息带到logger中来.
ERROR:__main__:Failed to open file
Traceback (most recent call last):
File "example.py", line 6, in <module>
open('/path/to/does/not/exist', 'rb')
IOError: [Errno 2] No such file or directory: '/path/to/does/not/exist'
除了使用logger.error('Failed to open file', exc_info=True), logger.exception(msg, *args)也是可以起到打印traceback的效果的.
总结
有了以上的工具和方法, 日常开发就可以愉快地打日志了. 写日志的时候不妨问问自己如果出问题了, 最想看到什么, 些什么东西才能让大家一眼就能明白发生了什么问题, 应该怎么解决. 😁
网友评论