python 的日志logging模块学习

来源:互联网 发布:淘宝怎样登陆子账号 编辑:程序博客网 时间:2024/06/16 21:04

1、简单的将日志打印到屏幕

import logginglogging.debug('This is debug message')logging.info('This is info message')logging.warning('This is warning message')屏幕上打印:WARNING:root:This is warning message

默认情况下,logging将日志打印到屏幕:
日志级别为WARNING;
日志级别大小关系为:CRITICAL > ERROR > WARNING > INFO > DEBUG > NOTSET,当然也可以自己定义日志级别。

2、通过logging.basicConfig函数对日志的输出格式及方式做相关配置

import logginglogging.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')logging.debug('This is debug message')logging.info('This is info message')logging.warning('This is warning message')./myapp.log文件中内容为:Sun, 24 May 2009 21:48:54 demo2.py[line:11] DEBUG This is debug messageSun, 24 May 2009 21:48:54 demo2.py[line:12] INFO This is info messageSun, 24 May 2009 21:48:54 demo2.py[line:13] WARNING This is warning message

logging.basicConfig**函数各参数:

filename: 指定日志文件名filemode: 和file函数意义相同,指定日志文件的打开模式,'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.WARNINGstream: 指定将日志的输出流,可以指定输出到sys.stderr,sys.stdout或者文件,默认输出到sys.stderr,当stream和filename同时指定时,stream被忽略

3.将日志同时输出到文件和屏幕

import logginglogging.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 messageroot        : WARNING  This is warning message./myapp.log文件中内容为:Sun, 24 May 2009 21:48:54 demo2.py[line:11] DEBUG This is debug messageSun, 24 May 2009 21:48:54 demo2.py[line:12] INFO This is info messageSun, 24 May 2009 21:48:54 demo2.py[line:13] WARNING This is warning message

4.logging之日志回滚

import loggingfrom logging.handlers import RotatingFileHandler##################################################################################################定义一个RotatingFileHandler,最多备份5个日志文件,每个日志文件最大10MRthandler = 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)################################################################################################

从上例和本例可以看出,logging有一个日志处理的主对象,其它处理方式都是通过addHandler添加进去的。
logging的几种handle方式如下:

logging.StreamHandler: 日志输出到流,可以是sys.stderr、sys.stdout或者文件logging.FileHandler: 日志输出到文件日志回滚方式,实际使用时用RotatingFileHandler和TimedRotatingFileHandlerlogging.handlers.BaseRotatingHandlerlogging.handlers.RotatingFileHandlerlogging.handlers.TimedRotatingFileHandlerlogging.handlers.SocketHandler: 远程输出日志到TCP/IP socketslogging.handlers.DatagramHandler:  远程输出日志到UDP socketslogging.handlers.SMTPHandler:  远程输出日志到邮件地址logging.handlers.SysLogHandler: 日志输出到sysloglogging.handlers.NTEventLogHandler: 远程输出日志到Windows NT/2000/XP的事件日志logging.handlers.MemoryHandler: 日志输出到内存中的制定bufferlogging.handlers.HTTPHandler: 通过"GET""POST"远程输出到HTTP服务器

由于StreamHandler和FileHandler是常用的日志处理方式,所以直接包含在logging模块中,而其他方式则包含在logging.handlers模块中,
上述其它处理方式的使用请参见python2.5手册!

5.通过logging.config模块配置日志

#logger.conf###############################################[loggers]keys=root,example01,example02[logger_root]level=DEBUGhandlers=hand01,hand02[logger_example01]handlers=hand01,hand02qualname=example01propagate=0[logger_example02]handlers=hand01,hand03qualname=example02propagate=0###############################################[handlers]keys=hand01,hand02,hand03[handler_hand01]class=StreamHandlerlevel=INFOformatter=form02args=(sys.stderr,)[handler_hand02]class=FileHandlerlevel=DEBUGformatter=form01args=('myapp.log', 'a')[handler_hand03]class=handlers.RotatingFileHandlerlevel=INFOformatter=form02args=('myapp.log', 'a', 10*1024*1024, 5)###############################################[formatters]keys=form01,form02[formatter_form01]format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)sdatefmt=%a, %d %b %Y %H:%M:%S[formatter_form02]format=%(name)-12s: %(levelname)-8s %(message)sdatefmt=
上例3:import loggingimport logging.configlogging.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')上例4:import loggingimport logging.configlogging.config.fileConfig("logger.conf")logger = logging.getLogger("example02")logger.debug('This is debug message')logger.info('This is info message')logger.warning('This is warning message')

———-

日志的另一种配置方式:

