必须脱敏密码、手机号、身份证号、银行卡号、邮箱地址、token、密钥等字段;优先通过重写logrecordfactory及getmessage()实现结构化解析脱敏,handler级仅作临时补救,第三方库日志需单独配置。

日志里哪些字段必须脱敏
密码、手机号、身份证号、银行卡号、邮箱地址、Token、密钥这些字段,只要出现在日志中,就属于高危明文,必须处理。不是“建议脱敏”,是“不脱敏就违规”。尤其在金融、政务、医疗类系统中,logging.info() 里直接拼接 user.phone 或 request.headers['Authorization'] 是典型雷区。
常见错误现象:WARNING:root:login failed for user 138****1234, pwd=123456 —— 这条日志同时泄露了脱敏不全的手机号和明文密码,审计一查一个准。
- 优先识别业务实体中的敏感字段,比如
User模型里的id_card、bank_account - HTTP 请求/响应体、Headers、Query Params 中的敏感键名(如
password、access_token)要统一拦截 - 避免只对固定字段名脱敏(比如只拦
password),有些接口用pwd、auth_key、secret也能传密钥
用 LogRecordFactory 替换默认日志工厂
Python 默认的 LogRecord 不会碰你传进去的 msg 字符串,它原样记录。想在写入前清洗内容,就得接管日志记录对象的生成过程——最稳妥的方式是重写 LogRecordFactory。
为什么这样做:比在每个 logger.info() 前手动调用脱敏函数更可靠,也比用 Handler 的 emit() 做字符串替换更早、更彻底(避免敏感内容已进缓冲区或被其他 Handler 拦截)。
立即学习“Python免费学习笔记(深入)”;
容易踩的坑:Logger.makeRecord() 是私有方法,不要重写它;也不要试图 monkey patch LogRecord.__init__,Python 3.12+ 已限制此类操作。
- 继承
logging.LogRecord,重写getMessage()方法,在其中对self.msg和self.args做结构化解析与脱敏 - 注册新工厂:调用
logging.setLogRecordFactory(YourSanitizedRecord),且必须在所有 logger 初始化前执行 - 注意
self.args可能是 dict、list 或嵌套结构,不能只做str.replace();推荐用json.dumps()+ 正则或递归遍历清洗
Handler 级脱敏只适合简单场景
如果项目已经上线、无法改日志工厂,或者只需要对特定输出渠道(比如只对文件日志脱敏,控制台保留原始)做处理,可以用自定义 Handler 在 emit() 阶段清洗。
性能影响明显:每条日志都要做一次字符串解析 + 正则匹配,高频日志服务下 CPU 开销会上升 10%~30%;而且若日志已被格式化为字符串(比如用了 %(message)s),再做关键词替换极易误伤(如把 “password_reset” 里的 “password” 也替了)。
- 仅适用于低频、非核心服务,或临时补救措施
- 务必在
Formatter.format()后、stream.write()前做脱敏,否则可能重复处理或漏处理 - 正则模式至少加词边界:
r'\bpassword\s*[:=]\s*["\']([^"\']*)["\']',避免匹配到单词片段
第三方库日志怎么管
像 requests、sqlalchemy、django.db 这些库自己打的日志,不会走你的 LogRecordFactory,除非它们显式调用你配置的 logger 实例。默认它们用的是各自模块名下的 logger(如 requests.packages.urllib3),你得单独配置。
最容易被忽略的一点:SQLAlchemy 的 echo=True 打印的 SQL 日志,如果带参数(WHERE phone = '13812345678'),根本不在 Python logging 体系内,而是直接 print 到 stderr —— 这类必须关掉或用 engine.echo = False + 改用 before_cursor_execute 事件钩子捕获并脱敏。
- 用
logging.getLogger('requests')单独获取并设置 handler / filter - 对
urllib3、boto3等底层库,优先设level=logging.WARNING,避免 INFO 级别泄露请求头 - Django 用户注意:
LOGGING配置里要显式包含'django.db.backends'并绑定脱敏 handler










