python logging best practice

作者: 程序员赤小豆_gzh同名 | 来源:发表于2017-12-16 14:20 被阅读112次

    在软件开发过程中, 写日志是非常好的习惯. 一年前我不知道日志都有哪些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.bazfoo.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的效果的.

    总结

    有了以上的工具和方法, 日常开发就可以愉快地打日志了. 写日志的时候不妨问问自己如果出问题了, 最想看到什么, 些什么东西才能让大家一眼就能明白发生了什么问题, 应该怎么解决. 😁

    reference

    相关文章

      网友评论

        本文标题:python logging best practice

        本文链接:https://www.haomeiwen.com/subject/mtwdwxtx.html