6.3 安全检索与最小复现
预计时间:15 分钟
完成标志:能构造脱敏查询,并交付别人可从干净目录运行的最小复现
日志保留了故障证据,却不等于根因已经清楚。查资料之前,先把原始记录留在受控位置:搜索框、公开 Issue 和聊天窗口都不是生产日志的默认去处。
搜不到的错
以这条数据库错误为例:
text
org.postgresql.util.PSQLException:
ERROR: duplicate key value violates unique constraint "users_pkey"如果盯着 duplicate 猜了半小时仍没有进展,可以搜索;但不要把生产日志当成公开文本。
第一步:在本地制作可分享副本
原始错误先留在受控位置。复制一份,再删除或替换:
- 密码、令牌、API 密钥、Cookie 和会话标识;
- 数据库连接串、私有仓库地址和内部主机名;
- 客户数据、个人信息、邮件、手机号和真实业务编号;
- 本地用户名、绝对路径、内网 IP;
- 与问题无关的请求正文、时间戳、随机 ID 和栈帧。
使用 <ACCESS_TOKEN>、<USER_ID> 这类占位符,而不是把值模糊到看不出字段含义。仅保留前后几位仍可能泄密。
第二步:构造能命中同类问题的查询
从下面这类稳定片段开始:
text
PSQLException duplicate key value violates unique constraint PostgreSQL 17 JDBC操作顺序:
- 保留异常类型和通用消息;
- 删除每次变化的 ID、路径、时间和行号;
- 只添加相关的语言、框架与确切版本;
- 先核对当前版本官方文档和项目 Issue,再参考社区回答;
- 在隔离环境用最小复现验证,别直接执行陌生的删除、权限或安装命令。
这条数据库消息证明唯一约束冲突,却没有证明是重复提交、序列不同步、主键生成错误还是残留测试数据。搜索结果只提供假设。
第三步:把五百行缩成可运行证据
把 Spring Boot、PostgreSQL、连接池、路由和缓存一起打包发出去,对方得先搭完整项目才有机会看见错误。先删掉与失败条件无关的部分,能省下大量准备工作。
一个合格的最小复现应包含:
| 内容 | 要写到什么程度 |
|---|---|
| 环境 | 操作系统、运行时、框架和相关依赖的确切版本 |
| 文件 | 只保留触发问题所需内容,附目录结构 |
| 准备步骤 | 从干净目录开始的完整命令 |
| 输入 | 最小、虚构或已脱敏的数据 |
| 复现步骤 | 每一步可复制,说明工作目录 |
| 预期结果 | 具体到输出、状态或返回值 |
| 实际结果 | 完整但已脱敏的错误和退出码 |
| 范围信息 | 最后正常版本、首次异常版本、已排除项 |
怎样缩减而不把 bug 一起删掉
- 先在副本中稳定复现原现象。
- 固定一条运行命令和一份输入。
- 每次只删除一个模块、配置或依赖。
- 每次都运行同一测试,记录仍能否复现。
- 用假实现替代数据库或 API;若真实边界正是条件,就保留它并提供安全的本地替代环境。
- 如果再删一步就不再复现,撤销最后这次缩减;交付的仍应是能复现的版本。
最小不等于“只有一行”,完整也不等于“整个仓库”。目标是保留导致现象的所有条件,删除其余变量。
一个可交付的 README 模板
markdown
# Reproduction: concise problem name
## Environment
- OS:
- Runtime:
- Framework / dependency versions:
## Run
1. From this directory, run: `...`
2. Observe the process exit code: `...`
## Expected
...
## Actual
...
## Notes
- Last known good version:
- First known bad version:
- Variables already ruled out:不要只发截图。文本便于检索、复制和辅助技术读取;截图可以补充界面状态,但不能替代命令、文本错误和文件。
提问前的安全检查
- [ ] 示例不含真实密钥、Cookie 或连接串;
- [ ] 用户与客户数据已换成虚构数据;
- [ ] 私有 URL、主机名和路径已替换;
- [ ] 问题在新目录或新容器中仍能重现;
- [ ] 锁文件或依赖版本信息已附上;
- [ ] 预期与实际结果分开写明;
- [ ] 你没有预设“肯定是某库的 bug”,只陈述观察结果。
离开这一页前
- 把示例数据库错误改写成不含项目标识的搜索查询;
- 从一个现有问题副本中逐项删除变量,保留每次运行记录;
- 让另一个人只看 README 就能复现;
- 能解释“搜索结果是候选原因,不是诊断结论”。
理论已经齐了。现在亲手捕获退出码、核对栈帧、检查日志,并处理一个常被讲错的 Java 依赖案例。