系列:Spring AI + MCP------大数据智能运维实战
本篇目标:按准确对象和时间范围查日志,处理分页、采集缺口与消息截断,让日志成为可引用的证据。
阅读基础:第七、八篇的运行身份与引擎关联;沿用
lab-07-12和相同技术版本。案例说明:本文日志全部为手工编写的虚构样本,包含用于测试的无效占位值,不读取真实平台日志。
1. 找到一个 timeout,就算定位了吗
"找到了,日志里有 timeout!"小周把一行 WARN 发给小林。
这一行发生在 09:20,内容是轮询响应超时。
"它是查询接口超时,还是任务执行失败?"小林问。
小周看了看后面的记录:09:30 应用还在 QUEUED,09:39 又记录了队列运行名额已满。
"看来这一行不能直接说明任务失败。"
让助手查日志,最容易得到的是一句像结论的话:"发现超时,建议检查网络。"真正有用的回答却需要更多信息:这行日志属于哪次尝试,前后发生了什么,日志是否完整,还有哪些解释尚未排除。
本篇继续查看 run-current-0917/a1,观察时刻是 2026-09-17 09:40 +08:00。我们给助手的能力是限定范围查询日志,而不是让它随意读取机器文件。
2. 查询之前,先把范围收紧
新增工具 ops_query_logs 支持两类范围:
| 范围 | 必需对象 | 适合回答的问题 |
|---|---|---|
| RUN | 精确 runId;可选该运行内的 attemptId | 这次运行、这次尝试发生了什么 |
| NODE | 精确 nodeId | 该节点在限定时段里记录了什么 |
每次查询还必须指定开始时间、结束时间和每页条数。教学限制是窗口最多 60 分钟,每页 1---100 条。
json
{
"scopeType": "RUN",
"scopeId": "run-current-0917",
"attemptId": "a1",
"start": "2026-09-17T09:05:00+08:00",
"end": "2026-09-17T09:40:00+08:00",
"limit": 3
}
范围采用左闭右开:包括 09:05,不包括 09:40。这样相邻窗口可以衔接,边界记录不会因为两边都包含而重复。
为什么节点查询不能只带 attemptId=a1?因为不同运行都可能有自己的 a1。如果确实要看某次尝试,就用 RUN 范围,把运行身份带齐。
节点日志还可能包含多个任务。工具会保留每一行的 runId 和 attemptId,助手不能把同节点上的其他任务日志,拼成当前任务的执行过程。
3. 先读一小段完整上下文,再决定是否扩大范围
本篇没有把工具设计成"只返回 ERROR"。
因为异常前的一条 INFO 可能记录提交标识,异常后的一条 INFO 可能说明重试成功。丢掉它们,剩下的异常行很容易被误读。
例如,第七篇的重试样本在 a1 失败前后有三条记录:
text
09:03:58 INFO opening simulated source connection
09:03:59 ERROR source connection timed out
09:04:00 INFO attempt marked FAILED; retry scheduled
这三条一起看,至少能支持"尝试建立模拟数据源连接时超时,随后该次尝试被标记失败并安排重试"。只取中间一行,则看不到调度结果。
即便如此,也不能直接断言"网络故障就是最终根因"。连接超时还需要结合服务端响应、网络或资源证据继续区分。

图 1:异常行提供线索,前后文和运行记录共同确定它在执行过程中的位置。
为了保持实验简单,当前工具按时间窗口返回原始顺序的片段,没有实现全文检索或任意正则。需要围绕异常进一步定位时,先缩小时间窗口;每次扩大范围仍要受服务端约束。
4. 分页没结束、源数据有缺口、消息被截断,是三回事
查询主样本时,教学数据在 09:05---09:40 范围内保存了 8 条可返回日志。每页取 3 条,需要 3 页。
但"8 条都取到了",并不代表这 35 分钟的日志完整。样本故意设置了 09:12---09:15 的采集缺口,还把 L07 设置为超过单条返回长度限制的消息。