import logging# 创建一个loggerlogger = logging.getLogger('mylogger')logger.setLevel(logging.DEBUG)# 创建一个handler,用于写入日志文件fh = logging.FileHandler('test.log')fh.setLevel(logging.DEBUG)# 再创建一个handler,用于输出到控制台ch = logging.StreamHandler()ch.setLevel(logging.DEBUG)# 定义handler的输出格式formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')fh.setFormatter(formatter)ch.setFormatter(formatter)# 给logger添加handlerlogger.addHandler(fh)logger.addHandler(ch)# 记录一条日志logger.info('foorbar')
  • 结合上面的例子,我们说下几个最常使用的API:

    • logging.getLogger([name])
      • 返回一个logger实例,如果没有指定name,返回root logger。只要name相同,返回的logger实例都是同一个而且只有一个,即name和logger实例是一一对应的。这意味着,无需把logger实例在各个模块中传递。只要知道name,就能得到同一个logger实例。
  • Logger.setLevel(lvl):

    • 设置logger的level, level有以下几个级别:级别高低顺序:NOTSET < DEBUG < INFO < WARNING < ERROR < CRITICAL
  • Logger.addHandler(hdlr)

    • 通过handler对象可以把日志内容写到不同的地方。比如简单的StreamHandler就是把日志写到类似文件的地方。每个Logger可以附加多个Handler。接下来我们就来介绍一些常用的Handler:

      • 1) logging.StreamHandler
        使用这个Handler可以向类似与sys.stdout或者sys.stderr的任何文件对象(file object)输出信息。它的构造函数是:
        StreamHandler([strm])
        其中strm参数是一个文件对象。默认是sys.stderr

      • 2) logging.FileHandler
        和StreamHandler类似,用于向一个文件输出日志信息。不过FileHandler会帮你打开这个文件。它的构造函数是:
        FileHandler(filename[,mode])
        filename是文件名,必须指定一个文件名
        mode是文件的打开方式。参见Python内置函数open()的用法。默认是’a’,即添加到文件末尾。

      • 3) logging.handlers.RotatingFileHandler
        这个Handler类似于上面的FileHandler,但是它可以管理文件大小。当文件达到一定大小之后,它会自动将当前日志文件改名,然后创建 一个新的同名日志文件继续输出。比如日志文件是chat.log。当chat.log达到指定的大小之后,RotatingFileHandler自动把 文件改名为chat.log.1。不过,如果chat.log.1已经存在,会先把chat.log.1重命名为chat.log.2。。。最后重新创建 chat.log,继续输出日志信息。它的构造函数是:
        RotatingFileHandler( filename[, mode[, maxBytes[, backupCount]]])
        其中filename和mode两个参数和FileHandler一样。
        maxBytes用于指定日志文件的最大文件大小。如果maxBytes为0,意味着日志文件可以无限大,这时上面描述的重命名过程就不会发生。
        backupCount用于指定保留的备份文件的个数。比如,如果指定为2,当上面描述的重命名过程发生时,原有的chat.log.2并不会被更名,而是被删除。

      • 4) logging.handlers.TimedRotatingFileHandler
        这个Handler和RotatingFileHandler类似,不过,它没有通过判断文件大小来决定何时重新创建日志文件,而是间隔一定时间就 自动创建新的日志文件。重命名的过程与RotatingFileHandler类似,不过新的文件不是附加数字,而是当前时间。它的构造函数是:
        TimedRotatingFileHandler( filename [,when [,interval [,backupCount]]])
        其中filename参数和backupCount参数和RotatingFileHandler具有相同的意义。
        interval是时间间隔。
        when参数是一个字符串。表示时间间隔的单位,不区分大小写。它有以下取值:
        S 秒M 分H 小时D 天W 每星期(interval==0时代表星期一)midnight 每天凌晨

      • 5) logging.handlers.SocketHandler

      • 6) logging.handlers.DatagramHandler
        以上两个Handler类似,都是将日志信息发送到网络。不同的是前者使用TCP协议,后者使用UDP协议。它们的构造函数是:
        Handler(host, port)
        其中host是主机名,port是端口名

      • 7) logging.handlers.SysLogHandler

      • 8) logging.handlers.NTEventLogHandler
      • 9) logging.handlers.SMTPHandler
      • 10) logging.handlers.MemoryHandler
      • 11) logging.handlers.HTTPHandler

备注:

  • logging.basicConfig([*kwargs]) 这个函数用来配置root logger, 为root
    logger创建一个StreamHandler,设置默认的格式。*
    这些函数:logging.debug()、logging.info()、logging.warning()、logging.error()、logging.critical()
    如果调用的时候发现root logger没有任何handler,会自动调用basicConfig添加一个handler* 如果root
    logger已有handler,这个函数不做任何事情使用basicConfig来配置root logger的输出格式和level:
import logginglogging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG)logging.debug('This message should appear on the console')
  • logger对象直接提供日志接口。formatter描述日志的格式。handler把日志写到不同的地方,你可以把日志保存成本地文件,也可以每个小时写一个日志文件,还可以把日志通过socket传到别的机器上。
    从最简单的formatter对象来看。formatter指定的是每一条日志记录的抬头信息,也就是你可以指定日志记录的时间格式、进程号、文件名、函数名等信息。可以用这个方法来创建一个formatter对象:
