logging模块
@(杂庭忆技)[Python, logging]
018.8.17
简单示例
import logging
logging.debug("debug")
logging.info("info")
logging.warning("warning")
logging.error("error")
# 输出
>>WARNING:root:warning
>>ERROR:root:error
默认情况下,logging将日志打印到屏幕上,按WARNING级别
日志等级
CRITICAL > ERROR > WARNING > INFO > DEBUG > NOTSET
设置日志等级以后,程序会自动过滤掉低于设置等级的日志信息。如:当等级为WARNING的时候,包括INFO及以下的日志信息会被过滤掉
简单设置
利用logging模块中的basicConfig()
方法对日志做简单设置
import logging
# 设置日志的基本信息
logging.basicConfig(
level=logging.DEBUG, # 设置等级
filename = "test.log", # 设置输出文件
datefmt = "%Y/%m/%d %H:%M:%S", # 设置时间格式
format = "【%(asctime)s %(levelname)s】 %(lineno)d: %(message)s") # 设置输出格式
logging.debug("debug")
logging.info("info")
logging.warning("warning")
logging.error("error")
运行程序之后,当前目录下会生成一个test.log的文件,内容如下:
可见输出格式是按照参数format指定样子生成的,而asctime,levelname,lineno指定的是需要输出哪些内容,这里依次是:时间,日志等级,出现行号,以及日志内容;如果不看小括号中的内容,其实%s,%d就是格式占位符
当然basicConfig()
的用法并没有完,完整参数如下
- filename:输出文件路径
- filemode:按什么方式写入,w
表示清空内容之后写入,a
表示追加(默认追加模式)
- datefmt:设置时间格式
- style:当format传入了参数的时候,用style参数来指定占位符,可以是:%,{,$
import logging
# 不指定style时,默认%
logging.basicConfig(
level=logging.DEBUG,
datefmt = "%Y/%m/%d",
filename = "test.log",
format = "%(asctime)s %(message)s")
# 指定用符号 {
logging.basicConfig(
level=logging.DEBUG,
style = "{",
datefmt = "%Y/%m/%d",
filename = "test.log",
format = "{asctime} {message}")
# 指定用符号 $
logging.basicConfig(
level=logging.DEBUG,
style = "{",
datefmt = "%Y/%m/%d",
filename = "test.log",
format = "$asctime $message")
logging.debug("debug")
需要区分%、{、$占位时的不同用法
- level:指定日志输出等级,只有大于等于这个等级的日志才会被输出
- stream:输出流,可以理解为输出到终端,当没有设置filename参数的时候,默认用StreamHandler;如果同时设置了stream和filename参数,stream的效果将被忽略
- handlers:指定日志处理时使用的Handler
- format:指定日志输出格式。常用参数如下:
1. asctime
:时间,默认按照2003-07-08 16:49:45,896格式
2. created
:也是时间,但以时间戳形式输出
3. filename
:当前执行文件的名字
4. funcName
:当前执行函数的名字
5. levelname
:日志级别的名称(WARNING、ERROR…)
6. lineno
:当前行号
7. message
:日志内容
8. module
:模块名
9. process
:进程号
10. processName
:进程名字
11. thread
:线程号
12. threadName
:线程名字
13. (更多相关参数可查看官方文档)
Handler
FileHandler
前面都是利用logging的basicConfig()
方法配置全局信息,我们也可以通过getLogger()
方法返回一个logging.Logger类型的对象进行动态配置
import logging
# 实例一个logger对象,并对其添加文件名
logger = logging.getLogger(__name__)
# 设置日志级别
logger.setLevel(logging.WARNING)
# 构建日志格式
formatter = logging.Formatter("%(asctime)s %(levelname)s %(message)s")
# 构建一个handler对象,并设置日志存放路径
handler = logging.FileHandler("test.log")
# 添加格式
handler.setFormatter(formatter)
# 添加handler
logger.addHandler(handler)
# 输出日志
logger.error("error")
我们也可以构建多个handler来处理问题,比如将日志输出到文件的同时,也在终端输出
import logging
logger = logging.getLogger(__name__)
formatter = logging.Formatter("【%(asctime)s %(levelname)s】 %(message)s")
# 构建一个StreamHandler,用于输出到终端
strHandler = logging.StreamHandler()
strHandler.setLevel(logging.WARN) # 设置等级高于WARN以及WARN以上的(WARNING的简写)
strHandler.setFormatter(formatter)
logger.addHandler(strHandler)
# 构建一个FileHandler,用于输出到文件
fileHandler = logging.FileHandler("a-simmple.log")
fileHandler.setLevel(logging.INFO)# 设置等级高于INFO以及INFO以上的(WARNING的简写)
fileHandler.setFormatter(formatter)
logger.addHandler(fileHandler)
logger.debug("debug")
logger.info("info")
logger.warning("warning")
logger.error("error")
终端输出结果如下:
日志文件内容如下:
需要注意一点,在使用Handler(FileHandler,StreamHandler)对象的setLevel()方法的时候,如果需要设置的日志级别低于WARNING,则必须对logger对象使用setLevel(),根据具体情况选择logging.DEBUG或logging.INFO参数;当然也可以使用logging.basicConfig()方法配置全局信息,也能起到同样的效果,如下:
import logging
logging.basicConfig(level = logging.DEBUG)
logger = logging.getLogger(__name__)
formatter = logging.Formatter("【%(asctime)s %(levelname)s】 %(message)s")
# 构建一个StreamHandler,用于输出到终端
...(省略)
其他Handler
除FileHandler,StreamHandler外,logging模块还提供了其他的Handler(以下都包含在logging.handlers模块中)
- BaseRotatingHandler:基本的日志回滚方式
- RotatingHandler:日志回滚方式,支持日志文件最大数量和日志文件回滚
- TimeRotatingHandler:日志回滚方式,在一定时间区域内回滚日志文件
- SocketHandler:远程输出日志到TCP/IP sockets
- DatagramHandler:远程输出日志到UDP sockets
- SMTPHandler:远程输出日志到邮件地址
- SysLogHandler:日志输出到syslog
- NTEventLogHandler:远程输出日志到Windows NT/2000/XP的事件日志
- MemoryHandler:日志输出到内存中的指定buffer
- HTTPHandler:通过GET或POST远程输出到HTTP服务器
捕获Traceback
利用logging模块,也可以详细记录Traceback,方便我们查找问题
import logging
logger = logging.getLogger(__name__)
formatter = logging.Formatter(
"【%(asctime)s %(levelname)s】 %(message)s")
handler = logging.FileHandler("traceback.log")
handler.setFormatter(formatter)
logger.addHandler(handler)
try:
print(name)
except:
logger.error("error", exc_info=True)
运行结果如下:
配置共享
可以通过配置父模块的方法,让子模块也受益
"""
父模块:mainLog.py
"""
import logging
import dupLog
logger = logging.getLogger("father") # 生成一个logger,并为其命名“father”
logger.setLevel(logging.DEBUG)
handler = logging.FileHandler("my-log.txt")
formatter = logging.Formatter(
"【%(asctime)s %(levelname)s】 %(name)s: %(message)s ")
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.debug("debug")
logger.info("info")
logger.warning("warning")
dupLog.record_log() # 调用子模块的日志记录功能
"""
子模块:dupLog.py
"""
import logging
logger = logging.getLogger("father.son") # 生成一个logger,并为其命名“father.son”
# 记录日志函数
def record_log():
logger.info("info")
logger.error("error")
输出结果:
"""
日志文件:my-log.txt
"""
【2018-08-18 07:47:21,775 DEBUG】 father: debug
【2018-08-18 07:47:21,776 INFO】 father: info
【2018-08-18 07:47:21,776 WARNING】 father: warning
【2018-08-18 07:47:21,776 INFO】 father.son: info
【2018-08-18 07:47:21,776 ERROR】 father.son: error
这里需要注意的是,子模块的logger名称必须以父模块的logger名称开头,如上所示的父father,子father.son
文件配置
尽管设置配置信息的时候有logging.basciConfig()
,logging.getLogger().setLevel()
,logging.Formatter().setFormatter()
等方法,但语句始终镶嵌在代码里,不利于修改,也不利于阅读。因此logging模块提供了以加载配置文件的方式对日志功能进行配置
配置文件: log.conf
[loggers]
keys=root,log02
[handlers]
keys=hand01,hand02
[formatters]
keys=form01,form02
#################################################
[logger_root]
level=NOTSET
handlers=hand01
[logger_log02]
level=WARNING
handlers=hand02
propagate=1
qualname=log02
#################################################
[handler_hand01]
class=FileHandler
level=NOTSET
formatter=form01
args=("mine.log", "w")
[handler_hand02]
class=StreamHandler
level=WARNING
formatter=form02
args=(sys.stdout,)
#################################################
[formatter_form01]
format=[%(asctime)s %(levelname)s]-%(name)s-: %(message)s
datefmt=%Y/%m/%d %H:%M:%S
class=logging.Formatter
[formatter_form02]
format=%(asctime)s %(levelname)s: %(message)s
class=logging.Formatter
执行文件:test_log.py
import logging
import logging.config
logging.config.fileConfig("log.conf") # 加载配置文件
loggerRoot = logging.getLogger("root") # 获取root的配置
loggerLog02 = logging.getLogger("log02") # 获取log2的配置
loggerRoot.debug("debug")
loggerRoot.info("info")
loggerRoot.warning("warning")
loggerRoot.error("error")
loggerLog02.warning("warning")
loggerLog02.error("error")
运行结果如下:
- 终端
- mine.log
需要注意以下几点:
- 配置文件中,必须含有[loggers]
[handlers]
[formatters]
字段
- [loggers]
的keys中,必须含有root,并对其配置
- root会执行所有logger应该执行的debug(),info()...
等方法(所以上示mine.log文件中,有log02的日志信息)
- 更多详情可查看官方logging.config信息
文件配置还有另一种方法,利用logging.config.dictConfig()
方法加载配置信息,配置文件格式如下:
具体使用方法可参看官方文档说明,也可以看崔庆才的一篇关于使用logging的文章