pin_drop当前位置:知识文库 ❯ 图文

自定义Filter详解 - Python日志过滤与上下文注入

一、自定义过滤器概述

Filterlogging 模块中用于精细控制日志输出的组件。与日志级别的粗粒度过滤不同,Filter 可以根据任意条件决定是否输出日志记录,例如根据 Logger 名称、消息内容、特定属性等进行过滤。

通过自定义 Filter 子类并重写 filter() 方法,可以实现各种复杂的日志过滤逻辑,满足业务特定的日志需求。

二、语法与参数说明

代码示例

import logging

# 内置Filter(按Logger名称过滤)
filter_obj = logging.Filter(name='app.database')

# 自定义Filter
class CustomFilter(logging.Filter):
    def filter(self, record):
        # 返回True表示输出,False表示过滤
        return True

# 添加到Logger或Handler
logger.addFilter(filter_obj)
handler.addFilter(filter_obj)

参数说明

参数/方法 说明
name Filter名称,用于匹配Logger名称
filter(record) 过滤方法,返回True输出日志,False过滤掉
name 属性 Filter的名称属性

Filter.filter() 方法返回布尔值:True 表示允许日志通过,False 表示过滤掉该日志。

三、代码示例

示例1:按 Logger 名称过滤

使用内置的 logging.Filter(name) 可以按 Logger 名称进行过滤,只允许指定名称及其子 Logger 的日志通过。

代码示例

import logging

logger = logging.getLogger("app")
logger.setLevel(logging.DEBUG)
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter('[%(name)s] %(message)s'))
logger.addHandler(handler)

# 只允许app.database的日志通过
db_filter = logging.Filter("app.database")
handler.addFilter(db_filter)

# 不同子Logger的日志
db_logger = logging.getLogger("app.database")
api_logger = logging.getLogger("app.api")
auth_logger = logging.getLogger("app.auth")

db_logger.info("数据库日志 - 会显示")
api_logger.info("API日志 - 被过滤")
auth_logger.info("认证日志 - 被过滤")
db_logger.warning("数据库警告 - 会显示")

输出:

代码示例

[app.database] 数据库日志 - 会显示
[app.database] 数据库警告 - 会显示

示例2:自定义 Filter 过滤敏感内容

通过继承 logging.Filter 并重写 filter() 方法,可以实现基于日志内容的过滤逻辑,如过滤敏感信息。

代码示例

import logging

class SensitiveFilter(logging.Filter):
    """过滤敏感信息"""

    SENSITIVE_WORDS = ["密码", "password", "token", "secret"]

    def filter(self, record):
        msg = record.getMessage().lower()
        for word in self.SENSITIVE_WORDS:
            if word.lower() in msg:
                return False
        return True

class LevelRangeFilter(logging.Filter):
    """只允许指定级别范围的日志"""

    def __init__(self, min_level, max_level):
        super().__init__()
        self.min_level = min_level
        self.max_level = max_level

    def filter(self, record):
        return self.min_level <= record.levelno <= self.max_level

# 使用敏感信息过滤器
logger = logging.getLogger("secure")
logger.setLevel(logging.DEBUG)
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter('[%(levelname)s] %(message)s'))
handler.addFilter(SensitiveFilter())
logger.addHandler(handler)

logger.info("用户登录成功")
logger.info("密码验证通过")  # 被过滤
logger.warning("token即将过期")  # 被过滤
logger.error("数据库连接失败")
logger.info("secret_key已加载")  # 被过滤

输出:

代码示例

[INFO] 用户登录成功
[ERROR] 数据库连接失败

示例3:给 LogRecord 添加自定义属性

Filter 不仅可以过滤日志,还可以在 filter() 方法中为 LogRecord 对象添加自定义属性,从而在日志格式中引用这些属性。

代码示例

import logging

