Python Logging模块

本文最后更新于:2021年10月22日 下午

Python Logging类

Python的Logging类是专门成立程序日志的类,能够方便的输出日志到屏幕、文件等多个地方,能够方便控制如何输出,还能够设置消息级别。

  • Logging.Logger:Logger是Logging模块的主体,为程序提供记录日志接口、判断级别、分配给handler。这个对象不能实例,应该通过getLogger()来获取。
  • Logging.Handler:Handler基于日志级别对日志进行分发,如设置为WARNING级别的Handler只会处理WARNING及以上级别的日志。
  • Logging.Filter:Filter是过滤器,可以提供更高级的自定义过滤方式。
  • Logging.Formatter: 这个类处理输出格式

Logging的结构图

日志级别

级别排序: CRITICAL > ERROR > WARNING > INFO > DEBUG

使用方法如下

1
2
3
4
5
6
import logging
logging.debug("张三")
logging.info("李四")
logging.warning("王五")
logging.error("小明")
logging.critical("小红")

log在控制台的输出

默认只显示WARING级别以上。下面代码实现自定义输出级别:

1
logging.basicConfig(level=logging.NOTSET)  # NOTSET是最低等级

注意!:This function does nothing if the root logger already has handlers configured, unless the keyword argument force is set to True.

若不使用force参数,则设置输出级别只有第一次有效。force=true能在执行其他参数指定的配置之前,将移除并关闭附加到根记录器的所有现有处理器。

有一个特殊的日志输出:

1
2
3
4
5
6
7
8
9
10
11
logger.exception("Failed to open sklearn.txt from logger.exception")
# 这个的输出是traceback的信息
#Failed to open sklearn.txt from logger.exception
#Traceback (most recent call last):
# File "G:\zhb7627\Code\Eclipse WorkSpace\PythonTest\test.py", line 23, in <module>
# open("sklearn.txt","rb")
#IOError: [Errno 2] No such file or directory: 'sklearn.txt'

# 也可以通过指定参数实现
# 下面这条语句和上面等价
logger.error("Faild to open sklearn.txt from logger.error",exc_info = True)

设置输出格式

格式有以下几种

  • %(levelno)s: 打印日志级别的数值
  • %(levelname)s: 打印日志级别名称
  • %(pathname)s: 打印当前执行程序的路径,其实就是sys.argv[0]
  • %(filename)s: 打印当前执行程序名,如:login.py
  • %(funcName)s: 打印日志的当前函数
  • %(lineno)d: 打印日志的当前行号,在第几行打印的日志
  • %(asctime)s: 打印日志的时间
  • %(thread)d: 打印线程ID
  • %(threadName)s: 打印线程名称
  • %(process)d: 打印进程ID
  • %(message)s: 打印日志信息
1
2
3
4
5
6
7
8
# 通过basicConfig来设置格式
# basicConfig是默认配置
# format是上面的格式字符串
# datefmt是和time.strftime()一样的格式字符串
logging.basicConfig(
format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s'
datefmt='%a, %d %b %Y %H:%M:%S'
)

注意输出格式在Windows下和Linux下都要符合命名规则。

日志输出到文件和控制台

接下来是高级用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import logging  # 引入logging模块
# 第一步,创建一个logger
logger = logging.getLogger()
logger.setLevel(logging.INFO) # Log等级总开关
# 第二步,创建一个FileHandler,用于写入日志文件
handler = logging.FileHandler("log.txt") # 文件名字
handler.setLevel(logging.INFO) # 单独handler的log等级设置
# 第三步,设置filehandler的格式
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
# 第四步,创建StreamHandler,用于输出到控制台
console = logging.StreamHandler()
console.setLevel(logging.INFO) # 单独handler的log等级设置
# 第六步,设置StreamHandler的格式
console.setFormatter(formatter)
# 第五步,添加handler到logger
logger.addHandler(handler)
logger.addHandler(console)

# 日志
logger.debug('this is a logger debug message')
logger.info('this is a logger info message')
logger.warning('this is a logger warning message')
logger.error('this is a logger error message')
logger.critical('this is a logger critical message')

logging.getLogger()

这个方法返回一个Logger对象,参数是Logger的名字,get相同名字会返回相同的logger,在不同模块要调用logger的时候永远都不需要传递logger参数,只需要使用这个方法即可。

示例:logging,getLogger("hahahaha")

Logger的名字可以体现继承关系,用.来分隔,子logger继承父logger的配置。

1
2
3
4
logging.getLogger("PythonApp")

logging.getLogger("PythonApp.Core") # 继承PythonApp的配置
logging.getLogger("PythonApp.Web")

logging.Filter

需要定义一个新的类来自定义过滤规则

1
2
3
4
5
6
7
8
9
10
class NoParsingFilter(logging.Filter):  # 继承logging.Filter
def filter(self, record): # 重写filter
if record.name == 'PythonApp' and record.levelno == logging.INFO:
return False # 返回True False来控制是否过滤
return True

logger = logging.getLogger('PythonApp')
logger.addFilter(NoParsingFilter())
logger.info("info")
logger.error("error")

record是logging.LogRecord类,有以下属性:

  • name logger的名字

  • levelno是级别

  • levelname是级别的字符串

  • pathname 是哪个文件输出的这行日志

  • lineno 是行号

  • msg 是日志本身

  • 除此以外还有formatter格式化字符串的所有属性

参考资料

python中logging日志模块详解 - 咸鱼也是有梦想的 - 博客园 (cnblogs.com)

使用Filter过滤python中的日志输出的实现方法