zoukankan      html  css  js  c++  java
  • 正确使用Python logging

     这篇文章主要参考: http://victorlin.me/posts/2012/08/26/good-logging-practice-in-python


    ==========================
    应用程序的logging的做法
    ==========================
    1. 对于主模块, 直接对root logger做logger输出设置. 这样所有模块都会按照root logger的设置进行log 输出.
    2. 所有模块, 包括主模块, 都应使用logging.getLogger(__name__)来获得logger实例, 使用getLogger(__name__)获得的logger, 输出的日志能体现出模块的命名空间. 主模块getLogger()调用应放在logging初始化之后.
    3. 对于非主模块,不应做logger的初始化配置, 也不应该在模块级上调用getLogger(), 而应该在function中或者class中来调用getLogger().
    理由是: 最主要的原因是, log配置应该是主模块的特权. 还有一个理由是, 我们的主module往往是先import非主module, 然后再用logging.config.fileConfig()做初始化, 而fileConfig()缺省会disable已存在的所有logger, 也就是说非主模块级别的那些logger都被禁掉了, 也就无法再用来记日志.
    4. 在主模块和非主模块推荐, 都应该使用logger.info()和logger.debug()来记日志, 不推荐使用logging.info()和logging.debug()这样的方法, 后者是用的是root logger, 所有模块都用root, 就无法区分日志是由哪个模块产生的了.
    5. 推荐使用 cloghandler.py 的 ConcurrentRotatingFileHandler.
       而不是FileHandler 和RotatingFileHandler 和TimedRotatingFileHandler .  
       理由是 FileHandler, 单文件日志缺点比较多.  
       而 RotatingFileHandler的问题是, 在windows下当文件大小到达max时, logging操作会报错, 新log有时候就记不进去了. 另外如果多个程序往同一个log文件写, RotatingFileHandler也会报错的, cloghandler.py没这个问题.

       而TimedRotatingFileHandler的问题是, 在windows下, 如果log文件在23:59正在被写入, logging操作也会报错, 无法进行log文件重命名, 新log有时候就记不进去了. 当然这个几率要比RotatingFileHandler失败几率小. 

      参考 http://pydoc.net/Python/cloud/2.3.9/cloud.util.cloghandler.cloghandler/
    6. 长log的format, 我使用的是,
       说明: 前面加上####, 这样log parser容易分开不同的log记录. 分隔符使用|而不是逗号, 因为时间串中会包含个逗号的.
       formatter = logging.Formatter(fmt='####%(asctime)s|pid=%(process)d|tid=%(thread)d|%(name)s|%(levelname)s|%(message)s')
       短log的format, 我使用的是,
       shortFormatter = logging.Formatter(fmt='####%(asctime)s|%(name)s|%(levelname)s|%(message)s', datefmt="%H:%M:%S")
    7. 关于应用程序如何合并第3方类库的log输出, 比如合并SqlAlchemy的日志.
       基本不用特别在意, 只要我们的程序设置了root logger, 应用程序和SqlAlchemy的日志自然会合并输出在root logger的handler中.


       
    ==========================   
    类库的logging的做法
    ==========================
    1.类库logging的使用, 其实和application的非主模块类似.
    2.在类库的top level库中, 最好为logger加上NullHandler, 这样即使应用程序根本没做logging配置, 也不会出现未配置logging的警告信息.



    ==========================
    几个有关的函数
    ==========================
    rootLogger = logging.getLogger() #getLogger()不带参数, 得到的即是rootLogger
    logging.basicConfig(level=logging.DEBUG) #是一个很顺手的logging配置方案, 输出为stderr, 非常适合在demo和调试程序

    logging.config.dictConfig(dict_LOG_CONFIG) #使用dict来初始化logger

    logging.config.fileConfig(ini_file) #使用ini文件来初始化logger




    ==========================
    选择合适的logging level
    ==========================
    这里误区也比较多, 很多程序员喜欢滥用error级别. 可最佳日志实践中的介绍,
    http://www.bitstech.net/2014/01/07/log-best-practice/
    DEBUG : 开发人员debug用, 比如记录详尽的业务动作, 比如SQL
    INFO  : 记录一些关键的动作和数据.  
    WARN  : 用于那些对业务功能已有影响, 但不太严重的情形
    ERROR : 仅用于记录那些已影响业务功能的Error或Exception
    FATAL : (同CRITICAL)仅用于影响到程序完全不能工作的情况, 比如非法退出
    如果还不容易区分, 换另一个角度, WARNING和ERROR级别的问题, 都是需要及时处理.



    ==========================   
    dict 配置的写法
    ==========================   
      1. 具体 ini 配置文件如何写, 参考<python日志不完全指南> http://cjs.linuxapp.org/?p=116  
      2. dict 配置, 后面有个sample.    
    dict_LOG_CONFIG ={
        #版本总是1
        'version': 1,  
        #     
        'disable_existing_loggers': False,
        'formatters': {
            'standard': {
                'format': '%(asctime)s [%(levelname)s] %(name)s: %(message)s'
            },
        },
        'handlers': {
            'default': {
                'level':'INFO',    
                'class':'logging.StreamHandler',
            },  
        },
        'loggers': {
            
            #加一个root logger        
            '': {                  
                'handlers': ['default'],        
                'level': 'INFO',  
                'propagate': True  
            },
            
            #加其他logger
            'django.request': {
                'handlers': ['default'],
                'level': 'WARN',  
                'propagate': False
            },
        }
    }
       
       
       
    ==========================   
    ini 配置的写法
    ==========================   
     参考<python日志不完全指南> http://cjs.linuxapp.org/?p=116   
    # 定义logger模块,root是必需的,其它的是自定义。
    [loggers]
    keys=root, web.debug, web.info, web.error

    # 定义格式化输出
    [formatters]
    keys=simpleFormatter, webSimpleFormatter

    # 定义handler
    [handlers]
    keys=consoleHandler,rotateFileHandler, webDebugRotateFileHandler, webInfoRotateFileHandler, webErrorRotateFileHandler, webSMTPHandler

    #--------------------------------------------------
    # 实现上面定义的logger模块,必需是[logger_xxxx]这样的形式
    #--------------------------------------------------
    # [logger_xxxx] logger_模块名称
    # level     级别,级别有DEBUG、INFO、WARNING、ERROR、CRITICAL
    # handlers  处理类,可以有多个,用逗号分开
    # qualname  logger名称,应用程序通过 logging.getLogger获取。对于不能获取的名称,则记录到root模块。
    # propagate 是否继承父类的log信息,0:否 1:是
    [logger_root]
    level=DEBUG
    handlers=consoleHandler,rotateFileHandler

    [logger_web.debug]
    level=DEBUG
    handlers=consoleHandler,webDebugRotateFileHandler
    qualname=web.debug
    propagate=0

    [logger_web.info]
    level=INFO
    handlers=consoleHandler,webInfoRotateFileHandler
    qualname=web.info
    propagate=0

    [logger_web.error]
    level=ERROR
    handlers=consoleHandler,webErrorRotateFileHandler,webSMTPHandler
    qualname=web.error
    propagate=0

    #--------------------------------------------------
    # 日志格式
    #--------------------------------------------------
    # %(asctime)s       年-月-日 时-分-秒,毫秒 2013-04-26 20:10:43,745
    # %(filename)s      文件名,不含目录
    # %(pathname)s      目录名,完整路径
    # %(funcName)s      函数名
    # %(levelname)s     级别名
    # %(lineno)d        行号
    # %(module)s        模块名
    # %(message)s       消息体
    # %(name)s          日志模块名
    # %(process)d       进程id
    # %(processName)s   进程名
    # %(thread)d        线程id
    # %(threadName)s    线程名
    [formatter_simpleFormatter]
    format=%(asctime)s|%(name)s|%(threadName)s|%(levelname)s > %(message)s

    [formatter_webSimpleFormatter]
    format=%(asctime)s %(clientip)s %(levelname)s > %(message)s

    #--------------------------------------------------
    # handler
    #--------------------------------------------------
    # [handler_xxxx]
    # class handler类名
    # level 日志级别
    # formatter,上面定义的formatter
    # args handler初始化函数参数
    [handler_consoleHandler]
    class=StreamHandler
    level=DEBUG
    formatter=simpleFormatter
    args=(sys.stdout,)

    [handler_rotateFileHandler]
    class=handlers.RotatingFileHandler
    level=DEBUG
    formatter=simpleFormatter
    args=('access.log','a',2000000,9)

    [handler_webDebugRotateFileHandler]
    class=handlers.RotatingFileHandler
    level=DEBUG
    formatter=simpleFormatter
    args=('debug.log','a',2000000,9)

    [handler_webInfoRotateFileHandler]
    class=handlers.RotatingFileHandler
    level=INFO
    formatter=webSimpleFormatter
    args=('info.log','a',2000000,9)

    [handler_webErrorRotateFileHandler]
    class=handlers.RotatingFileHandler
    level=ERROR
    formatter=webSimpleFormatter
    args=('error.log','a',2000000,9)

    [handler_webSMTPHandler]
    class=handlers.SMTPHandler
    level=ERROR
    formatter=webSimpleFormatter
    args=('mailhost', 'fromaddr', ('toaddrs1','toaddrs2'), 'subject', ('username','password'))
     
     
  • 相关阅读:
    storm从入门到放弃(三),放弃使用 StreamId 特性
    10、图像的几何变换——平移、镜像、缩放、旋转、仿射变换
    滚动条控件
    控件添加——静态控件、编辑框控件、命令按钮、复选框和单选按钮控件
    9、图像处理基础运算
    控件——静态空间、编辑框控件、命令按钮、复选框和单选控件
    Visual C++.NET设计
    CMD指令及其意义
    OpenCV与QT联合开发示例
    OpenCV基础知识介绍
  • 原文地址:https://www.cnblogs.com/harrychinese/p/logging_right_way.html
Copyright © 2011-2022 走看看