1介绍在SpringMVC中,我们有时需要记录请求的内容和返回的内容,以便出现问题时进行排查。比较Header,RequestBody等。这些也可以记录在Controller中,但是放在Filter中会更方便。我们正在使用OncePerRequestFilter。2Loggingrequests2.1重复读流的问题可以通过以下代码读取requestbody:byte[]requestBody=StreamUtils.copyToByteArray(request.getInputStream());log.info("requestbody={}",new字符串(requestBody,StandardCharsets.UTF_8));但是从流中读取一次内容后,就不能再读取了。这导致在实际处理请求时出错失败。我们需要将Request对象转化为可重复读取的类。2.2通过Wrapper解决重复读取流的问题为了允许重复读取流,增加了如下Wrapper:publicclassPkslowRequestWrapperextendsHttpServletRequestWrapper{privatefinalbyte[]body;publicPkslowRequestWrapper(HttpServletRequestdyrequest)抛出IOException={)super(StreamUtils.copyToByteArray(request.getInputStream());}@OverridepublicServletInputStreamgetInputStream()抛出IOException{ByteArrayInputStreambyteArrayInputStream=newByteArrayInputStream(body);返回新的ServletInputStream(){@Overridepublicintread()throwsIOException{returnbyteArrayInputStream.read();}@OverridepublicbooleanisFinished(){returntrue;}@OverridepublicbooleanisReady(){returntrue;}@OverridepublicvoidsetReadListener(ReadListenerreadListener){}};}@OverridepublicBufferedReadergetReader()throwsIOException{returnnewBufferedReader(newInputStreamReader(getInputStream()));}}这里主要是在构造时读取流,然后存入变量body中,每次返回流从body构造返回即可在Filter中使用这个Wrapper如下:PkslowRequestWrapperrequest=newPkslowRequestWrapper(req);ServletInputStreamservletInputStream=request.getInputStream();Stringbody=StreamUtils.copyToString(servletInputStream,Charset.defaultCharset());log.info(PkslowRequestWrapper):{}",body);2.3内置Filter其实对于Request,SpringBoot提供了内置的Filter,可以直接记录请求,使用如下:web.filter。过滤器.setAfterMessagePrefix("CommonsRequestLoggingFilter请求:");返回过滤器;}}但只有开启debug级别日志时,logging:level:root:debug日志如下:DEBUG20356---[nio-8080-exec-1]o.s.w.f.CommonsRequestLoggingFilter:在请求之前[POST/hello/pkslow,client=127.0.0.1,headers=[authorization:"Basicxxxxxx",content-length:"37",host:"localhost:8080",connection:"Keep-Alive",用户代理:"Apache-HttpClient/4.5.13(Java/17.0.5)",接受编码:"gzip,deflate",内容类型:"application/json;charset=UTF-8"]]3记录返回和返回也是如此。如果存在流的不可重复读取的问题,使用Spring自带的ContentCachingResponseWrapper即可。ContentCachingResponseWrapperresponse=newContentCachingResponseWrapper(res);log.info("响应代码:{}",response.getStatus());StringresponseBody=newString(response.getContentAsByteArray(),response.getCharacterEncoding());log.info("响应主体:{}",responseBody);response.copyBodyToResponse();特别注意调用copyBodyToResponse()方法,否则无法将正文返回给请求者。4记录时间记录整个请求的处理时间,请参考:Java如何测量方法执行时间5测试测试:POSThttp://localhost:8080/hello/pkslowContent-Type:application/jsonAuthorization:Basicxxxxxx{"id":999,"value":"content"}执行日志结果如下:6总结也可以使用ContentCachingRequestWrapper解决请求流不可重复读取的问题,但是这个Wrapper有局限性,可以有关详细信息,请参阅其源代码。也有人提了一个Issue。代码请查看GitHub:https://github.com/LarryDpk/p...
