python logging模块Filters过滤器介绍,如何使用自定义的过滤器

python的logging模块中,Filters 可被 HandlersLoggers 用来实现比按层级提供更复杂的过滤操作。

一、在代码中定义及使用Filter

先给一个直接使用的示例:

import logging

logger = logging.getLogger('dev')

hdl = logging.StreamHandler()
fmt = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
hdl.setFormatter(fmt)

# 重写过滤器类
class MyFilter(logging.Filter):
    def __init__(self, name):
        super().__init__(name=name)

    def filter(self, record):
        if record.levelno <= logging.WARNING:
            return True
        return False

flt = MyFilter('dev')
# 将过滤器应用到hdl处理程序
hdl.addFilter(flt)
logger.addHandler(hdl)
# 将过滤器应用到logger日志器
# logger.addFilter(flt)

logger_test1 = logging.getLogger('dev.test1')

logger.debug("This is a debug log.")
logger.info("This is an info log.")
logger.warning("This is a warning log.")
logger.error("This is an error log.")
logger.critical("This is a critical log.")

logger_test1.debug("This is a debug log.")
logger_test1.info("This is an info log.")
logger_test1.warning("This is a warning log.")
logger_test1.error("This is an error log.")
logger_test1.critical("This is a critical log.")

运行代码,输出:

2023-08-20 22:49:40,849 - dev - WARNING - This is a warning log.
2023-08-20 22:49:40,849 - dev.test1 - WARNING - This is a warning log.

解释:

  • 定义了一个MyFilter类,根据filter(record) 返回值决定是否要记录指定的记录?返回零表示否,非零表示是。示例中表示过滤掉高于logging.WARNING级别的日志。实例化MyFilter时,传了一个dev参数,它表示将对dev名称的日志器生效,然后将过滤器应用到处理程序hdl,然后hdl应用到logger日志器。
  • 输出结果中只有WARNING级别的日志,这是因为没有给loggerlogger_test1单独设置level级别,因此它默认使用了root根日志级别WARNING,因此WARNING以下级别的日志给过滤掉了,而自定义的过滤器,把高于WARNING级别的日志给过滤掉了,所以只剩下WARNING级别的日志。
  • 另外,你会发现,虽然没有给logger_test1配置处理程序、过滤器、格式化器等,但是它最终使用的是logger相同的配置,这是因为logger_test1 = logging.getLogger('dev.test1')dev.test1使用了日志器的层级关系。基本过滤器类只允许低于日志记录器层级结构中低于特定层级的事件。 例如,一个用 ‘A.B’ 初始化的过滤器将允许 ‘A.B’, ‘A.B.C’, ‘A.B.C.D’, ‘A.B.D’ 等日志记录器所记录的事件。 但 ‘A.BB’, ‘B.A.B’ 等则不允许。

将示例中的代码做一个小的改动:把hdl.addFilter(flt)注释掉,把# logger.addFilter(flt)取消注释,然后重新运行代码。

输出结果:

2023-08-20 23:16:49,507 - dev - WARNING - This is a warning log.
2023-08-20 23:16:49,507 - dev.test1 - WARNING - This is a warning log.
2023-08-20 23:16:49,507 - dev.test1 - ERROR - This is an error log.
2023-08-20 23:16:49,507 - dev.test1 - CRITICAL - This is a critical log.

解释:

  • 会发现,自定义的过滤器,不再对logger_test1日志器生效了。

    这是因为关联到处理程序的过滤器会在事件由处理程序发出之前被查询,而关联到日志记录器的过滤器则会在有事件被记录的的任何时候(使用 debug(), info()等等)在将事件发送给处理程序之前被查询。 这意味着由后代日志记录器生成的事件将不会被日志记录器的过滤器设置所过滤,除非该过滤器也已被应用于后代日志记录器。

二、logging.Filter 类filter(record)方法

在自定义过滤器时,filter(record)方法中,record有哪些属性可用呢?

下面写了一段代码,来探究一下(当然更推荐你去看logging模块的源码):

import logging
import time

class MyFilter(logging.Filter):
    def filter(self, record):
        time.sleep(1)
        for i in dir(record):
            if not i.startswith("__"):
                print(i,'=',eval('record.'+i))
        print('getMessage()','=',record.getMessage())
        return True

