用 ChatGPT 5.5 辅助后端接口排查:从报错日志到定位思路

AI编程·六月创作之星博客挑战赛 10w+人浏览 1.6k人参与

AI 时代程序员必备技能

Codex、Claude Code、Cursor、Hermes Agent、OpenClaw等工程化实战专栏 ,讲透 AI 如何接管脏活累活

文章摘要:本文以 ChatGPT 5.5 为例,介绍如何在后端接口排查中利用大模型辅助整理日志、分析异常堆栈、区分已确认事实与可能原因,并围绕订单创建接口偶发 500、userInfo 为空、ThreadLocal 上下文丢失、鉴权链路异常等问题,给出排查路径、日志补充建议和防御性代码思路。同时提到可通过 KULAAI 快速访问 ChatGPT、Claude、Gemini 等模型,降低部署和调试成本。

最近在做接口联调和线上问题排查时,一个很明显的感受是:后端开发真正耗时间的,往往不是写某一段业务代码,而是在一堆日志、异常堆栈、接口参数、数据库状态之间来回切换。尤其是线上偶发问题,日志看起来都有信息,但要把它们串成一条完整的定位路径,还是很费脑子。现在很多开发者会把 ChatGPT、Claude、Gemini 这类大模型工具放进日常开发流程里,用来辅助分析日志、整理排查步骤、生成测试用例和复盘文档。

我自己也对比过自研部署、开源 UI 和一些第三方聚合平台。实际用下来,如果只是想在国内环境里快速试用多种模型,而不是花时间折腾部署和接口配置,KULAAIhttps://ouai.me)这类一站式集成工具会比较省事。它集成了 Gemini、ChatGPT、Claude 等主流模型,适合个人日常测试,也适合小项目快速验证想法。本文主要围绕 ChatGPT 5.5 展开,聊一个更贴近日常开发的场景:如何用它辅助分析后端接口异常,把零散报错整理成可执行的排查路径

需要提前说明一点:这里说的“辅助排查”,不是让 ChatGPT 5.5 直接替你判断线上故障原因。线上问题最终还是要靠日志、监控、链路追踪、数据库状态和复现实验来确认。大模型更适合做的是:帮你整理信息、补全排查清单、发现容易遗漏的方向,并把一堆杂乱内容转成结构化分析


一、问题背景:一个接口偶发 500

假设现在有一个很常见的后端接口:

http

POST /api/order/create

接口功能是创建订单,流程大致如下:

  1. 校验用户登录状态;
  2. 查询商品库存;
  3. 创建订单记录;
  4. 扣减库存;
  5. 写入订单日志;
  6. 返回订单号。

最近联调时发现,这个接口偶尔返回 500。不是每次都失败,而是在并发稍微高一点的时候更容易出现。

接口返回:

json

{
  "code": 500,
  "message": "Internal Server Error",
  "data": null
}

服务端日志里可以看到类似内容:

2026-01-15 10:23:41.231 ERROR [order-service] 
c.e.order.controller.OrderController : create order failed

java.lang.NullPointerException: Cannot invoke "com.example.user.UserInfo.getUserId()" 
because "userInfo" is null
    at com.example.order.service.OrderService.createOrder(OrderService.java:86)
    at com.example.order.controller.OrderController.create(OrderController.java:42)

相关代码简化如下:

@PostMapping("/create")
public Result<String> create(@RequestBody CreateOrderRequest request) {
    String orderNo = orderService.createOrder(request);
    return Result.success(orderNo);
}
public String createOrder(CreateOrderRequest request) {
    UserInfo userInfo = userContext.getCurrentUser();

    Long userId = userInfo.getUserId();

    Product product = productMapper.selectById(request.getProductId());
    if (product.getStock() <= 0) {
        throw new BizException("库存不足");
    }

    Order order = new Order();
    order.setUserId(userId);
    order.setProductId(product.getId());
    order.setAmount(product.getPrice());
    orderMapper.insert(order);

    productMapper.decreaseStock(product.getId());

    return order.getOrderNo();
}

从堆栈看,问题似乎很直接:userInfo 是 null
但真实开发里,不能看到 NPE 就直接改一行判空了事。更关键的问题是:为什么当前接口里拿不到用户信息?


二、不要直接问“这是什么问题”

很多人使用大模型时会这样问:

下面代码报空指针,帮我看看原因。

这种问法不是不能用,但模型大概率会回答:

  • userInfo 为空;
  • 需要判空;
  • 检查登录态;
  • 检查 token;
  • 增加异常处理。

这些建议没错,但比较浅。

更好的问法是,把日志、代码、背景和你希望的输出形式一起给它。

可以这样问 ChatGPT 5.5:

你是一名 Java 后端问题排查助手。下面是一个订单创建接口的异常日志和简化代码。