logging.Formatter.__init__( fmt=None, datefmt=None)

fmt参数指定进程号、文件名、函数名等信息是否出现以及格式, datefmt为日期时间格式,默认的日期格式精确到微秒,例如‘2003-07-08 16:49:45,896’。fmt中可以指定多个字段,每个字段的格式为“%()s”, 例如你想打印时间、日志级别、日志信息可以用下面的format:

'%(asctime)s - %(levelname)s - %(message)s'
  • 将本文开始的代码封装在一个类中
#开发一个日志系统, 既要把日志输出到控制台, 还要写入日志文件   class Logger():    def __init__(self, logname, loglevel, logger):        '''           指定保存日志的文件路径,日志级别,以及调用文件           将日志存入到指定的文件中        '''        # 创建一个logger        self.logger = logging.getLogger(logger)        self.logger.setLevel(logging.DEBUG)        # 创建一个handler,用于写入日志文件        fh = logging.FileHandler(logname)        fh.setLevel(logging.DEBUG)        # 再创建一个handler,用于输出到控制台        ch = logging.StreamHandler()        ch.setLevel(logging.DEBUG)        # 定义handler的输出格式        #formatter = logging.Formatter('%(asctime)s - %   (name)s - %(levelname)s - %(message)s')        formatter = format_dict[int(loglevel)]        fh.setFormatter(formatter)        ch.setFormatter(formatter)        # 给logger添加handler        self.logger.addHandler(fh)        self.logger.addHandler(ch)    def getlog(self):        return self.logger

再通过以下方式调用,便是一个简单的日志系统了

logger = Logger(logname='log.txt', loglevel=1, logger="fox").getlog()

日志重复输出的坑

你有可能会看到你打的日志会重复显示多次,可能的原因有很多,但总结下来无非就一个,日志中使用了重复的handler。
- 第一坑

import logginglogging.basicConfig(level=logging.DEBUG)fmt = '%(levelname)s:%(message)s'console_handler = logging.StreamHandler()console_handler.setFormatter(logging.Formatter(fmt))logging.getLogger().addHandler(console_handler)logging.info('hello!')

–> INFO:root:hello!
–> INFO:hello!
上面这个例子出现了重复日志,因为在第3行调用basicConfig()方法时系统会默认创建一个handler,如果你再添加一个控制台handler时就会出现重复日志。

  • 第二坑
import loggingdef get_logger():    fmt = '%(levelname)s:%(message)s'    console_handler = logging.StreamHandler()    console_handler.setFormatter(logging.Formatter(fmt))    logger = logging.getLogger('App')    logger.setLevel(logging.INFO)    logger.addHandler(console_handler)    return loggerdef call_me():    logger = get_logger()    logger.info('hi')call_me()call_me()

–>INFO:hi
–> INFO:hi
–> INFO:hi
在这个例子里hi居然打印了三次,如果再调用一次call_me()呢?我告诉你会打印6次。why? 因为你每次调用get_logger()方法时都会给它加一个新的handler,你是自作自受。正常的做法应该是全局只配置logger一次。

  • 第三坑
import loggingdef get_logger():    fmt = '%(levelname)s: %(message)s'    console_handler = logging.StreamHandler()    console_handler.setFormatter(logging.Formatter(fmt))    logger = logging.getLogger('App')    logger.setLevel(logging.INFO)    logger.addHandler(console_handler)    return loggerdef foo():    logging.basicConfig(format='[%(name)s]: %(message)s')    logging.warn('some module use root logger')def main():    logger = get_logger()    logger.info('App start.')    foo()    logger.info('App shutdown.')main()

–> INFO: App start.
–> [root]: some module use root logger
–> INFO: App shutdown.
–> [App]: App shutdown.
为嘛最后的App shutdown打印了两次?所以在Stackoverflow上很多人都问,我应该怎么样把root logger关掉,root logger太坑爹坑妈了。只要你在程序中使用过root logger,那么默认你打印的所有日志都算它一份。上面的例子没有什么很好的办法,我建议你招到那个没有经过大脑就使用root logger的人,乱棍打死他或者开除他。
如果你真的想禁用root logger,有两个不是办法的办法:
logging.getLogger().handlers = [] # 删除所有的handler
logging.getLogger().setLevel(logging.CRITICAL) # 将它的级别设置到最高

小结

  • Python中的日志模块作为标准库的一部分,功能还是比较完善的。个人觉得上手简单,另外也支持比如过滤,文件锁等高级功能,能满足大多数项目需求。
    不过切记,小心坑。

参考文献:

http://blog.csdn.net/zyz511919766/article/details/25136485/
https://docs.python.org/3/library/logging.html#

原创粉丝点击