def mylogger():
    logger = logging.getLogger()
    fmt = logging.Formatter(fmt="%(asctime)s - %(message)s",datefmt="%Y-%m-%d %H:%M:%S")
    hld = logging.StreamHandler()
    hld.setFormatter(fmt)

    logger.addFilter(MyFilter())
    logger.addHandler(hld)
    return logger

if __name__ == '__main__':
    logger = mylogger()

    def myfunc():
        try:
            ttt = 1/0
        except Exception as err:
            logger.exception('The args: %s,%s','arg1','arg2',exc_info=True,stack_info=True)

    time.sleep(1)
    myfunc()

运行代码,输出:

2023-08-20 23:32:11 - The args: arg1,arg2
Traceback (most recent call last):
  File "E:/Chen/python3/ExciseA/config/test.py", line 49, in myfunc
    ttt = 1 / 0
ZeroDivisionError: division by zero
Stack (most recent call last):
  File "E:/Chen/python3/ExciseA/config/test.py", line 55, in <module>
    myfunc()
  File "E:/Chen/python3/ExciseA/config/test.py", line 51, in myfunc
    logger.exception('The args: %s,%s', 'arg1', 'arg2', exc_info=True, stack_info=True)
args = ('arg1', 'arg2')
created = 1692554731.9882045
exc_info = (<class 'ZeroDivisionError'>, ZeroDivisionError('division by zero'), <traceback object at 0x0000018C67FC61C8>)
exc_text = None
filename = test.py
funcName = myfunc
getMessage = <bound method LogRecord.getMessage of <LogRecord: root, 40, E:/Chen/python3/ExciseA/config/test.py, 51, "The args: %s,%s">>
levelname = ERROR
levelno = 40
lineno = 51
module = test
msecs = 988.2044792175293
msg = The args: %s,%s
name = root
pathname = E:/Chen/python3/ExciseA/config/test.py
process = 13384
processName = MainProcess
relativeCreated = 1015.1534080505371
stack_info = Stack (most recent call last):
  File "E:/Chen/python3/ExciseA/config/test.py", line 55, in <module>
    myfunc()
  File "E:/Chen/python3/ExciseA/config/test.py", line 51, in myfunc
    logger.exception('The args: %s,%s', 'arg1', 'arg2', exc_info=True, stack_info=True)
thread = 10072
threadName = MainThread
getMessage() = The args: arg1,arg2
属性名称描述
args合并到 msg 以产生 message 的包含参数的元组,或是其中的值将被用于合并的字典(当只有一个参数且其类型为字典时)。
createdLogRecord 被创建的时间(即time.time()的返回值)。
exc_info异常元组 (例如 sys.exc_info) 或者如未发生异常则为 None
exc_text用来缓存traceback文本内容的,好像写死为了None
filename发生日志记录调用所在的文件的名称,pathname 的文件名部分
funcName日志记录调用所在的函数
levelname消息文本记录级别 ('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL').
levelno消息数字记录级别 (DEBUG, INFO, WARNING, ERROR, CRITICAL).
lineno发出日志记录调用所在的源行号(如果可用)。
module模块 (filename 的名称部分)。
msecs日志被创建的时间的毫秒部分。
msg在原始日志记录调用中传入的格式字符串。 与 args 合并以产生 message(注意:record属性中没有直接提供message,可以通过record.getMessage()间接获取)
name用于记录调用的日志记录器名称。
pathname发出日志记录调用的源文件的完整路径名(如果可用)。
process进程ID(如果可用)
processName进程名(如果可用)
relativeCreated以毫秒数表示的LogRecord被创建的时间,即相对于 logging 模块被加载时间的差值。
stack_info当前线程中从堆栈底部起向上直到包括日志记录调用并导致创建此记录的堆栈帧的堆栈帧信息(如果可用)。
thread线程ID(如果可用)
threadName线程名(如果可用)

getMessage()在将LogRecord实例与任何用户提供的参数合并之后,返回此实例的消息。 如果用户提供给日志记录调用的消息参数不是字符串,则会在其上调用str()以将它转换为字符串。

三、在配置文件中配置自定义的Filter

