pin_drop当前位置:知识文库 ❯ 图文
自定义Filter详解 - Python日志过滤与上下文注入
一、自定义过滤器概述
Filter 是 logging 模块中用于精细控制日志输出的组件。与日志级别的粗粒度过滤不同,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)参数说明
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 添加自定义属性时,确保格式字符串中引用了该属性,否则添加的属性不会出现在日志输出中。
六、过滤方式对比
小贴士
在 Web 应用中,可以使用 Filter 结合 threading.local 或 contextvars 为每个 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.msg 或 record.args,将敏感信息替换为 ***,然后返回 True。这样日志会输出但敏感内容被脱敏。
本文涉及AI创作
内容由AI创作,请仔细甄别