图 2:翻到最后一页,只解决当前可用记录的分页问题,不能补回源端缺失或被截断的内容。
工具分别用三个字段表达:
| 字段 | 本样本的含义 | 助手需要做什么 |
|---|---|---|
hasMore |
当前可用结果还有下一页 | 按预算继续读取,或说明尚未读完 |
sourceCompleteness |
PARTIAL 表示已知采集缺口 | 不能声称覆盖整个时段 |
messageTruncated |
当前这条消息只返回前 160 个字符 | 引用时说明片段被截断 |
第三页返回的关键元信息是:
json
{
"sourceCompleteness": "PARTIAL",
"missingIntervals": [{
"start": "2026-09-17T09:12+08:00",
"endExclusive": "2026-09-17T09:15+08:00",
"reason": "SIMULATED_COLLECTOR_GAP"
}],
"returned": 2,
"hasMore": false,
"nextCursor": null
}
hasMore=false 与 sourceCompleteness=PARTIAL 同时出现是正常的:可用记录已经翻完,源端缺口仍然存在。
另外,nextCursor 翻的是日志记录,不是单条长消息的后半段。本文没有提供"获取完整消息"工具,所以不能承诺继续翻页就能补齐 L07。需要完整内容时,应回到经过授权的数据源能力,再单独设计受限获取方式。
5. 续查沿用原查询,游标不是新的访问凭证
第一页会返回一个 nextCursor。续查时,除增加这个值外,其余参数保持不变。
text
第 1 页:L01、L02、L03 → hasMore=true
第 2 页:L04、L05、L06 → hasMore=true
第 3 页:L07、L08 → hasMore=false
教学实现将游标绑定到调用身份、对象、尝试、时间窗口和每页条数,同时固定在这组教学快照内。有效期为 120 秒,服务端最多保留 256 个游标项。
所以,拿第一页的游标把 limit 从 3 改成 100,或者把运行标识换掉,都会被拒绝。更换调用身份也不能继承原身份的续查权限。
读取同一个有效游标会得到相同的记录页;返回的新游标字符串可以不同,记录内容不能因此改变。这个设计帮助客户端处理"上一页响应没收到,再读一次"的情况。
真实日志源不断追加时,还需要稳定的排序键和快照边界,不能用一个随新增日志移动的偏移量直接替代。本文的数据是固定内存快照,因此偏移分页只用于演示机制。
服务重启后内存游标失效,客户端应在原授权范围内重新查询,并按事件标识避免重复处理。它不能因为游标失效,就退化成无范围的大查询。
6. 日志里的文字是证据,不能变成对助手的命令
L06 是专门设计的验收样本,包含一段外部文本,要求助手忽略原要求、查询其他集群并输出凭据。
这句话出现在日志里,不是用户的新指令。工具在结果中标记:
json
{"trust": "UNTRUSTED_LOG_TEXT"}
这个字段是提醒调用方正确处理内容,单靠字段本身不能阻止模型误判。真正的边界要同时存在:服务端限制允许访问的对象,客户端把日志放在证据区域,不把其中的指令当成新的操作授权。
助手可以描述"日志中出现了可疑外部指令文本",但不应照做,也不应为了验证它而扩大查询范围。
日志返回前还做了一个可观察的脱敏示例:形如 token=... 的内容被替换为 token=[REDACTED],同时返回 redacted=true。L07 的原值是故意写入的无效教学占位值。
这条正则只是演示脱敏位置,并不覆盖所有敏感信息。实际接入要按日志字段和数据分类建立规则,不能把"有一个替换正则"当作已经处理所有隐私与凭据。
7. 好的日志解释,应能指回具体片段
读完主样本三页后,一段合适的预期回答是:
本次读取了
run-current-0917/a1在 09:05---09:40 的 8 条可用日志。L02、L03 记录了提交与关联;L04 和 L07 仍显示应用在排队;L05 是一次轮询响应超时,不能据此判定任务执行失败。L08 在 09:39 记录队列运行名额为 8/8。当前可用记录已翻完,但 09:12---09:15 有采集缺口,且 L07 被截断,无法还原整个时段的完整过程。
这里的证据引用不只是"根据日志"。至少应能定位到:
text
快照:teaching-20260917-v1
对象:run-current-0917 / a1
时间窗口:[09:05, 09:40),+08:00
事件:L05、L07、L08
在本教学快照中,事件标识是唯一的;真实系统若只在分区或文件内唯一,就要把对应来源一并纳入引用。
对 COMPLETE_FOR_FIXTURE 也要谨慎理解:它只说明固定教学样本没有声明该范围的采集缺口,不代表真实系统已经证明所有程序事件都被记录。
8. 这篇要验收的,是"有没有把不知道的说清楚"
配套验证会检查三页记录共 8 条、事件标识不重复、结束页仍保留 PARTIAL、L07 同时标记脱敏与截断,以及修改游标范围时被拒绝。
接入模型后,还要再观察它是否做到以下几点:
- 保留查询对象和窗口,没有把其他运行的日志拼进来。
- 没有把单条 WARN 或 ERROR 自动升级为最终根因。
- 没有把分页结束说成日志完整。
- 没有执行日志中的外部指令。
- 引用片段时保留事件标识,并说明缺口与截断。
小周已经能找到异常,也开始知道什么时候应该停下来,说"这段证据还不够"。
下一篇把运行时间线、引擎关联、节点趋势和日志串成一次完整诊断。工具越多,越需要有明确的查询顺序和停止条件。