日志模块化配置通常是更好的实践方式,接下来将举例如何在配置文件中配置自定义的过滤器。

大致思路是:

1)在配置文件中配置过滤器(这里选yaml格式配置文件);

2)将自定义的过滤器,单独放到一个模块中供调用;

3)通过logging.config.dictConfig()加载日志配置文件;

4)使用添加了过滤器的日志器

示例项目目录结构:

.
│ main.py
└─config
    │ myfilter.py	
    │ logger.py
    └─logconf
        log.yaml

log.yaml

version: 1
disable_existing_loggers: false
formatters:
  simple:
    format: '[%(asctime)s - %(name)s - %(levelname)-8s] %(message)s'
handlers:
  console:
    class: logging.StreamHandler
    formatter: simple
    level: DEBUG
    stream: ext://sys.stdout
    filters:
      - myfilter
loggers:
  mylogger:
    level: DEBUG
    handlers:
      - console
    propagate: no
#    filters: [myfilter]
filters:
  myfilter:
    (): config.myfilter.MyFilter
    name: mylogger
    param1: False
    param2: True

log.yamlfilters内配置了一个ID为myfilter的过滤器,其中()键表明config.myfilter.MyFilter是一个自定义的类对象,nameparam1param2均是传递给该自定义过滤器类实例化时的参数。然后将该过滤器应用到了handlersconsole处理程序。

myfilter.py

import logging

class MyFilter(logging.Filter):

    def __init__(self, name, param1=True, param2=True):
        super().__init__(name=name)
        self.param1 = param1
        self.param2 = param2

    def filter(self, record):
        allow = False
        if self.param1 == False:
            allow = allow or record.levelno < logging.ERROR
        if self.param2 == True and record.levelname == 'WARNING':
            record.msg = 'This WARNING messages modified by my filter.'+record.msg
        return allow

myfilter.py 自定义一个简单的过滤器,该过滤器功能:当param1=False时,仅返回低于logging.ERROR级别的日志记录;当param2=True且日志级别为WARNING时,修改msg信息。

logger.py

import logging.config
import yaml
import os

class Logger():

    def __init__(self,logger_name=''):
        self.logger_name = logger_name
        cur_dir = os.path.dirname(__file__)
        log_conf = os.path.join(cur_dir, 'logconf', 'flog.yaml')
        with open(log_conf, 'r') as f:
            config = yaml.safe_load(f.read())
            logging.config.dictConfig(config)

    def logger(self):
        logger = logging.getLogger(self.logger_name)
        return logger

logger.py 通过config=yaml.safe_load(f.read())读取log.yaml日志配置文件,logging.config.dictConfig(config)加载日志配置文件,完成初始化日志配置。logger()方法返回一个应用了配置的logger日志器。

main.py

from config.logger import Logger

logger = Logger('mylogger').logger()

if __name__ == '__main__':
    logger.debug("dddebug")
    logger.info("iiinfo")
    logger.warning("wwwarning")
    logger.error("eeerror")
    logger.critical("cccritical")

main.py 使用logger.py中封装的方法,实例化了一个logger日志器,供日志记录使用。

运行main.py,输出:

[2023-08-21 00:32:43,810 - mylogger - DEBUG   ] dddebug
[2023-08-21 00:32:43,810 - mylogger - INFO    ] iiinfo
[2023-08-21 00:32:43,810 - mylogger - WARNING ] This WARNING messages modified by my filter.wwwarning

从运行结果看,过滤器配置已生效。

最后:

  • logging.config.dicConfig() 可以支持Filters过滤器配置,logging.config.fileConfig() 不支持.
  • 在3.2版更改: 不需要创建专门的Filter类,或使用具有filter方法的其他类:你可以使用一个函数(或其他可调用对象)作为过滤器。 过滤逻辑将检查过滤器对象是否文化的filter属性:如果有,就会将它当作是Filter并调用它的filter()方法。


参考资料:

字典配置,用户定义对象

