自动化交付

排查文档失败需要日志,但不需要把全文打进日志

定位翻译失败需要日志,但不需要把整段合同、身份证号码或下载令牌打印出来。日志应该回答“哪一个任务、在哪一步、为什么失败”,而不是成为第二份缺乏权限管理的文档库。

阅读文本 / Reading text
本文使用合成样本演示处理方法。代码已通过隔离环境中的示例检查;这不代表对所有真实文件或产品效果的保证。处理重要文件前,请保留原件,并按文中的边界与验收项复核。 定位翻译失败需要日志,但不需要把整段合同、身份证号码或下载令牌打印出来。日志应该回答“哪一个任务、在哪一步、为什么失败”,而不是成为第二份缺乏权限管理的文档库。 从允许记录的字段开始 黑名单容易漏掉新出现的敏感字段。更稳妥的是只输出经过设计的字段,例如内部任务 ID、阶段、错误类别和耗时。下面的合成例子使用白名单投影,输入里的全文和令牌不会进入日志。 验证日志没有顺手带出原文 import io import json import logging ALLOWED = ("job_id", "stage", "error_code", "elapsed_ms") def log_record(data): return {key: data[key] for key in ALLOWED if key in data} stream = io.StringIO() logger = logging.getLogger("synthetic-document-job") logger.handlers.clear() logger.propagate = False logger.setLevel(logging.INFO) handler = logging.StreamHandler(stream) logger.addHandler(handler) event = {"job_id": "demo-12", "stage": "parse", "error_code": "BAD_CONTAINER", "elapsed_ms": 42, "document_text": "synthetic confidential clause", "access_token": "synthetic-secret"} logger.info(json.dumps(log_record(event), sort_keys=True)) logged = json.loads(stream.getvalue()) assert logged["error_code"] == "BAD_CONTAINER" assert "document_text" not in logged assert "synthetic-secret" not in stream.getvalue() assert set(logged) <= set(ALLOWED) logger.removeHandler(handler) 示例仅修改自己命名的演示 logger,不建议在业务库里清空应用全局 handler。字段值仍需验证长度和类型;如果把用户全文塞进 error_code ,白名单名称本身救不了你。 关联 ID 代替敏感内容 想排查的问题 推荐记录 不推荐记录 哪个任务失败 随机内部任务 ID 用户完整文件名及路径 哪一阶段耗时 阶段与耗时 每段源文和译文 上游为何拒绝 规范化错误类别 带认证头的完整请求 如何找到原件 受控系统内的引用 可永久公开下载的链接 保留期与访问范围同样重要 即使没有正文,任务时间、语言和文件大小组合起来也可能透露业务信息。日志应有访问控制、保留期限和删除流程。排障临时提高日志级别时,还要明确何时恢复,避免调试模式长期留在生产。 翻译集成也能有清楚而克制的日志 对接 OmniDoc(产品入口) 时,记录本地任务与远端任务之间的关联即可,不应为排查方便把用户上传文件复制进日志。好的问题报告能说明失败阶段和复现条件,同时保持客户文档只在被授权的处理路径里流动。 参考: Python logging 。 - 1 -
更新于 2026-10-08