class ContextFilter(logging.Filter):
    """为日志记录添加上下文信息"""

    def __init__(self, app_name="", user_id=""):
        super().__init__()
        self.app_name = app_name
        self.user_id = user_id

    def filter(self, record):
        record.app_name = self.app_name
        record.user_id = self.user_id
        return True

# 使用上下文过滤器
logger = logging.getLogger("context_demo")
logger.setLevel(logging.INFO)
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter(
    '%(asctime)s [%(app_name)s] [用户:%(user_id)s] %(message)s',
    datefmt='%H:%M:%S'
))

# 添加过滤器
ctx_filter = ContextFilter(app_name="MyApp", user_id="user_001")
handler.addFilter(ctx_filter)
logger.addHandler(handler)

logger.info("执行操作A")
logger.warning("资源不足")
logger.info("操作完成")

输出:

代码示例

10:30:00 [MyApp] [用户:user_001] 执行操作A
10:30:00 [MyApp] [用户:user_001] 资源不足
10:30:00 [MyApp] [用户:user_001] 操作完成

四、实际应用场景

  • 敏感信息过滤:在日志中过滤掉密码、token、密钥等敏感信息,防止信息泄露。

  • 上下文信息注入:通过 Filter 为日志记录添加请求ID、用户ID、应用名称等上下文信息,便于日志追踪。

  • 业务逻辑过滤:根据业务需求过滤特定类型的日志,如只记录特定模块的日志、只记录异常日志等。

五、注意事项

注意1:Filter 可以添加到 Logger 或 Handler 上。添加到 Logger 上会影响该 Logger 的所有 Handler,添加到 Handler 上只影响该 Handler。

注意2:内置 logging.Filter(name) 的过滤逻辑是:如果 name 为空,允许所有日志通过;如果 name 非空,只允许该 name 及其子 Logger 的日志通过。

注意3:自定义 Filter 的 filter() 方法应尽量轻量,避免在过滤逻辑中执行耗时操作,否则会影响日志性能。

提示:通过 Filter 添加自定义属性时,确保格式字符串中引用了该属性,否则添加的属性不会出现在日志输出中。

六、过滤方式对比

对比项 Filter Level过滤 Handler级别 条件判断
过滤粒度 最细
自定义条件 支持 不支持 不支持 支持
添加属性 支持 不支持 不支持 不支持
复用性
适用场景 复杂过滤 级别控制 目标控制 简单条件

小贴士

在 Web 应用中,可以使用 Filter 结合 threading.localcontextvars 为每个 HTTP 请求自动注入请求ID,实现完整的请求链路追踪。这是微服务架构中常用的日志追踪方案。

七、常见问题

Filter 添加到 Logger 和添加到 Handler 上有什么区别?

添加到 Logger 上时,该 Filter 会影响该 Logger 的所有 Handler(包括传播到父 Logger 的日志);添加到 Handler 上时,该 Filter 只影响这一个 Handler 的输出。如果你只想在某个特定的输出目标上过滤,应添加到 Handler 上。

如何使用正则表达式过滤日志?

可以自定义一个 RegexFilter 类,在 filter() 方法中使用 re 模块匹配日志消息。例如:return bool(re.search(self.pattern, record.getMessage()))

可以为同一个 Logger 添加多个 Filter 吗?

可以。当有多个 Filter 时,日志必须通过所有 Filter 的 filter() 检查(都返回 True)才会被输出。这相当于多个 Filter 之间是逻辑与(AND)的关系。

如何替换日志中的敏感信息而不是直接过滤掉?

可以在自定义 Filter 的 filter() 方法中修改 record.msgrecord.args,将敏感信息替换为 ***,然后返回 True。这样日志会输出但敏感内容被脱敏。


标签: Python 自定义过滤器 Filter 日志过滤 敏感信息脱敏 上下文注入

本文涉及AI创作

内容由AI创作,请仔细甄别

list快速访问

上一篇: logging.config详解 - Python日志dictConfig高级配置 下一篇: 日志传播机制详解 - Python Logger propagate属性配置

poll相关推荐