利用 dictConfig() 定义过滤器

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: Python logging 模块Python 标准库中的一个模块,用于记录程序运行时的日志信息。使用 logging 模块可以方便地记录程序的运行状态,以便在出现问题时进行排查。 使用 logging 模块需要先导入模块,然后创建一个 logger 对象,设置日志级别和输出格式,最后在程序中使用 logger 对象记录日志信息。 下面是一个简单的示例代码: ``` import logging # 创建 logger 对象 logger = logging.getLogger('mylogger') # 设置日志级别 logger.setLevel(logging.DEBUG) # 创建一个输出到控制台的 handler console_handler = logging.StreamHandler() console_handler.setLevel(logging.DEBUG) # 创建一个输出到文件的 handler file_handler = logging.FileHandler('mylog.log') file_handler.setLevel(logging.INFO) # 设置输出格式 formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') console_handler.setFormatter(formatter) file_handler.setFormatter(formatter) # 将 handler 添加到 logger 对象中 logger.addHandler(console_handler) logger.addHandler(file_handler) # 记录日志信息 logger.debug('debug message') logger.info('info message') logger.warning('warning message') logger.error('error message') logger.critical('critical message') ``` 在上面的示例代码中,我们创建了一个名为 `mylogger` 的 logger 对象,并设置了日志级别为 `DEBUG`。然后创建了一个输出到控制台的 handler 和一个输出到文件的 handler,并设置了输出格式。最后将这两个 handler 添加到 logger 对象中。 在程序中使用 logger 对象记录日志信息时,可以使用 `debug()`、`info()`、`warning()`、`error()`、`critical()` 等方法,分别对应不同的日志级别。例如,`logger.debug('debug message')` 就会记录一条 DEBUG 级别的日志信息。 以上就是 Python logging 模块的基本使用方法。 ### 回答2: Pythonlogging模块是一个强大的日志记录工具。它提供了一种灵活的方式来在程序中记录日志,以便在程序运行过程中获取有用的信息和诊断数据。下面是如何使用Python logging模块的基本步骤。 1. 导入logging模块: ```python import logging ``` 2. 配置日志记录器: ```python logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)s %(message)s', filename='app.log', filemode='w') ``` 在配置方法中,level参数设置日志级别,可选项包括DEBUG、INFO、WARNING、ERROR和CRITICAL。format参数定义日志记录的格式,其中asctime表示日志记录的时间,levelname表示日志级别,message表示要记录的消息。filename参数指定日志文件的名称,filemode参数定义日志写入模式,例如'w'表示写入模式,'a'表示追加模式。 3. 记录日志: ```python logging.debug('Debug message') logging.info('Info message') logging.warning('Warning message') logging.error('Error message') logging.critical('Critical message') ``` 使用logging模块中的不同方法来记录不同级别的日志。 4. 进一步配置日志记录器: ```python logger = logging.getLogger('my_logger') logger.setLevel(logging.DEBUG) ``` 如果需要更多的配置选项,可以创建一个logger对象,并使用setLevel方法设置日志级别。 通过这些步骤,可以在程序中使用logging模块记录日志。这样,可以根据需要记录不同级别的日志,并通过指定的格式将日志写入到文件中。日志记录对于程序的调试和问题排查非常有帮助。 ### 回答3: Python中的logging模块是一个内置的日志记录工具。它提供了一种简单而灵活的方式来记录运行时的信息,帮助我们调试程序和追踪错误。下面是使用logging模块的一些主要步骤: 1. 导入logging模块: ```python import logging ``` 2. 设置日志记录的级别: ```python logging.basicConfig(level=logging.DEBUG) ``` 这里设置的是日志记录的最低级别为DEBUG,即记录所有级别的日志信息。还有其他的级别可选择,如:INFO、WARNING、ERROR等,可以根据需要设置。 3. 编写日志信息: ```python logging.debug("This is a debug message") logging.info("This is an info message") logging.warning("This is a warning message") logging.error("This is an error message") ``` 4. 输出日志信息: 日志信息可以输出到控制台、文件或其他地方。默认情况下,日志信息会输出到控制台。 ```python logging.debug("This is a debug message") ``` 可以通过配置logging模块将日志信息输出到文件。 ```python logging.basicConfig(filename='example.log', level=logging.DEBUG) ``` 这里将日志信息输出到文件"example.log"中。 以上是使用logging模块的基本步骤。logging模块还支持更高级的用法,如添加时间戳、设置日志的格式等。可以通过查阅官方文档来了解更多关于logging模块的详细信息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小青龍

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值