zoukankan      html  css  js  c++  java
  • python 自动化之路 logging日志模块

    logging 日志模块

    http://python.usyiyi.cn/python_278/library/logging.html 中文官方
    http://blog.csdn.net/zyz511919766/article/details/25136485 清晰明了,入门必备
    http://my.oschina.net/leejun2005/blog/126713 继承讲的很棒
    http://my.oschina.net/u/126495/blog/464892 实例分析

    一:概述


    在实际项目中,需要对一些数据进行日志记录,并将日志记录到不同的存储单元中,例如数据库,文本,或者推送到图形化界面中,当需要时发现自己实现一个日
    志库其实是要很大的代价,因此,第三方的日志库上进行定制化处理 正文内容是对logging的理解和使用方式,非常方便
    1:四个主要类,使用官方文档中的概括:


      logger 提供了应用程序可以直接使用的接口;
      handler 将(logger创建的)日志记录发送到合适的目的输出;
      filter 提供了细度设备来决定输出哪条日志记录;用处不太大
      formatter 决定日志记录的最终输出格式


    2:模块级函数


      logging.getLogger([name]): #返回一个logger对象,如果没有指定名字将返回root logger,最常用
      logging.basicConfig(): #给logger对象的配置管理函数 ,不常用
      logging.debug()logging.info()logging.warning()logging.error()logging.critical(): #logger的日志级别

    二: logging工作流演示

    cat demo.py
    #coding:utf-8
    import logging
    # 创建一个logger命名为mylogger, %(name)s可调用这个名字
    mylogger = logging.getLogger('mylogger')
    mylogger.setLevel(logging.DEBUG)
    # 定义日志输出格式formatter
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(filename)s- %(levelname)s - %(message)s')
    # 创建一个handler,用于写入日志文件,只输出debug级别以上的日志,并调用定义的输出格式
    fh = logging.FileHandler('test.log')
    fh.setLevel(logging.DEBUG)
    fh.setFormatter(formatter)
    # 再创建一个handler,用于输出到控制台, 一般不用
    ch = logging.StreamHandler()
    ch.setLevel(logging.DEBUG)
    ch.setFormatter(formatter)
    # 给我们开始实例化的logger对象添加handler
    mylogger.addHandler(fh)
    mylogger.addHandler(ch)
    # 直接在本模块中调用记录两条日志——生产环境会封装成函数调用
    mylogger.info('foorbar')
    mylogger.debug('just a test ')
    $ python demo.py
    2015-10-30 15:44:01,722 - mylogger - test1.py- INFO - foorbar
    2015-10-30 15:44:01,723 - mylogger - test1.py- DEBUG - just a test
    http://www.php101.cn/2015/03/05/Python%E4%B8%AD%E7%9A%84Logging%E7%AE%80%E4%BB%8B/ 精彩实例

    三:logging模块的API

    1:logging.getLogger([name])
    返回一个logger实例,如果没有指定name,返回root logger。只要name相同,返回的logger实例都是同一个而且只有一个,即name和logger实例是一一对
    应的。这意味着,无需把logger实例在各个模块中传递。只要知道name,就能得到同一个logger实例
    2:logger.setLevel(lvl)——设置logger的level,
    level有以下几个级别:
    NOTSET < DEBUG < INFO < WARNING < ERROR < CRITICAL
    不同日志级别对应的数字对照
    级别 数值
    CRITICAL 50
    ERROR 40
    WARNING 30
    INFO 20
    DEBUG 10
    NOTSET 0
    如果把logger的级别设置为INFO, 那么小于INFO级别的日志都不输出, 大于等于INFO级别的日志都输出。也就意味着同一个logger实例,如果多个地方调用,会
    出现很多重复的日志
    logger.debug("debug") # 不输出
    logger.info("info") # 输出
    logger.warning("warning") # 输出
    logger.error("error") # 输出
    logger.critical("critical") # 输出
    3:logger.addHandler(hd)—— logger雇佣handler来帮它处理日志
    handler对象负责发送相关的信息到指定目的地。Python的日志系统有多种Handler可以使用。有些Handler可以把信息输出到控制台,有些Logger可以把信息输
    出到文件,还有些 Handler可以把信息发送到网络上。如果觉得不够用,还可以编写自己的Handler。可以通过addHandler()方法添加多个多handler
    handler主要有以下几种:
    logging.StreamHandler: #日志输出到流即控制台,可以是sys.stderr、sys.stdout
    logging.FileHandler: #日志输出到文件
    logging.handlers.RotatingFileHandler #日志输出到文件,并按照设定的日志文件大小切割
    logging.handlers.TimedRotatingFileHandler #日志输出到文件,并按设定的时间切割日志文件
    logging.handlers.SocketHandler: #远程输出日志到TCP/IP sockets
    logging.handlers.DatagramHandler: #远程输出日志到UDP sockets
    logging.handlers.SMTPHandler: #远程输出日志到邮件地址
    logging.handlers.SysLogHandler: #日志输出到syslog
    logging.handlers.NTEventLogHandler: #远程输出日志到Windows NT/2000/XP的事件日志
    logging.handlers.MemoryHandler: #日志输出到内存中的制定buffer
    由于StreamHandlerFileHandler是常用的日志处理方式,所以直接包含在logging模块中,而其他方式则包含在logging.handlers模块中,
    handle常见调用
    Handler.setLevel(lel) #指定被处理的信息级别,低于lel级别的信息将被忽略
    Handler.setFormatter() #给这个handler选择一个格式
    Handler.addFilter(filt)、Handler.removeFilter(filt):#新增或删除一个filter对象
    举两个栗子
    1: logging内部调用——测试学习即可
    cat demo.py
    import logging
    import logging.handlers
    LOG_FILE = 'api.log'
    #定义日志文件切割规则最多备份5个日志文件,每个日志文件最大10M
    handler = logging.handlers.RotatingFileHandler(LOG_FILE, maxBytes = 10*1024*1024, backupCount = 5)
    #handler=logging.handlers.TimedRotatingFileHandler(LOG_FILE, when='midnight') #每天零点切换
    #定义日志输出格式
    fmt = '%(asctime)s - %(filename)s:%(lineno)s - %(name)s - %(message)s'
    formatter = logging.Formatter(fmt) # 实例化formatter
    handler.setFormatter(formatter) # 为handler添加formatter
    #实例化一个logger对象,并为其绑定handle
    mylogger = logging.getLogger('test')
    mylogger.addHandler(handler) # 为mylogger添加handler
    mylogger.setLevel(logging.DEBUG) # 为mylogger设置输出级别
    #调用mylogger
    mylogger.info('first info message')
    mylogger.debug('first debug message')
    2: logging封装为函数——适用生产环境中
    cat util.py
    #/usr/bin/env python
    #coding:utf-8
    import logging,logging.handlers
    def WriteLog(log_name):
    log_filename = "/tmp/test.log"
    log_level = logging.DEBUG
    format = logging.Formatter('%(asctime)s %(filename)s [line:%(lineno)2d]-%(funcName)s %(levelname)s %(message)s')
    handler = logging.handlers.RotatingFileHandler(log_filename, mode='a', maxBytes=10*1024*1024, backupCount=5)
    handler.setFormatter(format)
    logger = logging.getLogger(log_name)
    logger.addHandler(handler)
    logger.setLevel(log_level)
    return logger #函数最终将实例化的logger对象返回,后面直接调用即可
    if __name__ == "__main__":
    WriteLog('api').info('123') #模块内部直接调用函数。等价下面两行,下面的方法不推荐
    # writelog = WriteLog('api')
    # writelog.info('123')
    '''
    外面程序调用函数
    cat test.py
    import util
    def index()
    util.WriteLog('api').info('123')
    index()
    '''
    4:logging.basicConfig([**kwargs])——加载logger的各项配置参数,不好用
    cat test.py
    import logging
    logging.basicConfig(level=logging.DEBUG, #输出debug及其级别更高级别的日志
    format='%(asctime)s %(filename)s [line:%(lineno)d] %(levelname)s %(message)s',
    datefmt='%d %b %Y %H:%M:%S',
    filename='myapp.log', #日志文件输出的文件地址,不写默认打印到桌面
    filemde='w')
    logging.debug("this is debug message")
    logging.info("this is info message")
    logging.warning("this is warning message")
    tail -f myapp.log
    30 Oct 2015 14:18:51 test.py [line:8] DEBUG this is debug message
    30 Oct 2015 14:18:51 test.py [line:9] INFO this is info message
    30 Oct 2015 14:18:51 test.py [line:10] WARNING this is warning message
    关于logging.basicConfig函数的常用配置:
    filename: 指定日志文件名
    filemode: 和file函数意义相同,指定日志文件的打开模式,'w'或'a'
    datefmt: 指定时间格式,同time.strftime()
    level: 设置日志级别,默认为logging.WARNING,即warning及级别更高的日志才输出
    stream: 指定将日志的输出流,可以指定输出到sys.stderr,sys.stdout或者文件,默认输出到sys.stderr,当stream和filename同时指定时,stream被
    忽略
    format: 指定输出的格式和内容,format可以输出很多有用信息,如上例所示:
    %(name)s 打印logger名,默认为root
    %(levelno)s: 打印日志级别的数值
    %(levelname)s: 打印日志级别名称
    %(pathname)s: 打印当前执行程序的路径,其实就是sys.argv[0]
    %(filename)s: 打印当前执行程序名
    %(funcName)s: 打印日志的当前函数
    %(lineno)d: 打印日志的当前行号
    %(asctime)s: 打印日志的时间
    %(message)s: 打印日志信息
    %(thread)d: 打印线程ID
    %(threadName)s: 打印线程名称
    %(process)d: 打印进程ID
    5: logging.config模块通过配置文件的方式,加载logger的参数——最好用的方式
    cat logger.conf
    # 定义logger模块,root是父类,必需存在的,其它的是自定义。
    # logging.getLogger(NAME)就相当于向logging模块注册了实例化了
    # name 中用 . 表示 log 的继承关系
    [loggers]
    keys=root,example01,example02
    # [logger_xxxx] logger_模块名称
    # level 级别,级别有DEBUG、INFO、WARNING、ERROR、CRITICAL
    # handlers 处理类,可以有多个,用逗号分开
    # qualname logger名称,应用程序通过 logging.getLogger获取。对于不能获取的名称,则记录到root模块。
    # propagate 是否继承父类的log信息,0:否 1:是
    [logger_root]
    level=DEBUG
    handlers=hand01,hand02
    [logger_example01]
    handlers=hand01,hand02
    qualname=example01
    propagate=0
    [logger_example02]
    handlers=hand01,hand03
    qualname=example02
    propagate=0
    # [handler_xxxx]
    # class handler类名
    # level 日志级别
    # formatter,上面定义的formatter
    # args handler初始化函数参数
    [handlers]
    keys=hand01,hand02,hand03
    [handler_hand01]
    class=StreamHandler
    level=INFO
    formatter=form02
    args=(sys.stderr,)
    [handler_hand02]
    class=FileHandler
    level=DEBUG
    formatter=form01
    args=('myapp.log', 'a')
    [handler_hand03]
    class=handlers.RotatingFileHandler
    level=INFO
    formatter=form02
    args=('myapp.log', 'a', 10*1024*1024, 5)
    # 日志格式
    [formatters]
    keys=form01,form02
    [formatter_form01]
    format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s
    datefmt=%a, %d %b %Y %H:%M:%S
    [formatter_form02]
    format=%(asctime)s%(name)-12s: %(levelname)-8s %(message)s
    datefmt=%a, %d %b %Y %H:%M:%S
    调用
    import logging
    import logging.config
    logging.config.fileConfig("logger.conf")
    logger = logging.getLogger("example01")
    logger.debug('This is debug message')
    logger.info('This is info message')
    logger.warning('This is warning message')
    生产环境中使用案例——通过函数定义调用
    A:定义到工具模块中
    cat util.py
    import logging,
    import logging.config
    def write_log(loggername):
    work_dir = os.path.dirname(os.path.realpath(__file__))
    log_conf= os.path.join(work_dir, 'conf/logger.conf')
    logging.config.fileConfig(log_conf)
    logger = logging.getLogger(loggername)
    return logger
    B: 外部模块调用
    cat test.py
    import util
    util.write_log('api').info('just a test')

    四: 关于root logger以及logger的父子关系


    logger实例之间还有父子关系, root logger就是处于最顶层的logger, 它是所有logger的祖先。如下图:
    如何得到root logger
    root logger是默认的logger如果不创建logger实例, 直接调用logging.debug()、logging.info()logging.warning(),logging.error()、logging.critical()这
    些函数,
    那么使用的logger就是 root logger, 它可以自动创建,也是单实例的。
    root logger的日志级别
    root logger默认的level是logging.WARNING
    如何表示父子关系
    logger的name的命名方式可以表示logger之间的父子关系. 比如:
    parent_logger = logging.getLogger('foo')
    child_logger = logging.getLogger('foo.bar')
    什么是effective level
    logger有一个概念,叫effective level。 如果一个logger没有显示地设置level,那么它就
    用父亲的level。如果父亲也没有显示地设置level, 就用父亲的父亲的level,以此推....
    最后到达root logger,一定设置过level。默认为logging.WARNING
    child loggers得到消息后,既把消息分发给它的handler处理,也会传递给所有祖先logger处理,
    eg:
    import logging
    # 设置root logger,祖先
    r = logging.getLogger()
    ch = logging.StreamHandler()
    ch.setLevel(logging.DEBUG)
    formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
    ch.setFormatter(formatter)
    r.addHandler(ch)
    # 创建一个logger作为父亲
    p = logging.getLogger('foo')
    p.setLevel(logging.DEBUG)
    ch = logging.StreamHandler()
    ch.setLevel(logging.DEBUG)
    formatter = logging.Formatter('%(asctime)s - %(message)s')
    ch.setFormatter(formatter)
    p.addHandler(ch)
    # 创建一个孩子logger
    c = logging.getLogger('foo.bar')
    c.debug('foo')
    输出:
    2011-08-31 21:04:29,893 - foo #父亲处理
    2011-08-31 21:04:29,893 - DEBUG - foo #祖先处理
    可见, 孩子logger没有任何handler,所以对消息不做处理。但是它把消息转发给了它的父亲以及root logger。最后输出两条日志。
    这也就出现了一个问题,同一条日志会重复输出
    解决方案
    1:每个logger实例都给一个独立的名字,输出之间互不影响,
    2: logging.conf中定义不继承

  • 相关阅读:
    LeetCode456. 132模式
    LeetCode455. 分发饼干
    LeetCode454. 四数相加 II
    LeetCode453. 最小移动次数使数组元素相等
    matchMedia 媒体查询结果
    异常捕获
    常用xpath选择器和css选择器总结
    python-爬虫中的extract()
    Python应用前景广阔,怎么学才能快速上手?
    Python 为什么要有 pass 语句?
  • 原文地址:https://www.cnblogs.com/yangliheng/p/6058436.html
Copyright © 2011-2022 走看看