EveryInfra 返回的错误码在文档里查不到时,最省事的做法不是二选一,而是按请求标识把接入层日志和业务日志各查一遍,再对齐时间戳,看请求停在哪一跳。判断顺序是:先保留下完整响应体和响应头里的请求标识,再用同一标识先查接入层、后查业务日志。接入层有记录、业务侧没有同标识日志,问题多半在接入层到业务之间;两侧都有但阶段不同,就看业务侧最后一条日志停在哪一步。
先保存完整响应体和响应头里的请求标识,再用同一标识先查接入层日志、后查业务日志,最后把两侧时间戳对齐,判断请求停在哪一跳。这套方法适用于错误码在文档里没有对应说明、需要快速分流排查方向的场景。如果响应头和接入层日志里都没有请求标识,这条链路会断掉,需要先补上标识透传和日志字段,再重新构造一次请求验证。
保存完整响应体与响应头,而不只是错误码数字
很多排查卡住的原因,是日志里只留了一个错误码数字,没有留下能把这些数字串起来的请求标识。建议在调用方或接入层的拦截器里,把整次交互落盘。需要落盘的字段清单:
- HTTP 状态码,以及请求方法、路径、上游服务名、发起时间(带时区)
- 响应头中的请求标识,常见头名如 X-Request-Id、X-Trace-Id、traceparent(W3C Trace Context),以及网关自己追加的 request id 头;具体头名需要按 EveryInfra 实际返回确认
- 响应体原文,不要只取 code 字段,把 code、message、detail、data 以及响应体里自带的 requestId/traceId 一起存
下面是一个可替换的采集片段,放在调用方或 SDK 的响应钩子里执行:
import time, json, requests
def log_response(resp, *args, **kwargs):
record = {
'ts': time.strftime('%Y-%m-%dT%H:%M:%S%z', time.localtime()),
'status': resp.status_code,
'request_id': resp.headers.get('X-Request-Id')
or resp.headers.get('X-Trace-Id')
or resp.headers.get('traceparent'),
'body': resp.text,
'url': resp.request.url,
'method': resp.request.method,
}
print(json.dumps(record, ensure_ascii=False))
s = requests.Session()
s.hooks['response'].append(log_response)
替换项是头名和落盘方式(打印、写文件、发到采集端);验证方式是故意请求一个不存在的路径,看这条记录里 request_id 是否有值。如果为空,先确认响应头里到底有没有这类字段,再决定是否需要在接入层补一个。
在接入层日志里按请求标识检索这条记录
拿到请求标识后,第一站是接入层(网关、Ingress、反向代理、API 网关)。它要回答的是:请求有没有到、被谁接走、以什么状态返回。
# 在宿主机上的网关日志里检索
rg -n 'REPLACE_WITH_REQUEST_ID' /var/log/gateway/*.log
# 容器化部署时
kubectl logs -l app=gateway `--since`=15m | grep 'REPLACE_WITH_REQUEST_ID'
# 网关带查询页面时,用请求标识或响应时间做过滤条件
时间窗口建议以调用方记录的响应时间为锚点,前后各取 5 分钟开始检索;如果确认两侧机器时钟不同步,可以放宽到 10 分钟。日志时间带时区偏移时,先确认两侧是否同一时区,再比较时间。
检索不到时的几种可能,需要结合网关配置确认,不要直接断定是应用的问题:请求根本没到接入层(域名解析、连接层或客户端自身失败);接入层只在出错路径打印日志,正常请求没有记录;网关自己生成的请求标识和响应头里返回给你的不是同一个,这时要用响应时间加路径去反查。
在业务日志里用同一标识检索
确认接入层有记录后,用同一个请求标识去查业务日志,看请求有没有穿透到应用、停在哪一步。
rg -n 'REPLACE_WITH_REQUEST_ID' /var/log/everyinfra-app/*.log
kubectl logs -l app=everyinfra-api `--since`=15m | grep 'REPLACE_WITH_REQUEST_ID'
命中的日志行通常长这样,字段名按你现有的日志框架可能不同:
10:00:01.220 INFO [request_id=abc123] inbound POST /v1/orders
10:00:01.245 INFO [request_id=abc123] validate payload ok
10:00:02.310 WARN [request_id=abc123] upstream timeout, code=50301
如果业务日志里完全没有这个标识,要么请求没进到业务代码,要么业务侧没有把请求标识打印出来。需要补的日志点通常有三处:入口中间件打印 inbound 的请求标识和路径;调用下游之前打印 outbound 的同一标识;异常捕获处打印错误码和当前所处阶段。补日志时还要保证请求标识从接入层透传到业务,一般依赖请求头透传,缺了透传,后面两处日志仍然串不起来。
把两处日志的时间戳对齐,找出请求停在哪一跳
错误码本身往往只说明结果,说不出阶段。把接入层和业务侧的时间戳排在一起,才能把“某个码”翻译成“卡在哪一步”。
- 把两侧同一请求标识的日志按时间排序,贴进同一张表或同一个编辑器里。
- 先确认两侧时区一致,再比较绝对时间;带时区偏移的 ISO 时间可以直接排序。
- 允许的偏差取决于两台机器的时钟同步情况:同一宿主机上通常在毫秒到秒级,跨机器且都做了时间同步时一般也在秒级。如果偏差达到分钟级,先怀疑时钟没同步,而不是先怀疑业务慢。
判断停在哪一跳的依据:
- 接入层有请求进入记录、没有返回记录:请求卡在接入层到业务之间,或接入层超时后主动断开。
- 接入层有完整进出记录、业务侧没有任何同标识日志:请求没进入业务代码,或业务未打印标识。
- 两侧都有,业务侧最后一条日志停在某个阶段(校验、下游调用等):错误码对应的是这个阶段的失败。
- 业务侧有完整处理记录并已返回,接入层却返回了另一个错误码:差异在网关的响应处理或超时配置上,需要对照网关配置确认。
整理错误码与日志位置的对照表
把每次排查的结论记成表,下次遇到同类错误就能直接跳到该看的日志。建议至少保留四列:
| 错误码 | 首次出现位置 | 对应日志字段 | 建议下一步动作 |
|---|---|---|---|
| 按返回原文填写,保留前缀 | 接入层 | request_id、时间戳 | 用同一 request_id 查业务日志,确认是否有同标识记录 |
| 按返回原文填写,保留前缀 | 业务入口中间件 | request_id、路径 | 查看该标识后续日志,确认停在哪个阶段 |
| 按返回原文填写,保留前缀 | 下游调用前 | trace_id、下游服务名 | 用下游服务名和时间戳核对下游侧日志 |
填表时的两个注意点:错误码写成实际看到的原文,不要做归一化,否则下次对不上;首次出现位置写“接入层”“业务入口”“下游调用前”这类阶段名,而不是写某个服务名。“建议下一步动作”这一列只写可执行的动作,结论性判断留到有日志证据之后再下,避免下次照表操作时被上一次的错误判断带偏。