请你帮我完成:
1. 只基于已给信息整理问题现象;
2. 区分“已确认事实”和“可能原因”;
3. 给出排查优先级;
4. 不要直接下最终结论;
5. 输出适合研发排查记录使用。

【异常日志】
粘贴日志

【相关代码】
粘贴代码

【补充背景】
接口在并发稍高时更容易出现 500,单次手动调用不一定复现。

这样输入后,ChatGPT 5.5 的输出通常会更接近排查记录,而不是简单给一句“加判空”。


三、让 ChatGPT 5.5 先整理事实

比较理想的输出应该类似这样:

markdown

## 已确认事实

1. `/api/order/create` 接口偶发返回 500。
2. 异常类型为 `NullPointerException`。
3. 空指针发生在 `OrderService.createOrder` 方法中。
4. 具体位置是调用 `userInfo.getUserId()` 时,`userInfo` 为 null。
5. `userInfo` 来源于 `userContext.getCurrentUser()`。
6. 问题在并发稍高时更容易出现,单次调用不一定复现。

## 可能原因

1. 当前请求未正确携带登录 token。
2. token 解析失败,导致用户上下文未写入。
3. 用户上下文使用了 ThreadLocal,但在线程复用或异步场景下没有正确设置。
4. 网关或拦截器在部分请求中没有执行鉴权逻辑。
5. 并发场景下存在上下文污染或清理时机不正确。
6. 测试环境存在部分请求绕过登录态的情况。

## 暂不能直接确认的部分

1. 不能仅凭 NPE 判断是前端未传 token。
2. 不能确认一定是 ThreadLocal 问题,需要查看 `userContext` 实现。
3. 不能确认是否与异步线程有关,需要查看调用链。
4. 不能确认是否所有失败请求都缺少 token,需要结合网关日志或 access log。

这个结构就比“加个 if 判断”有价值多了。

因为实际排查线上问题时,第一步不是急着改代码,而是先搞清楚:

  • 确认了什么;
  • 猜测了什么;
  • 哪些证据还缺;
  • 下一步该查哪里。

四、继续让它生成排查路径

接下来可以继续问:

请基于上面的事实和可能原因,给出一个后端接口排查路径。

要求:
1. 从最容易验证、最高优先级开始;
2. 每一步说明要看什么;
3. 每一步说明目的;
4. 不超过 8 步;
5. 包含日志、鉴权、ThreadLocal、并发复现几个方向。

输出可以整理成这样:

markdown

## 排查路径

### 1. 确认失败请求是否携带 token

- 看什么:access log、网关日志、请求 Header。
- 目的:确认失败请求是否缺少 Authorization 信息。

### 2. 查看鉴权拦截器是否执行

- 看什么:登录拦截器、Filter、Interceptor 的日志。
- 目的:确认请求是否经过统一鉴权流程。

### 3. 检查 token 解析结果

- 看什么:token 是否过期、解析是否异常、用户 ID 是否为空。
- 目的:确认是否因为 token 无效导致用户上下文未写入。

### 4. 检查 UserContext 实现

- 看什么:是否基于 ThreadLocal 保存用户信息。
- 目的:确认用户上下文是否依赖当前线程。

### 5. 检查 ThreadLocal 设置和清理时机

- 看什么:是否在请求开始时 set,在请求结束时 remove。
- 目的:避免上下文残留或被提前清理。

### 6. 检查是否存在异步调用

- 看什么:是否使用 `@Async`、线程池、CompletableFuture。
- 目的:确认用户上下文是否跨线程丢失。

### 7. 压测复现并增加关键日志

- 看什么:并发请求下 userInfo 为 null 的比例。
- 目的:判断问题是否与并发、线程复用有关。

### 8. 增加接口防御性处理

- 看什么:userInfo 为空时是否返回明确的 401/登录失效。
- 目的:避免 NPE 暴露为 500,同时提升问题可观测性。

这个排查顺序比较适合真实项目。
它不会一上来就认定“前端没传 token”,也不会直接把问题归到 ThreadLocal,而是一步步补证据。


五、补充一个更合理的接口防御写法

从代码上看,这里确实应该避免直接调用:

Long userId = userInfo.getUserId();

可以先做基本防御:

public String createOrder(CreateOrderRequest request) {
    UserInfo userInfo = userContext.getCurrentUser();
    if (userInfo == null || userInfo.getUserId() == null) {
        throw new UnauthorizedException("用户未登录或登录状态已失效");
    }

    Long userId = userInfo.getUserId();

    Product product = productMapper.selectById(request.getProductId());
    if (product == null) {
        throw new BizException("商品不存在");
    }

    if (product.getStock() <= 0) {
        throw new BizException("库存不足");
    }

    Order order = new Order();
    order.setUserId(userId);
    order.setProductId(product.getId());
    order.setAmount(product.getPrice());
    orderMapper.insert(order);

    productMapper.decreaseStock(product.getId());

    return order.getOrderNo();
}

