如何实现Spring Boot中打印接口调用日志的示例代码?

2026-05-25 21:061阅读0评论SEO资讯
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计1750个文字,预计阅读时间需要7分钟。

如何实现Spring Boot中打印接口调用日志的示例代码?

目录+概述+方案思路+封装HttpServletRequest请求+通过过滤器传递可重复读请求+记录入参日志+实现入参记录拦截器+注册拦截器+记录返回日志+概述+请求日志几乎涵盖所有大型企业级项目

目录
  • 概述
  • 方案思路
  • 封装HttpServletRequest请求
  • 把可重复读请求体通过过滤器往下传
  • 记录入参日志
    • 实现入参记录拦截器
    • 注册拦截器
  • 记录返参日志

    概述

    请求日志几乎是所有大型企业级项目的必要的模块,请求日志对于我们来说后期在项目运行上线一段时间用于排除异常、请求分流处理、限制流量等。

    请求日志一般都会记录请求参数、请求地址、请求状态(Status Code)、SessionId、请求方法方式(Method)、请求时间、客户端IP地址、请求返回内容、耗时等等。如果你得系统还有其他个性化的配置,也可以完成记录。

    记录请求参数时,由于servlet.getInputStream的数据只能读取一次,因此需要先把数据缓存下来,构造返回流,保证之后的Controller可以正常读取到请求体的数据。

    方案思路

    • 封装HttpServletRequest请求类,改类在构造方法中将请求的输入流中的数据缓存了起来,保证之后的处理可以重复读取输入流中的数据。
    • 实现过滤器,把上步封装的请求类传下去,保证Controller可以正常读取输入流中的数据。
    • 添加拦截器,读取输入流中的数据。
    • 读取返回参数。

    封装HttpServletRequest请求

    package com.example.demo.intercept; import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import java.io.*; /**  * @author   * @date 封装HttpServletRequest请求  */ public class RequestWrapper extends HttpServletRequestWrapper {     private final String body;     public RequestWrapper(HttpServletRequest request) {         super(request);         StringBuilder stringBuilder = new StringBuilder();         BufferedReader bufferedReader = null;         InputStream inputStream = null;         try {             inputStream = request.getInputStream();             if (inputStream != null) {                 bufferedReader = new BufferedReader(new InputStreamReader(inputStream));                 char[] charBuffer = new char[128];                 int bytesBody = -1;                 while ((bytesBody = bufferedReader.read(charBuffer)) > 0) {                     stringBuilder.append(charBuffer, 0, bytesBody);                 }             } else {                 stringBuilder.append("");             }         } catch (IOException e) {         } finally {             if (inputStream != null) {                 try {                     inputStream.close();                 } catch (IOException e) {                     e.printStackTrace();                 }             }             if (bufferedReader != null) {                 try {                     bufferedReader.close();                 } catch (IOException e) {                     e.printStackTrace();                 }             }         }         body = stringBuilder.toString();     }     @Override     public ServletInputStream getInputStream() throws IOException {         final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());         ServletInputStream servletInputStream = new ServletInputStream() {             @Override             public boolean isFinished() {                 return false;             }             @Override             public boolean isReady() {                 return false;             }             @Override             public void setReadListener(ReadListener readListener) {             }             @Override             public int read() throws IOException {                 return byteArrayInputStream.read();             }         };         return servletInputStream;     }     @Override     public BufferedReader getReader() throws IOException {         return new BufferedReader(new InputStreamReader(this.getInputStream()));     }     public String getBody() {         return this.body;     } }

    把可重复读请求体通过过滤器往下传

    防止请求流读取一次后就没有了,之后的不管是过滤器、拦截器、处理器都是读的已经缓存好的数据,实现可重复读。

    如何实现Spring Boot中打印接口调用日志的示例代码?

    package com.example.demo.intercept; import org.springframework.stereotype.Component; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import java.io.IOException; /**  * @author   * @date 防止请求流读取一次后就没有了  */ @Component @WebFilter(urlPatterns = "/**") public class RecordChannelFilter implements Filter {     @Override     public void init(FilterConfig filterConfig) throws ServletException {     }     @Override     public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {         ServletRequest request = null;         if (servletRequest instanceof HttpServletRequest) {             request = new RequestWrapper((HttpServletRequest) servletRequest);         }         if (request ==null){             //防止流读取一次就没有了,将流传递下去             filterChain.doFilter(servletRequest,servletResponse);         }else {             filterChain.doFilter(request,servletResponse);         }     }     @Override     public void destroy() {     } }

    记录入参日志

    实现入参记录拦截器

    通过拦截器的方式实现用户入参记录。

    package com.example.demo.intercept; import com.alibaba.fastjson.JSONObject; import org.bouncycastle.util.encoders.Base64; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import io.jsonwebtoken.Claims; import javax.annotation.Resource; import javax.crypto.spec.SecretKeySpec; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /**  * @author   * @date 记录用户操作记录入参  */ @Component public class OperationLogInterceptor implements HandlerInterceptor {     /**      * Jwt secert串,需要与加密token的秘钥一致      */     public static final String JWT_SECERT = "23142d7a9s7d66970ad07d8sa";     /**      * 需要记录的接口URL      */     private static List<String> pathList = new ArrayList<>();     static {         pathList.add("/mdms/model");     }     @Resource     private FunctionDOMapper functionDOMapper;//菜单动能sql     @Resource     private UserOperationHistoryDOMapper historyDOMapper;//操作日志记录表     @Override     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {         String servletPath = "" + request.getServletPath();         String method = request.getMethod();         pathList.forEach(path -> {             if (servletPath.contains(path)){                 Cookie[] cookies = request.getCookies();                 if (cookies != null) {                     for (Cookie cookie : cookies) {                         //获取token在请求中                         if (cookie.getName().equals("_qjt_ac_")) {                             String token = cookie.getValue();                             /**解密token**/                             byte[] encodeKey = Base64.decode(JWT_SECERT);                             Claims claims = null;                             try {                                 SecretKeySpec keySpec = new SecretKeySpec(encodeKey, 0, encodeKey.length, "AES");                                 claims = Jwts.parser().setSigningKey(keySpec).parseClaimsJws(token).getBody();                             } catch (Exception e) {                                 return;                             }                             //用户账号                             String account = claims.getSubject();                             //查询URL在功能表中的功能                             functionDOMapper.selectOne(servletPath, method);                             //获取入参                             RequestWrapper requestWrapper = null;                             if (request instanceof HttpServletRequest) {                                 requestWrapper = new RequestWrapper(request);                             }                             Map<String,Object> map = new HashMap<>();                             map.put("parameter", JSONObject.parse(requestWrapper.getBody()));                             historyDOMapper.insert(map);//将操作信息入库                         }                     }                 }              }         });         return true;     } }

    注册拦截器

    package com.example.demo.config; import com.example.demo.intercept.OperationLogInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /**  * @author   * @date 注册拦截器  */ public class WebConfig implements WebMvcConfigurer {     @Bean     public HandlerInterceptor getOperationLogInterceptor() {         return new OperationLogInterceptor();     }     @Override     public void addInterceptors(InterceptorRegistry registry){         registry.addInterceptor(getOperationLogInterceptor()).addPathPatterns("/**").excludePathPatterns();     } }

    记录返参日志

    package com.example.demo.intercept; import com.alibaba.fastjson.JSONObject; import org.bouncycastle.util.encoders.Base64; import org.springframework.core.MethodParameter; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.util.CollectionUtils; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; import javax.annotation.Resource; import javax.crypto.spec.SecretKeySpec; import javax.servlet.http.HttpServletRequest; import javax.xml.ws.Response; import java.util.*; /**  * @author   * @date 记录用户操作日志返参  */ @ControllerAdvice(basePackages = "项目包") public class GetResponseBody implements ResponseBodyAdvice<Object> {     /**      * Jwt secert串,需要与加密token的秘钥一致      */     public static final String JWT_SECERT = "23142d7a9s7d66970ad07d8sa";     /**      * 需要记录的接口URL      */     private static List<String> pathList = new ArrayList<>();     static {         pathList.add("/mdms/model");     }     @Resource     private FunctionDOMapper functionDOMapper;//菜单动能sql     @Resource     private UserOperationHistoryDOMapper historyDOMapper;//操作日志记录表     @Override     public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {         return false;     }     @Override     public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {         String path = serverHttpRequest.getURI().getPath();         String methodValue = serverHttpRequest.getMethodValue();         pathList.forEach(serverPath -> {             if (path.contains(serverPath)) {                 HashMap<String, String> cookieMap = new HashMap<>();                 HttpHeaders headers = serverHttpRequest.getHeaders();                 List<String> cookieList = headers.get("cookie");                 if (CollectionUtils.isEmpty(cookieList)) {                     return;                 }                 String replaceAll = cookieList.get(0).replaceAll(";", "").replaceAll(";", "");                 String[] split = replaceAll.split(";");                 for (String cookie : split) {                     String[] param = cookie.split("=");                     cookieMap.put(param[0], param[1]);                 }                 String token = cookieMap.get("_qjt_ac_");                 byte[] encodeKey = Base64.decode(JWT_SECERT);                 Claims claims = null;                 try {                     SecretKeySpec keySpec = new SecretKeySpec(encodeKey, 0, encodeKey.length, "AES");                     claims = Jwts.parser().setSigningKey(keySpec).parseClaimsJws(token).getBody();                 } catch (Exception e) {                     return;                 }                 //用户账号                 String account = claims.getSubject();                 //查询URL在功能表中的功能                 functionDOMapper.selectOne(servletPath, method);                 //获取返参                 List<Object> list = historyDOMapper.select("功能表参数", account);                 list.sort((Object1,Object2)->Object2.getTime().compareTo(Object1.getTime()));//将查询到的操作记录按时间降序排列                 Object history = list.get(0);                 if (body instanceof Response) {                     Response response = (Response) body;                     JSONObject jsonObject = JSONObject.parseObject(history.getParam());                     jsonObject.put("message",response.getMessage());                     jsonObject.put("body",response.getData());                     history.setParam(jsonObject.toString());                     history.setDes(response.getMessage());                 }                 historyDOMapper.updateByPrimaryKeySelective(history);//将操作信息更新             }         });         return body;     } }

    以上为个人经验,希望能给大家一个参考,也希望大家多多支持自由互联。

    标签:实例目录

    本文共计1750个文字,预计阅读时间需要7分钟。

    如何实现Spring Boot中打印接口调用日志的示例代码?

    目录+概述+方案思路+封装HttpServletRequest请求+通过过滤器传递可重复读请求+记录入参日志+实现入参记录拦截器+注册拦截器+记录返回日志+概述+请求日志几乎涵盖所有大型企业级项目

    目录
    • 概述
    • 方案思路
    • 封装HttpServletRequest请求
    • 把可重复读请求体通过过滤器往下传
    • 记录入参日志
      • 实现入参记录拦截器
      • 注册拦截器
    • 记录返参日志

      概述

      请求日志几乎是所有大型企业级项目的必要的模块,请求日志对于我们来说后期在项目运行上线一段时间用于排除异常、请求分流处理、限制流量等。

      请求日志一般都会记录请求参数、请求地址、请求状态(Status Code)、SessionId、请求方法方式(Method)、请求时间、客户端IP地址、请求返回内容、耗时等等。如果你得系统还有其他个性化的配置,也可以完成记录。

      记录请求参数时,由于servlet.getInputStream的数据只能读取一次,因此需要先把数据缓存下来,构造返回流,保证之后的Controller可以正常读取到请求体的数据。

      方案思路

      • 封装HttpServletRequest请求类,改类在构造方法中将请求的输入流中的数据缓存了起来,保证之后的处理可以重复读取输入流中的数据。
      • 实现过滤器,把上步封装的请求类传下去,保证Controller可以正常读取输入流中的数据。
      • 添加拦截器,读取输入流中的数据。
      • 读取返回参数。

      封装HttpServletRequest请求

      package com.example.demo.intercept; import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import java.io.*; /**  * @author   * @date 封装HttpServletRequest请求  */ public class RequestWrapper extends HttpServletRequestWrapper {     private final String body;     public RequestWrapper(HttpServletRequest request) {         super(request);         StringBuilder stringBuilder = new StringBuilder();         BufferedReader bufferedReader = null;         InputStream inputStream = null;         try {             inputStream = request.getInputStream();             if (inputStream != null) {                 bufferedReader = new BufferedReader(new InputStreamReader(inputStream));                 char[] charBuffer = new char[128];                 int bytesBody = -1;                 while ((bytesBody = bufferedReader.read(charBuffer)) > 0) {                     stringBuilder.append(charBuffer, 0, bytesBody);                 }             } else {                 stringBuilder.append("");             }         } catch (IOException e) {         } finally {             if (inputStream != null) {                 try {                     inputStream.close();                 } catch (IOException e) {                     e.printStackTrace();                 }             }             if (bufferedReader != null) {                 try {                     bufferedReader.close();                 } catch (IOException e) {                     e.printStackTrace();                 }             }         }         body = stringBuilder.toString();     }     @Override     public ServletInputStream getInputStream() throws IOException {         final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());         ServletInputStream servletInputStream = new ServletInputStream() {             @Override             public boolean isFinished() {                 return false;             }             @Override             public boolean isReady() {                 return false;             }             @Override             public void setReadListener(ReadListener readListener) {             }             @Override             public int read() throws IOException {                 return byteArrayInputStream.read();             }         };         return servletInputStream;     }     @Override     public BufferedReader getReader() throws IOException {         return new BufferedReader(new InputStreamReader(this.getInputStream()));     }     public String getBody() {         return this.body;     } }

      把可重复读请求体通过过滤器往下传

      防止请求流读取一次后就没有了,之后的不管是过滤器、拦截器、处理器都是读的已经缓存好的数据,实现可重复读。

      如何实现Spring Boot中打印接口调用日志的示例代码?

      package com.example.demo.intercept; import org.springframework.stereotype.Component; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import java.io.IOException; /**  * @author   * @date 防止请求流读取一次后就没有了  */ @Component @WebFilter(urlPatterns = "/**") public class RecordChannelFilter implements Filter {     @Override     public void init(FilterConfig filterConfig) throws ServletException {     }     @Override     public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {         ServletRequest request = null;         if (servletRequest instanceof HttpServletRequest) {             request = new RequestWrapper((HttpServletRequest) servletRequest);         }         if (request ==null){             //防止流读取一次就没有了,将流传递下去             filterChain.doFilter(servletRequest,servletResponse);         }else {             filterChain.doFilter(request,servletResponse);         }     }     @Override     public void destroy() {     } }

      记录入参日志

      实现入参记录拦截器

      通过拦截器的方式实现用户入参记录。

      package com.example.demo.intercept; import com.alibaba.fastjson.JSONObject; import org.bouncycastle.util.encoders.Base64; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import io.jsonwebtoken.Claims; import javax.annotation.Resource; import javax.crypto.spec.SecretKeySpec; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /**  * @author   * @date 记录用户操作记录入参  */ @Component public class OperationLogInterceptor implements HandlerInterceptor {     /**      * Jwt secert串,需要与加密token的秘钥一致      */     public static final String JWT_SECERT = "23142d7a9s7d66970ad07d8sa";     /**      * 需要记录的接口URL      */     private static List<String> pathList = new ArrayList<>();     static {         pathList.add("/mdms/model");     }     @Resource     private FunctionDOMapper functionDOMapper;//菜单动能sql     @Resource     private UserOperationHistoryDOMapper historyDOMapper;//操作日志记录表     @Override     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {         String servletPath = "" + request.getServletPath();         String method = request.getMethod();         pathList.forEach(path -> {             if (servletPath.contains(path)){                 Cookie[] cookies = request.getCookies();                 if (cookies != null) {                     for (Cookie cookie : cookies) {                         //获取token在请求中                         if (cookie.getName().equals("_qjt_ac_")) {                             String token = cookie.getValue();                             /**解密token**/                             byte[] encodeKey = Base64.decode(JWT_SECERT);                             Claims claims = null;                             try {                                 SecretKeySpec keySpec = new SecretKeySpec(encodeKey, 0, encodeKey.length, "AES");                                 claims = Jwts.parser().setSigningKey(keySpec).parseClaimsJws(token).getBody();                             } catch (Exception e) {                                 return;                             }                             //用户账号                             String account = claims.getSubject();                             //查询URL在功能表中的功能                             functionDOMapper.selectOne(servletPath, method);                             //获取入参                             RequestWrapper requestWrapper = null;                             if (request instanceof HttpServletRequest) {                                 requestWrapper = new RequestWrapper(request);                             }                             Map<String,Object> map = new HashMap<>();                             map.put("parameter", JSONObject.parse(requestWrapper.getBody()));                             historyDOMapper.insert(map);//将操作信息入库                         }                     }                 }              }         });         return true;     } }

      注册拦截器

      package com.example.demo.config; import com.example.demo.intercept.OperationLogInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /**  * @author   * @date 注册拦截器  */ public class WebConfig implements WebMvcConfigurer {     @Bean     public HandlerInterceptor getOperationLogInterceptor() {         return new OperationLogInterceptor();     }     @Override     public void addInterceptors(InterceptorRegistry registry){         registry.addInterceptor(getOperationLogInterceptor()).addPathPatterns("/**").excludePathPatterns();     } }

      记录返参日志

      package com.example.demo.intercept; import com.alibaba.fastjson.JSONObject; import org.bouncycastle.util.encoders.Base64; import org.springframework.core.MethodParameter; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.util.CollectionUtils; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; import javax.annotation.Resource; import javax.crypto.spec.SecretKeySpec; import javax.servlet.http.HttpServletRequest; import javax.xml.ws.Response; import java.util.*; /**  * @author   * @date 记录用户操作日志返参  */ @ControllerAdvice(basePackages = "项目包") public class GetResponseBody implements ResponseBodyAdvice<Object> {     /**      * Jwt secert串,需要与加密token的秘钥一致      */     public static final String JWT_SECERT = "23142d7a9s7d66970ad07d8sa";     /**      * 需要记录的接口URL      */     private static List<String> pathList = new ArrayList<>();     static {         pathList.add("/mdms/model");     }     @Resource     private FunctionDOMapper functionDOMapper;//菜单动能sql     @Resource     private UserOperationHistoryDOMapper historyDOMapper;//操作日志记录表     @Override     public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {         return false;     }     @Override     public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {         String path = serverHttpRequest.getURI().getPath();         String methodValue = serverHttpRequest.getMethodValue();         pathList.forEach(serverPath -> {             if (path.contains(serverPath)) {                 HashMap<String, String> cookieMap = new HashMap<>();                 HttpHeaders headers = serverHttpRequest.getHeaders();                 List<String> cookieList = headers.get("cookie");                 if (CollectionUtils.isEmpty(cookieList)) {                     return;                 }                 String replaceAll = cookieList.get(0).replaceAll(";", "").replaceAll(";", "");                 String[] split = replaceAll.split(";");                 for (String cookie : split) {                     String[] param = cookie.split("=");                     cookieMap.put(param[0], param[1]);                 }                 String token = cookieMap.get("_qjt_ac_");                 byte[] encodeKey = Base64.decode(JWT_SECERT);                 Claims claims = null;                 try {                     SecretKeySpec keySpec = new SecretKeySpec(encodeKey, 0, encodeKey.length, "AES");                     claims = Jwts.parser().setSigningKey(keySpec).parseClaimsJws(token).getBody();                 } catch (Exception e) {                     return;                 }                 //用户账号                 String account = claims.getSubject();                 //查询URL在功能表中的功能                 functionDOMapper.selectOne(servletPath, method);                 //获取返参                 List<Object> list = historyDOMapper.select("功能表参数", account);                 list.sort((Object1,Object2)->Object2.getTime().compareTo(Object1.getTime()));//将查询到的操作记录按时间降序排列                 Object history = list.get(0);                 if (body instanceof Response) {                     Response response = (Response) body;                     JSONObject jsonObject = JSONObject.parseObject(history.getParam());                     jsonObject.put("message",response.getMessage());                     jsonObject.put("body",response.getData());                     history.setParam(jsonObject.toString());                     history.setDes(response.getMessage());                 }                 historyDOMapper.updateByPrimaryKeySelective(history);//将操作信息更新             }         });         return body;     } }

      以上为个人经验,希望能给大家一个参考,也希望大家多多支持自由互联。

      标签:实例目录