Python日志logging模块使用详解
- 2019 年 10 月 4 日
- 笔记
前言
每个语言都会有自己的日志模块,Python也不例外。通常情况下当需要使用到日志的时候, 一般都是匆匆查找下资料,按照步骤进行下配置就是完事了,不太会去总结日志模块的使用方式。 经常过一段时间新项目需要用的时候,还是需要去网上搜索下配置方式。所以今天就为了日后的使用方便而进行的内容整理。
使用默认配置记录日志
Python的日志模块是logging,属于Python的标准发行库。如果你只是用于调试程序,又不想用print
函数的情况下。 倒是可以直接使用logging的默认配置来进行调试信息的打印。比如:
import logging logging.debug('debug') logging.info('info') logging.warning('warning')
这段代码的运行结果如下:(日志格式为:日志等级:Logger名称:用户信息)
WARNING:root:warning
可以看出,默认情况下logging的日志级别为warning
,并不是info
。当然你也可以通过如下设置来更改日志级别。
import logging logging.basicConfig(level=logging.INFO, format="[%(asctime)s][%(name)s][%(levelname)s] => %(message)s", datefmt='%Y-%m-%d %H:%M:%S %a' ) logging.info('info')
这段代码不仅设置了默认的日志等级,同时还设置了日志的输出格式和日期格式。该段代码的运行结果如下:
[2019-03-23 17:57:31 Sat][root][INFO] => info
关于更多的日志格式属性说明,请参见官方说明文档
写入到日志文件
前面都是直接在命令行输出日志信息,有时候你可能希望输入到日志文件中。那么你也可以通过basicConfig方法来进行设置。比如:
import logging logging.basicConfig(level=logging.INFO, format="[%(asctime)s][%(name)s][%(levelname)s] => %(message)s", datefmt='%Y-%m-%d %H:%M:%S %a', filename='my.log', filemode='a' ) logging.info('info')
该段代码执行后,不会在命令行有任何信息的输出,但是会在代码执行目录生成一个mylog
的日志文件。里面的内容为:
[2019-03-23 17:58:27 Sat][root][INFO] => info
另外,这个设置中还规定了文件日志的写入模式为a
,即追加写入模式。重新再次执行该代码时会在原日志内容后追加新日志。 而如果你希望每次执行都是覆盖原来的日志内容,则可以把写入模式该为w
,即写入模式。
日志信息格式化
前面作为介绍方便,打印的信息都是比较简单的内容;而有时候我们还是会打印一些比较复杂的、需要拼接的字符内容。 此时就需要使用格式化的功能来完成了,除了我们提前把日志信息格式化好,Logging的日志方法也提供了格式化的调用。
import logging logging.warning('this is %s', 'warning')
需要注意的是,
logging.basicConfig
方法只能配置一次,配置多次时也只有第一次会生效。
通过代码配置日志
上面是直接使用logging模块的方法来记录日志信息的,这时用到的是Logger是顶级的Logger,名字为root
,是个单例对象。 其实除了这个顶级的Logger之外,还可以生成其它的Logger对象。下面就来做个介绍。
首先,想要获取Logger对象,可以通过logging.getLogger方法来实现。比如:
import logging logging.basicConfig(level=logging.INFO, format="[%(asctime)s][%(name)s][%(levelname)s] => %(message)s", datefmt='%Y-%m-%d %H:%M:%S %a' ) Logger = logging.getLogger() # 获取的是名为root的Logger Logger.warning('this is warning') Logger = logging.getLogger(__name__) # 获取的是名为__main__的Logger Logger.warning('this is warning')
这段代码在命令行打印的信息如下:
[2019-03-23 18:52:39 Sat][root][WARNING] => this is warning [2019-03-23 18:52:39 Sat][__main__][WARNING] => this is warning
可以看到%(name)s
占位符的内容分别是root和main。当然你也可以获取指定名字的Logger。 比如:logging.getLogger('test')
则会获取一个名为test的Logger。
不同Logger进行不同设置
通过上面的代码,我们已经可以获取多个Logger对象了,那么就有需求对不同的Logger进行日志设置。不同Logger分别不同设置的操作如下:
import logging formatter = logging.Formatter( fmt="[%(asctime)s][%(name)s][%(levelname)s] => %(message)s", datefmt="%Y-%m-%d %H:%M:%S %a" ) Logger = logging.getLogger('test') Logger.setLevel(logging.INFO) FH = logging.FileHandler("my.log", encoding="utf-8") # 文件处理器,日志会记录到文件 FH.setFormatter(formatter) Logger.addHandler(FH) Logger.info('this is info') Logger2 = logging.getLogger(__name__) Logger2.setLevel(logging.WARNING) CH = logging.StreamHandler() # 流处理器,日志默认显示在命令行 CH.setFormatter(formatter) Logger2.addHandler(CH) Logger2.warning('this is warning')
这段代码执行后,info的日志会写入到my.log文件,而warning的日志则会在命令行执行打印出来。 此外,还可以对日志文件按照指定规则进行分割,这时就需要使用特定的Handler来完成这个任务了。 比如:RotatingFileHandler可以根据文件大小来分割,TimedRotatingFileHandler可以按照指定的时间间隔来分割日志。
import logging.handlers formatter = logging.Formatter( fmt="[%(asctime)s][%(name)s][%(levelname)s] => %(message)s", datefmt="%Y-%m-%d %H:%M:%S %a" ) Logger = logging.getLogger('test') Logger.setLevel(logging.INFO) RFH = logging.handlers.RotatingFileHandler("test.log", maxBytes=1024 * 1024 * 5, backupCount=5) RFH.setFormatter(formatter) Logger.addHandler(RFH) Logger.info('this is info') Logger2 = logging.getLogger('test2') Logger2.setLevel(logging.WARNING) TRFH = logging.handlers.TimedRotatingFileHandler("test2.log", when='D', interval=2, backupCount=3) TRFH.setFormatter(formatter) Logger2.addHandler(TRFH) Logger2.warning('this is warning')
这段代码中RotatingFileHandler
的配置是日志文件按照5M大小来分割,最多最多保留5个日志文件。 而TimedRotatingFileHandler
的配置是按照每2天分割一次日志文件,保留3个日志文件。
关于更多的Handler类,可以查看官方文档
通过文件配置日志
上面介绍的日志配置都是通过代码的方式配置的,其实还可以通过日志配置文件来配置。下面就来看看配置文件的格式,首先是字典配置的格式:
import logging.config LOGGING = { "version": 1, "disable_existing_loggers": False, "formatters": { "simple": { 'format': '%(asctime)s [%(name)s:%(lineno)d] [%(levelname)s]- %(message)s' }, 'standard': { 'format': '%(asctime)s [%(threadName)s:%(thread)d] [%(name)s:%(lineno)d] [%(levelname)s]- %(message)s' }, }, "handlers": { "console": { "class": "logging.StreamHandler", "level": "DEBUG", "formatter": "simple", "stream": "ext://sys.stdout" }, "default": { "class": "logging.handlers.RotatingFileHandler", "level": "INFO", "formatter": "simple", "filename": 'test.log', 'mode': 'a', "maxBytes": 1024*1024*5, # 5 MB "backupCount": 5, "encoding": "utf8" }, }, "loggers": { "test": { "level": "WARNING", "handlers": ["console"], "propagate": "no" } }, "root": { 'handlers': ['default'], 'level': "INFO", 'propagate': False } } logging.config.dictConfig(LOGGING) Logger = logging.getLogger('test') Logger.warning('this is warning')
接着是文件配置的格式,先看日志配置文件:
[loggers] keys=root,logger01 [logger_root] level=DEBUG handlers=hand01 [logger_logger01] handlers=hand02,hand03 qualname=logger01 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=('test.log', 'a') [handler_hand03] class=handlers.RotatingFileHandler level=INFO formatter=form02 args=('test.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
python代码就更简单了。
import logging.config logging.config.fileConfig('logging.conf') Logger = logging.getLogger() # root Logger Logger.warning('this is warning') Logger2 = logging.getLogger('logger01') # logger01 Logger Logger2.info('this is info')
总结
到这里关于Python的logging模块的介绍就结束了。现在回过头来再总结下,logging模块其实有很多的子模块, 不同的子模块有不同的作用,具体而言可以通过一张图来理解。

logging
从图中可以看出logging模块的主要子模块有:Logger,Handler,Filter, Formatter等。 其中Logger下面调用Handler,Handler下面调用Filter和Formatter来进行日志处理。