美文网首页python~flask
Python标准模块之logging模块

Python标准模块之logging模块

作者: Sank | 来源:发表于2017-01-24 17:44 被阅读169次

    在写Python程序的时候不论老手或者新手一般都会用print 函数去console中打印信息,可以用来调试,警告,等等,的确很方便。写小程序的时候没有什么问题,当你的程序很庞大的时候,是不是为打印出来的信息过多而烦恼,时不时的要注释掉或者反注释print函数,而且有些信息不仅仅是希望打印在console中,而且也希望记录下log文件中,或者kafka中呢。 其实python 自带的logging 模块就非常的好用,基本可以满足所有需求。

    Python logging 模块的官方文档地址为:https://docs.python.org/2/howto/logging.html#logging-basic-tutorial

    一个简单的例子

    import logging
    logging.warning('Watch out!')  # will print a message to the console
    logging.info('I told you so')  # will not print anything
    

    如果你输入上面程序,你会看到什么呢? 下面的信息会打印出来:

    WARNING:root:Watch out!
    

    你会发现Info的信息并没有打印出来,这是因为logging 默认的Level是warning,只有在比warning更严重的message才会打印出来。 logging模块中各个Level日志级别关系为:CRITICAL > ERROR > WARNING > INFO > DEBUG > NOTSET,当然也可以自定义。

    logging to file

    一个简单的例子

    import logging
    logging.basicConfig(filename='example.log',level=logging.DEBUG)
    logging.debug('This message should go to the log file')
    logging.info('So should this')
    logging.warning('And this, too')
    

    结果就是,产生一个example.log 文件,内容为

    DEBUG:root:This message should go to the log file
    INFO:root:So should this
    WARNING:root:And this, too
    

    Logging from multiple modules

    如果你的程序包含很多的模块,那么该如何使用和配置logging 模块呢,看下面的例子

    # myapp.py
    import logging
    import mylib
    
    def main():
        logging.basicConfig(filename='myapp.log', level=logging.INFO)
        logging.info('Started')
        mylib.do_something()
        logging.info('Finished')
    
    if __name__ == '__main__':
        main()
    
    # mylib.py
    import logging
    
    def do_something():
        logging.info('Doing something')
    

    如果你跑上面的程序,在myapp.log文件里面的内容为

    INFO:root:Started
    INFO:root:Doing something
    INFO:root:Finished
    

    格式化log

    在log中我们经常可以看到时间戳,机器名等信息,这些会使我们的log 更加的专业,python的logging 模块当然也支持这种功能。 看下面的例子

    import logging
    logging.basicConfig(format='%(asctime)s %(message)s')
    logging.warning('is when this event was logged.')
    

    输出结果为

    2010-12-12 11:41:42,612 is when this event was logged.
    

    其中时间格式是可以调整的,比如

    import logging
    logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')
    logging.warning('is when this event was logged.')
    

    输出为

    12/12/2010 11:46:36 AM is when this event was logged.
    

    logging.basicConfig 函数中各个参数的说明如下:

    • filename: 指定日志文件名
    • filemode: 和file函数意义相同,指定日志文件的打开模式,'w'或'a'。 如果指定为'w',日志文件每次会被覆盖。默认为'a'
    • format: 指定输出的格式和内容,format可以输出很多有用信息,如上例所示:
      %(levelno)s: 打印日志级别的数值
      %(levelname)s: 打印日志级别名称
      %(pathname)s: 打印当前执行程序的路径,其实就是sys.argv[0]
      %(filename)s: 打印当前执行程序名
      %(funcName)s: 打印日志的当前函数
      %(lineno)d: 打印日志的当前行号
      %(asctime)s: 打印日志的时间
      %(thread)d: 打印线程ID
      %(threadName)s: 打印线程名称
      %(process)d: 打印进程ID
      %(message)s: 打印日志信息
    • datefmt: 指定时间格式,同time.strftime()
    • level: 设置日志级别,默认为logging.WARNING
    • stream: 指定将日志的输出流,可以指定输出到sys.stderr,sys.stdout或者文件,默认输出到sys.stderr,当stream和filename同时指定时,stream被忽略

    上面为基本的简单用法,可以满足大部分的需求

    logging高级用法

    python的logging其实是由下面四个部分组成:

    • Loggers : 这个给用户用接口
    • Hanlders: logger生成的log内容由Handler发送到指定的地方,比如console,log文件,kafka等等
    • Filters
    • Formatters : 制定log最后输出的格式

    看个例子

    import logging
    
    # create logger
    logger = logging.getLogger('simple_example')
    logger.setLevel(logging.DEBUG)
    
    # create console handler and set level to debug
    ch = logging.StreamHandler()
    ch.setLevel(logging.DEBUG)
    
    # create formatter
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    
    # add formatter to ch
    ch.setFormatter(formatter)
    
    # add ch to logger
    logger.addHandler(ch)
    
    # 'application' code
    logger.debug('debug message')
    logger.info('info message')
    logger.warn('warn message')
    logger.error('error message')
    logger.critical('critical message')
    

    输出结果是:

    $ python simple_logging_module.py
    2005-03-19 15:10:26,618 - simple_example - DEBUG - debug message
    2005-03-19 15:10:26,620 - simple_example - INFO - info message
    2005-03-19 15:10:26,695 - simple_example - WARNING - warn message
    2005-03-19 15:10:26,697 - simple_example - ERROR - error message
    2005-03-19 15:10:26,773 - simple_example - CRITICAL - critical message
    

    过程比较清晰,先建logger,handler, formatter。然后给handler指定formatter,再给logger添加handler。 一个logger其实可以添加多个handler,而每个handler的formatter可以不同。这样你就可以既在console 中打印信息,又在log文件中记录信息。

    下面这个例子就是这样:

    import logging
    logging.basicConfig(level=logging.DEBUG,
    format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
    datefmt='%a, %d %b %Y %H:%M:%S',
    filename='myapp.log',
    filemode='w')
    
    #定义一个StreamHandler,将INFO级别或更高的日志信息打印到标准错误,并将其添加到当前的日志处理对象#
    console = logging.StreamHandler()
    console.setLevel(logging.INFO)
    formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s')
    console.setFormatter(formatter)
    logging.getLogger('').addHandler(console)
    
    logging.debug('This is debug message')
    logging.info('This is info message')
    logging.warning('This is warning message')
    

    输出为:

    屏幕上打印:
    root : INFO This is info message
    root : WARNING This is warning message
    ./myapp.log文件中内容为:
    Sun, 24 May 2009 21:48:54 demo2.py[line:11] DEBUG This is debug message
    Sun, 24 May 2009 21:48:54 demo2.py[line:12] INFO This is info message
    Sun, 24 May 2009 21:48:54 demo2.py[line:13] WARNING This is warning message
    

    回滚日志

    你可能不希望日子文件太大,所以你可能需要用到回滚日志的handler,看下面的例子

    import logging
    from logging.handlers import RotatingFileHandler
    #################################################################################################
    #定义一个RotatingFileHandler,最多备份5个日志文件,每个日志文件最大10M
    Rthandler = RotatingFileHandler('myapp.log', maxBytes=10*1024*1024,backupCount=5)
    Rthandler.setLevel(logging.INFO)
    formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s')
    Rthandler.setFormatter(formatter)
    logging.getLogger('').addHandler(Rthandler)
    ################################################################################################
    

    python logging 模块提供下面几种handlers

    • StreamHandler instances send messages to streams (file-like objects).
    • FileHandler
      instances send messages to disk files.
    • BaseRotatingHandler
      is the base class for handlers that rotate log files at a certain point. It is not meant to be instantiated directly. Instead, use RotatingFileHandler
      or TimedRotatingFileHandler
      .
    • RotatingFileHandler
      instances send messages to disk files, with support for maximum log file sizes and log file rotation.
      TimedRotatingFileHandler
      instances send messages to disk files, rotating the log file at certain timed intervals.
    • SocketHandler
      instances send messages to TCP/IP sockets.
    • DatagramHandler
      instances send messages to UDP sockets.
      SMTPHandler
      instances send messages to a designated email address.
      SysLogHandler
      instances send messages to a Unix syslog daemon, possibly on a remote machine.
    • NTEventLogHandler
      instances send messages to a Windows NT/2000/XP event log.
    • MemoryHandler
      instances send messages to a buffer in memory, which is flushed whenever specific criteria are met.
    • HTTPHandler
      instances send messages to an HTTP server using either GET
      or POST
      semantics.
    • WatchedFileHandler
      instances watch the file they are logging to. If the file changes, it is closed and reopened using the file name. This handler is only useful on Unix-like systems; Windows does not support the underlying mechanism used.
    • NullHandler
      instances do nothing with error messages. They are used by library developers who want to use logging, but want to avoid the ‘No handlers could be found for logger XXX’ message which can be displayed if the library user has not configured logging. See Configuring Logging for a Library for more information.

    自己创建handler

    当然你可以自己创建一个handler去处理,比如kafka, 看下面的例子

    from kafka.client import KafkaClient
    from kafka.producer import SimpleProducer,KeyedProducer
    import logging,sys
    
    class KafkaLoggingHandler(logging.Handler):
    
        def __init__(self, host, port, topic, key=None):
            logging.Handler.__init__(self)
            self.kafka_client = KafkaClient(host, port)
            self.key = key
            if key is None:
                self.producer = SimpleProducer(self.kafka_client, topic)
            else:
                self.producer = KeyedProducer(self.kafka_client, topic)
    
        def emit(self, record):
            #drop kafka logging to avoid infinite recursion
            if record.name == 'kafka':
                return
            try:
                #use default formatting
                msg = self.format(record)
                #produce message
                if self.key is None:
                    self.producer.send_messages(msg)
                else:
                    self.producer.send(self.key, msg)
            except:
                import traceback
                ei = sys.exc_info()
                traceback.print_exception(ei[0], ei[1], ei[2], None, sys.stderr)
                del ei
    
        def close(self):
            self.producer.stop()
            logging.Handler.close(self)
    
    kh = KafkaLoggingHandler("localhost", 9092, "test_log")
    #OR
    #kh = KafkaLoggingHandler("localhost", 9092, "test_log", "key1")
    
    logger = logging.getLogger("")
    logger.setLevel(logging.DEBUG)
    logger.addHandler(kh)
    logger.info("The %s boxing wizards jump %s", 5, "quickly")
    logger.debug("The quick brown %s jumps over the lazy %s", "fox",  "dog")
    try:
        import math
        math.exp(1000)
    except:
        logger.exception("Problem with %s", "math.exp")
    

    P.S. The handler uses this Kafka client: https://github.com/mumrah/kafka-python

    通过logging.config模块配置日志

    logging模块的配置其实可以通过文件来配置,这样更加方便,看下面的例子

    #logger.conf
    ###############################################
    [loggers]
    keys=root,example01,example02
    [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
    ###############################################
    [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=%(name)-12s: %(levelname)-8s %(message)s
    datefmt=
    
    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')
    

    相关文章

      网友评论

        本文标题:Python标准模块之logging模块

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