同时配合全局异常处理:

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(UnauthorizedException.class)
    public Result<Void> handleUnauthorized(UnauthorizedException e) {
        return Result.fail(401, e.getMessage());
    }

    @ExceptionHandler(BizException.class)
    public Result<Void> handleBizException(BizException e) {
        return Result.fail(400, e.getMessage());
    }

    @ExceptionHandler(Exception.class)
    public Result<Void> handleException(Exception e) {
        // 这里应记录完整异常堆栈
        return Result.fail(500, "系统异常,请稍后重试");
    }
}

这样做的好处是:

  • 未登录问题返回 401,而不是 500;
  • 业务异常和系统异常区分开;
  • 前端能根据状态码做明确处理;
  • 后端日志也更容易定位问题。

不过要注意:这只能防止接口继续抛 NPE,并不等于根因已经解决。
如果用户上下文不应该为空,那仍然要继续查鉴权链路和上下文传递。


六、重点检查 ThreadLocal 场景

很多 Java Web 项目会用 ThreadLocal 保存当前登录用户,例如:

public class UserContext {

    private static final ThreadLocal<UserInfo> USER_HOLDER = new ThreadLocal<>();

    public static void set(UserInfo userInfo) {
        USER_HOLDER.set(userInfo);
    }

    public static UserInfo getCurrentUser() {
        return USER_HOLDER.get();
    }

    public static void clear() {
        USER_HOLDER.remove();
    }
}

拦截器中设置:

public boolean preHandle(HttpServletRequest request,
                         HttpServletResponse response,
                         Object handler) {
    String token = request.getHeader("Authorization");
    UserInfo userInfo = tokenService.parse(token);
    UserContext.set(userInfo);
    return true;
}

请求结束后清理:

public void afterCompletion(HttpServletRequest request,
                            HttpServletResponse response,
                            Object handler,
                            Exception ex) {
    UserContext.clear();
}

这种写法本身很常见,但有几个坑:

1. 没有及时 clear

如果请求结束后没有 remove(),线程池复用时可能出现用户上下文残留。
这类问题更隐蔽,甚至可能造成数据串用户。

2. 异步线程拿不到上下文

比如:

CompletableFuture.runAsync(() -> {
    UserInfo userInfo = UserContext.getCurrentUser();
    // 这里可能是 null
});

因为 ThreadLocal 只绑定当前线程,换了线程就拿不到原来的值。

3. 拦截器没有覆盖所有路径

有些接口路径可能被配置为白名单,或者网关转发时路径变化,导致鉴权逻辑没有执行。

4. token 解析异常被吞掉

如果 token 解析失败后没有中断请求,而是继续往下走,就可能导致业务层拿到 null。

这几个点都很适合让 ChatGPT 5.5 帮忙整理成 checklist,然后逐项验证。


七、让 ChatGPT 5.5 帮你补日志点

排查偶发问题时,最怕日志里只有一句 NPE,没有上下文。

可以继续问:

针对这个接口偶发 userInfo 为空的问题,请帮我设计需要补充的关键日志点。

要求:
1. 不打印完整 token;
2. 能关联单次请求;
3. 包含网关、拦截器、业务层;
4. 注意日志不要泄露敏感信息。

可以得到类似建议:

markdown

## 建议日志点

### 1. 网关层

- requestId
- path
- method
- 是否存在 Authorization Header
- token 前 6 位 hash 或脱敏标识
- 转发目标服务

### 2. 鉴权拦截器

- requestId
- token 是否存在
- token 解析是否成功
- 解析出的 userId
- 是否写入 UserContext

### 3. 业务层入口

- requestId
- productId
- UserContext 中 userInfo 是否为空
- userId 是否为空

### 4. 异常日志

- requestId
- 接口路径
- 异常类型
- 堆栈
- 当前线程名

对应代码中可以使用 MDC 记录 requestId:

@Component
public class RequestIdFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request,
                         ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
        String requestId = UUID.randomUUID().toString().replace("-", "");
        MDC.put("requestId", requestId);
        try {
            chain.doFilter(request, response);
        } finally {
            MDC.remove("requestId");
        }
    }
}

日志格式中带上:

[%X{requestId}] %-5level %logger - %msg%n

这样一次请求从网关、拦截器到业务层,都能串起来。


八、让它生成压测复现思路

如果问题只在并发稍高时出现,就不能只靠手动点接口。

可以让 ChatGPT 5.5 生成一个简单的压测方案:

请给出一个针对该接口的并发复现方案。

