Spring/SpringBoot 请求/响应日志打印

Spring/SpringBoot 请求/响应日志打印

前言

一个请求日志拦截的Demo 使用Filer方式,不进行Tomact 骚操作处理

依赖

为了少些几行代码 使用了 Hutool 工具包

废话少说直接上代码

1、拦截器

import cn.hutool.extra.servlet.JakartaServletUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingResponseWrapper;

import java.io.IOException;

/**
 * 

* http 请求-响应日志打印 *

*/
@Slf4j @Component public class RequestLogFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { //请求接收时间毫秒数,用来计算请求处理时间 long receiveTime = System.currentTimeMillis(); //使用包装类将request body 进行缓存,解决request.getInputStream()不可重复调用问题 RequestContentCachingWrapper requestWrapper = new RequestContentCachingWrapper(request); //response 包装类 缓存并获取最终响应数据报文信息 ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response); try { //打印请求日志 logRequest(requestWrapper); //请求处理 filterChain.doFilter(requestWrapper,responseWrapper); //响应日志处理 logResponse(request,responseWrapper,receiveTime); } finally { //最终需要执行此方法,将响应数据写入response中 responseWrapper.copyBodyToResponse(); } } /** *

* 请求报文获取 *

* * @param request http 请求数据 */
private void logRequest(RequestContentCachingWrapper request){ String requestParam = null; try { if (request.isJsonRequest()){ String body = JakartaServletUtil.getBody(request); if (StringUtils.isNotBlank(body)){ requestParam = body; } }else { requestParam = new ObjectMapper().writeValueAsString(request.getParameterMap()); } log.info("HttpRequest [{}::{}] receive:{}",request.getMethod(),request.getRequestURI(), requestParam); } catch (Exception e) { log.error("获取请求参数异常!",e); } } /** *

* 响应报文获取 *

* * @param request 请求参数 * @param responseWrapper http 请求数据 */
private String logResponse(HttpServletRequest request, ContentCachingResponseWrapper responseWrapper, long receiveTime){ try { log.info("HttpResponse [{}::{}ms] response:{}",request.getRequestURI(), System.currentTimeMillis() - receiveTime,new String(responseWrapper.getContentAsByteArray())); } catch (Exception e) { log.error("响应数据获取失败",e); } return null; } }

2、 请求包装类

import jakarta.servlet.ReadListener;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.util.ContentCachingRequestWrapper;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 

* 自定义 ContentCachingRequestWrapper *

*/
public class RequestContentCachingWrapper extends ContentCachingRequestWrapper { /** * 是否第一次获取 */ private AtomicBoolean isFirst = new AtomicBoolean(true); /** * 参数构造 * @param request 原请求数据 */ public RequestContentCachingWrapper(HttpServletRequest request) { super(request); } @Override public ServletInputStream getInputStream() throws IOException { if(isFirst.get()){ //首次读取直接调父类的方法,这一次执行完之后 缓存流中有数据了 //后续读取就读缓存流里的。 isFirst.set(false); return super.getInputStream(); } return new CustomizeServletInputStream(super.getContentAsByteArray()); } /** * 判断是都时json请求 * @return 判断结果 */ protected boolean isJsonRequest() { String contentType = getContentType(); return (contentType != null && contentType.contains("application/json")); } class CustomizeServletInputStream extends ServletInputStream{ /** * sourceStream */ private InputStream sourceStream; /** * 是否已完成 */ private boolean finished = false; /** * 参数构造 */ public CustomizeServletInputStream(byte [] bytes) { this.sourceStream = new ByteArrayInputStream(bytes); } @Override public int read() throws IOException { int data = this.sourceStream.read(); if (data == -1) { this.finished = true; } return data; } @Override public int available() throws IOException { return this.sourceStream.available(); } @Override public void close() throws IOException { super.close(); this.sourceStream.close(); } @Override public boolean isFinished() { return this.finished; } @Override public boolean isReady() { return true; } @Override public void setReadListener(ReadListener listener) { } } }

你可能感兴趣的:(spring,spring,boot,servlet)