java web审计日志链路需通过interceptor或filter拦截请求,结合mdc传递traceid、ip、脱敏参数摘要,并用system.nanotime()精准计时,输出结构化日志。

在 Java Web 应用中构建完整的审计日志链路,核心是统一捕获请求入口的客户端 IP、完整请求参数(含 GET/POST/JSON)、处理耗时,并关联同一请求的上下游日志。不依赖第三方 APM,纯代码层即可实现,关键是拦截时机、参数提取方式和上下文传递。
一、用 Filter 或 Interceptor 拦截请求入口
推荐使用 Spring 的 HandlerInterceptor(更轻量、与 MVC 生命周期契合)或标准 Servlet Filter(兼容性更强)。两者都能在请求开始和结束时介入,便于计时和上下文绑定。
- Filter 更底层,能捕获所有请求(包括静态资源),但需手动解析参数;
- Interceptor 在 DispatcherServlet 之后执行,天然支持 Spring 参数解析(如 @RequestBody),但不拦截非 Spring MVC 请求(如 Actuator 端点需额外配置)。
建议优先选 Interceptor,并配合 MDC(Mapped Diagnostic Context)将请求唯一 ID、IP、参数摘要等写入日志上下文,确保后续任意位置打日志都能自动携带。
二、准确获取客户端真实 IP
直接调用 request.getRemoteAddr() 往往拿到的是反向代理(Nginx、SLB)的内网 IP。必须检查 X-Forwarded-For、X-Real-IP 等头字段,并按信任链顺序取值:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 若应用部署在可信内网,且 Nginx 已配置
proxy_set_header X-Real-IP $remote_addr;,优先取X-Real-IP; - 否则解析
X-Forwarded-For:取最左端非内网地址(过滤 127.0.0.1、10.x.x.x、172.16–31.x.x、192.168.x.x); - 最后 fallback 到
getRemoteAddr()。
封装成工具方法,避免各处重复判断。
三、安全地记录请求参数
避免直接打印敏感字段(密码、token、身份证号等),也不宜全量打印大 JSON Body(影响性能和日志体积):
-
GET 请求:用
request.getQueryString()获取原始参数串,或遍历request.getParameterMap(); -
POST 表单:同样可用
getParameterMap(); - JSON Body:需在 Interceptor 中提前读取 InputStream(注意只能读一次),用 Jackson/ObjectMapper 转为 Map 或简化结构(如只取一级 key 和字符串值长度);
-
脱敏处理:对 key 包含 "password"、"token"、"idcard" 等字段,值统一替换为
"***"; - 限长截断:单个参数值超 256 字符时,记录前 128 + "...",防止日志爆炸。
四、计算耗时并组装审计日志
在 preHandle 记录开始时间戳,在 afterCompletion 计算耗时(单位毫秒),结合 MDC 中已存的 traceId、ip、params 摘要,输出结构化日志:
- 推荐使用 SLF4J + Logback,格式示例:
[traceId=abc123] [ip=1.2.3.4] [uri=/api/user] [method=POST] [params={\"name\":\"张***\",\"age\":25}] [cost=142ms] [status=200] - 耗时统计务必用
System.nanoTime()(纳秒级,不受系统时间调整影响),而非System.currentTimeMillis(); - 异常情况(如解析参数失败、IO 中断)也需在 afterCompletion 中捕获
Throwable,标记[error=xxx]并记录堆栈关键行。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










