报错日志太长时,我也会想到让 AI 帮忙找问题。
但复制之前最好停一下。日志里除了异常堆栈,还可能混着用户邮箱、手机号、Authorization、API Key 和带 Token 的请求地址。
手动删除并不稳,日志一长就容易漏。更省事的办法,是先在本地跑一次脱敏脚本,只把处理后的副本交给 AI。
这个脚本处理什么

下面的脚本默认识别:
Authorization: Bearer ...;api_key、token、password、secret等字段;- URL 查询参数中的 Token;
- 邮箱地址;
- 中国大陆手机号。
它不会修改原始日志,也不会删除 Traceback、文件名、行号和异常类型。
完整代码
保存为 redact_log.py:
bash
from __future__ import annotations
import argparse
import re
from collections.abc import Callable
from pathlib import Path
Replacement = str | Callable[[re.Match[str]], str]
def mask_email(match: re.Match[str]) -> str:
local, domain = match.group(1), match.group(2)
visible = local[0] if local else "*"
return f"{visible}***@{domain}"
RULES: list[tuple[str, re.Pattern[str], Replacement]] = [
(
"authorization",
re.compile(
r"(?i)(\bAuthorization\s*[:=]\s*Bearer\s+)"
r"[A-Za-z0-9._~+/=-]{8,}"
),
r"\1***",
),
(
"url_secret",
re.compile(
r"(?i)([?&](?:api[_-]?key|access[_-]?token|"
r"token|secret)=)[^&\s]+"
),
r"\1***",
),
(
"named_secret",
re.compile(
r'''(?ix)
(?<![?&])
(?P<prefix>
["']?(?:api[_-]?key|access[_-]?token|
token|password|passwd|secret)["']?
\s*[:=]\s*
)
(?P<quote>["']?)
(?P<value>[^\s,&"'}]+)
(?P=quote)
'''
),
lambda m: (
f"{m.group('prefix')}"
f"{m.group('quote')}***{m.group('quote')}"
),
),
(
"email",
re.compile(
r"(?<![\w.+-])([\w.+-]+)@"
r"([A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)+)"
),
mask_email,
),
(
"cn_mobile",
re.compile(
r"(?<!\d)(1[3-9]\d)(\d{4})(\d{4})(?!\d)"
),
r"\1****\3",
),
]
def redact_text(text: str) -> tuple[str, dict[str, int]]:
counts: dict[str, int] = {}
result = text
for name, pattern, replacement in RULES:
result, count = pattern.subn(replacement, result)
if count:
counts[name] = count
return result, counts
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description=(
"Redact common secrets and personal data "
"from a UTF-8 log file."
)
)
parser.add_argument(
"input",
type=Path,
help="Source log file",
)
parser.add_argument(
"-o",
"--output",
type=Path,
required=True,
help="Redacted log file",
)
parser.add_argument(
"--overwrite",
action="store_true",
help="Allow replacing an existing output file",
)
return parser.parse_args()
def main() -> int:
args = parse_args()
source = args.input.expanduser().resolve()
output = args.output.expanduser().resolve()
if not source.is_file():
raise SystemExit(
f"Input file does not exist: {source}"
)
if source == output:
raise SystemExit(
"Input and output must be different files"
)
if output.exists() and not args.overwrite:
raise SystemExit(
f"Output already exists: {output} "
"(use --overwrite to replace it)"
)
if not output.parent.is_dir():
raise SystemExit(
f"Output directory does not exist: {output.parent}"
)
try:
original = source.read_text(encoding="utf-8")
except UnicodeDecodeError as exc:
raise SystemExit(
"Input is not valid UTF-8; "
"convert its encoding first"
) from exc
redacted, counts = redact_text(original)
output.write_text(redacted, encoding="utf-8")
total = sum(counts.values())
details = ", ".join(
f"{name}={count}"
for name, count in counts.items()
) or "none"
print(f"Redacted {total} item(s): {details}")
print(f"Wrote: {output}")
return 0
if __name__ == "__main__":
raise SystemExit(main())
脚本需要 Python 3.10 或更高版本,不依赖第三方包。
运行方法
bash
python redact_log.py app.log -o app.redacted.log
如果目标文件已经存在,需要明确允许覆盖:
bash
python redact_log.py app.log \
-o app.redacted.log \
--overwrite
假设原始日志是:
bash
Authorization: Bearer abc.def-123456789
email=alice.dev@example.com
phone=13812345678
api_key='sk-demo-123456'
url=https://example.com/run?token=query-secret&mode=debug
ValueError: request failed
处理后会变成:
bash
Authorization: Bearer ***
email=a***@example.com
phone=138****5678
api_key='***'
url=https://example.com/run?token=***&mode=debug
ValueError: request failed
可以看到,Token 被隐藏了,但 URL 后面的 mode=debug 仍然保留。异常类型、文件路径和行号也不会受到影响。
使用前还要注意
正则脱敏不是万能的。
项目可能使用自定义字段,例如 private_code、session_id 或业务内部密钥。遇到这种情况,需要继续往 RULES 中增加规则。
另外,脚本生成的是脱敏副本,不代表一定没有遗漏。真正发送给外部 AI 工具前,最好再人工搜索一次:
bash
password
token
secret
key
Authorization
如果日志涉及公司源码、用户数据或其他机密,即使经过正则脱敏,也不应该直接提交到未经确认的外部服务。
Python 官方文档建议复杂场景使用预编译正则对象;subn() 还能同时返回替换后的文本和替换次数,正好适合统计本次处理了多少项。Python [re](https://docs.python.org/3/library/re.html "re") 官方文档
如果想在程序输出日志时直接脱敏,可以继续把相同规则接入 logging.Filter。过滤器可以在 Handler 输出日志前检查或修改记录。Python [logging](https://docs.python.org/3/library/logging.html#filter-objects "logging") 官方文档
如果平时长期使用 ChatGPT、Claude、Cursor 或 Kiro,也可以了解 gpt68.com。它是第三方 AI 会员充值平台,使用前应看清套餐说明、账号要求和售后规则。工具能不能写代码是一方面,提交给工具的内容是否安全,同样值得注意。