首页 > 技术文章 > logging模块

youguanxinqing 2018-08-18 10:55 原文

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指定样子生成的,而asctimelevelnamelineno指定的是需要输出哪些内容,这里依次是:时间,日志等级,出现行号,以及日志内容;如果不看小括号中的内容,其实%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;如果同时设置了streamfilename参数,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 DEBUGfather: debug 
【2018-08-18 07:47:21,776 INFOfather: info 
【2018-08-18 07:47:21,776 WARNINGfather: warning 
【2018-08-18 07:47:21,776 INFOfather.son: info 
【2018-08-18 07:47:21,776 ERRORfather.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的文章

推荐阅读