全局异常处理需通过requestcontextholder安全获取客户端ip、url、参数、用户名等信息,注意代理场景ip解析、敏感参数脱敏、异步线程上下文丢失及日志堆栈完整性,推荐异步落库并合理设计数据库字段类型。

全局异常处理中记录客户端信息,关键是在异常发生时能拿到请求上下文里的真实客户端数据,比如 IP、URL、参数、用户身份等。这些信息不能只靠 Controller 参数传入,因为异常可能出现在 Service、Filter 或异步线程里。
需要记录哪些客户端信息
实际落地时,至少应包含以下几项:
-
客户端 IP:注意区分直连和代理场景,优先取
X-Forwarded-For头, fallback 到request.getRemoteAddr() -
请求路径与方法:如
POST /api/user/login -
请求参数:GET 参数可从 query string 获取,POST/PUT 的 body 需借助
ContentCachingRequestWrapper提前缓存读取 -
当前用户名:若用 JWT,可通过
JwtUtils.getCurrentUser()或类似工具类解析 token 获取;若用 Spring Security,可用SecurityContextHolder.getContext().getAuthentication() - User-Agent 和 Referer:辅助识别设备、来源,排查异常请求
在 GlobalExceptionHandler 中安全获取 Request
@ControllerAdvice 类里不能直接声明 HttpServletRequest 参数(部分异常可能不走 MVC 调度链),必须通过 RequestContextHolder 拿:
- 使用
RequestContextHolder.getRequestAttributes()获取ServletRequestAttributes - 再调用
getRequest()得到HttpServletRequest - 务必判空——异步线程、定时任务或 Filter 提前结束时,该值可能为 null
- 若需读取 request body,要确保已启用
ContentCachingRequestWrapper(在 Filter 中 wrap)
避免常见陷阱
几个高频出错点要特别注意:
- 异步线程中 RequestContextHolder 为空:需在主线程中提前提取并手动传递 IP、URL 等字段,不要在 @Async 方法里再查
- 日志中打印敏感参数:密码、token、身份证号等字段要脱敏,建议用工具类统一过滤 key 名(如含 "pwd"、"token"、"idcard")
-
IP 地址不准确:Nginx 配置未透传
X-Forwarded-For,或代码没做多级代理解析(如取第一个非私有 IP) -
异常堆栈被吞掉:Logback 配置中确保
%ex或%throwable存在,否则日志里看不到完整堆栈
写入数据库的实践要点
异常日志实体(如 SysExceptionLog)入库时建议:
- 字段
ip类型设为VARCHAR(45),兼容 IPv6 -
params和exceptionDetail用 TEXT 类型,防超长截断 - 创建时间用数据库自动填充(
@TableField(fill = FieldFill.INSERT))或 Java 层赋值new Date() - 异步落库更稳妥:用
@Async或发消息队列,避免阻塞主异常处理流程











