You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Python日志框架中屏蔽请求URL中的敏感密码信息?

问题:日志中意外记录了URL里的敏感密码

先看示例代码:

try:
    r = requests.get('https://sensitive:passw0rd@what.ever/')
    r.raise_for_status()
except requests.HTTPError:
    logging.exception("Failed to what.ever")

当端点返回非成功HTTP状态码时,日志会输出包含密码的内容:

Traceback (most recent call last):
  File "a.py", line 5, in <module>
    r.raise_for_status()
  File "venv/lib/python3.5/site-packages/requests/models.py", line 928, in raise_for_status
    raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 404 Client Error: Not Found for url: https://sensitive:passw0rd@what.ever/

问题在于密码被直接记录到了日志中,虽然可以用logging filter过滤整行,但更理想的是直接屏蔽密码。由于logging.exception没有传入字符串,在应用侧过滤比较棘手,想知道在logging框架中何处可以转换日志记录?


解决方案

我来帮你梳理几个在logging框架里实现日志内容转换的实用方法,从源头到输出环节都有对应的方案:

1. 自定义LogRecord工厂:从创建日志记录时就处理

Python的logging允许替换默认的LogRecord工厂函数,在日志记录刚被创建的时候就修改敏感内容,这样后续所有处理环节拿到的都是已经脱敏的内容:

import logging
import re

def sanitized_record_factory(*args, **kwargs):
    # 创建原始的LogRecord
    record = logging.LogRecord(*args, **kwargs)
    # 定义匹配URL中密码部分的正则
    url_pattern = re.compile(r'(https?://[^:]+):[^@]+@')
    # 处理日志消息
    if record.msg:
        record.msg = url_pattern.sub(r'\1:***@', record.msg)
    # 处理异常栈文本
    if record.exc_text:
        record.exc_text = url_pattern.sub(r'\1:***@', record.exc_text)
    return record

# 替换默认的工厂函数
logging.setLogRecordFactory(sanitized_record_factory)

2. 自定义Filter:灵活修改LogRecord

Filter不仅能过滤日志,还可以直接修改可变的LogRecord对象,你可以针对特定日志器添加这个过滤,不会影响全局:

import logging
import re

class SanitizePasswordFilter(logging.Filter):
    def filter(self, record):
        url_pattern = re.compile(r'(https?://[^:]+):[^@]+@')
        # 处理日志消息
        if hasattr(record, 'msg') and record.msg:
            record.msg = url_pattern.sub(r'\1:***@', record.msg)
        # 处理异常栈文本
        if hasattr(record, 'exc_text') and record.exc_text:
            record.exc_text = url_pattern.sub(r'\1:***@', record.exc_text)
        # 返回True表示保留这条日志(如果返回False就会被过滤掉)
        return True

# 给根日志器添加过滤器
root_logger = logging.getLogger()
root_logger.addFilter(SanitizePasswordFilter())

3. 自定义Formatter:聚焦日志输出环节的处理

Formatter是负责把LogRecord转换成最终字符串的环节,你可以重写它的异常格式化方法,专门处理异常栈里的敏感URL:

import logging
import re

class SanitizedExceptionFormatter(logging.Formatter):
    def formatException(self, exc_info):
        # 获取原始的异常栈文本
        original_exc_text = super().formatException(exc_info)
        # 替换密码部分
        url_pattern = re.compile(r'https?://[^:]+:[^@]+@')
        return url_pattern.sub(r'https://***:***@', original_exc_text)

# 给日志处理器设置这个Formatter
console_handler = logging.StreamHandler()
console_handler.setFormatter(SanitizedExceptionFormatter())
logging.getLogger().addHandler(console_handler)

额外建议:从源头避免密码出现在URL里

其实最彻底的办法是不要在URL里直接携带密码,requests支持通过auth参数传递认证信息,这样即使触发异常,日志里的URL也不会包含敏感内容:

# 用auth参数替代URL里的密码
r = requests.get('https://what.ever/', auth=('sensitive', 'passw0rd'))

内容的提问来源于stack exchange,提问作者vidstige

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:00:13