要求:
1. 使用 JMeter 或 curl 都可以;
2. 重点观察 userInfo 为空的问题;
3. 给出需要记录的指标;
4. 不要求复杂压测,只用于问题复现。

它可能会给出这样的方案:

markdown

## 并发复现方案

### 测试目标

验证 `/api/order/create` 在并发请求下是否更容易出现 `userInfo == null`。

### 请求准备

- 使用同一个有效 token;
- 使用多个不同商品 ID;
- 保证测试商品库存充足;
- 每个请求带上 requestId。

### 并发设置

- 线程数:10、30、50 逐步增加;
- 循环次数:每个线程 100 次;
- 间隔时间:可设置 100ms;
- 观察错误率变化。

### 重点记录

- 总请求数;
- 500 数量;
- 401 数量;
- userInfo 为空次数;
- token 解析失败次数;
- 平均响应时间;
- 失败请求 requestId。

### 判断方向

- 如果 401 增多:可能是 token 校验或登录态问题;
- 如果 500 仍然出现:说明业务层异常处理仍不完整;
- 如果 userInfo 为空集中在异步流程:重点检查上下文传递;
- 如果并发越高越明显:重点检查线程池、ThreadLocal 或共享变量。

这类输出不是最终方案,但能快速给你一个复现框架。


九、ChatGPT 5.5 在后端开发里适合做什么

结合这个例子,我觉得 ChatGPT 5.5 比较适合做这些事情:

1. 日志整理

把 N 段日志合并成:

  • 问题现象;
  • 异常位置;
  • 可能影响范围;
  • 待确认问题。

2. 排查清单生成

例如:

  • 接口 500 排查清单;
  • Redis 缓存穿透排查清单;
  • MySQL 慢查询排查清单;
  • MQ 消息堆积排查清单。

3. 代码风险点提示

它可以帮你指出一些常见问题:

  • 空指针;
  • 事务边界;
  • 并发安全;
  • 资源未关闭;
  • 异常被吞;
  • 日志不完整。

4. 单元测试用例补充

例如针对订单接口,可以让它生成测试用例:

  • 未登录;
  • 商品不存在;
  • 库存不足;
  • 正常下单;
  • 并发下单;
  • 重复提交。

5. 复盘文档生成

一次线上问题处理完,可以把排查过程丢给它,让它整理成:

  • 问题背景;
  • 影响范围;
  • 时间线;
  • 根因分析;
  • 修复方案;
  • 预防措施。

十、但不要让它替你拍板

需要强调的是,大模型在后端排查里也有明显边界。

它不适合:

  • 在缺少日志时直接判断根因;
  • 替你确认线上数据库状态;
  • 替你判断某次发布是否一定导致问题;
  • 在没看完整调用链时给最终结论;
  • 直接生成未经验证的生产修复代码。

尤其是线上故障,不能因为模型说“可能是 ThreadLocal”就直接改生产逻辑。
正确做法应该是:让它帮你整理假设,然后你用日志、监控和复现实验去验证。


十一、我常用的 Prompt 模板

如果你想用 ChatGPT 5.5 辅助后端接口排查,可以直接套这个模板:

你是一名 Java 后端问题排查助手。下面是接口异常信息,请帮我整理排查思路。

【接口背景】
接口路径:
接口功能:
技术栈:
是否经过网关:
是否需要登录:

【异常现象】
1.
2.
3.

【异常日志】
粘贴完整异常日志

【相关代码】
粘贴 Controller / Service / Mapper / 拦截器代码

【补充信息】
是否偶发:
是否与并发有关:
最近是否发布:
是否有监控异常:

【输出要求】
1. 先整理已确认事实;
2. 再列可能原因;
3. 给出排查优先级;
4. 每一步说明要看什么日志或证据;
5. 不要直接下最终结论;
6. 如果信息不足,请列出需要补充的内容。

这个模板的核心是:让模型帮你“拆问题”,而不是让它直接“猜答案”


总结

ChatGPT 5.5 用在后端开发里,比较实用的位置不是替你写几行简单代码,而是辅助处理那些信息量大、上下文多、排查链路长的问题

以接口偶发 500 为例,它可以帮你:

  • 从日志中提取关键事实;
  • 区分已确认现象和可能原因;
  • 生成排查优先级;
  • 补充日志设计;
  • 梳理 ThreadLocal、鉴权、并发等方向;
  • 整理压测复现思路;
  • 最后生成问题复盘文档。

但最终结论仍然要靠真实证据确认。
如果把它当成一个“排查思路整理助手”,而不是“线上故障裁判”,它在日常开发中的价值会更稳定,也更符合真实工程场景

AI 时代程序员必备技能

Codex、Claude Code、Cursor、Hermes Agent、OpenClaw等工程化实战专栏 ,讲透 AI 如何接管脏活累活

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值