SpringBoot记录Http请求日志的方法
在使用SpringBoot开发webapi的时候希望把request,requestheader,responsereponseheader,uri,method等等的信息记录到我们的日志中,方便我们排查问题,也能对系统的数据做一些统计。
Spring使用了DispatcherServlet来拦截并分发请求,我们只要自己实现一个DispatcherServlet并在其中对请求和响应做处理打印到日志中即可。
我们实现一个自己的分发Servlet,它继承于DispatcherServlet,我们实现自己的doDispatch(HttpServletRequestrequest,HttpServletResponseresponse)方法。
publicclassLoggableDispatcherServletextendsDispatcherServlet{ privatestaticfinalLoggerlogger=LoggerFactory.getLogger("HttpLogger"); privatestaticfinalObjectMappermapper=newObjectMapper(); @Override protectedvoiddoDispatch(HttpServletRequestrequest,HttpServletResponseresponse)throwsException{ ContentCachingRequestWrapperrequestWrapper=newContentCachingRequestWrapper(request); ContentCachingResponseWrapperresponseWrapper=newContentCachingResponseWrapper(response); //创建一个json对象,用来存放http日志信息 ObjectNoderootNode=mapper.createObjectNode(); rootNode.put("uri",requestWrapper.getRequestURI()); rootNode.put("clientIp",requestWrapper.getRemoteAddr()); rootNode.set("requestHeaders",mapper.valueToTree(getRequestHeaders(requestWrapper))); Stringmethod=requestWrapper.getMethod(); rootNode.put("method",method); try{ super.doDispatch(requestWrapper,responseWrapper); }finally{ if(method.equals("GET")){ rootNode.set("request",mapper.valueToTree(requestWrapper.getParameterMap())); }else{ JsonNodenewNode=mapper.readTree(requestWrapper.getContentAsByteArray()); rootNode.set("request",newNode); } rootNode.put("status",responseWrapper.getStatus()); JsonNodenewNode=mapper.readTree(responseWrapper.getContentAsByteArray()); rootNode.set("response",newNode); responseWrapper.copyBodyToResponse(); rootNode.set("responseHeaders",mapper.valueToTree(getResponsetHeaders(responseWrapper))); logger.info(rootNode.toString()); } } privateMapgetRequestHeaders(HttpServletRequestrequest){ Map headers=newHashMap<>(); Enumeration headerNames=request.getHeaderNames(); while(headerNames.hasMoreElements()){ StringheaderName=headerNames.nextElement(); headers.put(headerName,request.getHeader(headerName)); } returnheaders; } privateMap getResponsetHeaders(ContentCachingResponseWrapperresponse){ Map headers=newHashMap<>(); Collection headerNames=response.getHeaderNames(); for(StringheaderName:headerNames){ headers.put(headerName,response.getHeader(headerName)); } returnheaders; }
在LoggableDispatcherServlet中,我们可以通过HttpServletRequest中的InputStream或reader来获取请求的数据,但如果我们直接在这里读取了流或内容,到后面的逻辑将无法进行下去,所以需要实现一个可以缓存的HttpServletRequest。好在Spring提供这样的类,就是ContentCachingRequestWrapper和ContentCachingResponseWrapper,根据官方的文档这两个类正好是来干这个事情的,我们只要将HttpServletRequest和HttpServletResponse转化即可。
HttpServletRequestwrapperthatcachesallcontentreadfromtheinputstreamandreader,andallowsthiscontenttoberetrievedviaabytearray.
Usede.g.byAbstractRequestLoggingFilter.Note:AsofSpringFramework5.0,thiswrapperisbuiltontheServlet3.1API.HttpServletResponsewrapperthatcachesallcontentwrittentotheoutputstreamandwriter,andallowsthiscontenttoberetrievedviaabytearray.
Usede.g.byShallowEtagHeaderFilter.Note:AsofSpringFramework5.0,thiswrapperisbuiltontheServlet3.1API.
实现好我们的LoggableDispatcherServlet后,接下来就是要指定使用LoggableDispatcherServlet来分发请求。
@SpringBootApplication publicclassSbDemoApplicationimplementsApplicationRunner{ publicstaticvoidmain(String[]args){ SpringApplication.run(SbDemoApplication.class,args); } @Bean publicServletRegistrationBeandispatcherRegistration(){ returnnewServletRegistrationBean(dispatcherServlet()); } @Bean(name=DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) publicDispatcherServletdispatcherServlet(){ returnnewLoggableDispatcherServlet(); } }
增加一个简单的Controller来测试一下
@RestController @RequestMapping("/hello") publicclassHelloController{ @RequestMapping(value="/word",method=RequestMethod.POST) publicObjecthello(@RequestBodyObjectobject){ returnobject; } }
使用curl发送一个Post请求:
$curl--header"Content-Type:application/json"\ --requestPOST\ --data'{"username":"xyz","password":"xyz"}'\ http://localhost:8080/hello/word {"username":"xyz","password":"xyz"}
查看打印的日志:
{ "uri":"/hello/word", "clientIp":"0:0:0:0:0:0:0:1", "requestHeaders":{ "content-length":"35", "host":"localhost:8080", "content-type":"application/json", "user-agent":"curl/7.54.0", "accept":"*/*" }, "method":"POST", "request":{ "username":"xyz", "password":"xyz" }, "status":200, "response":{ "username":"xyz", "password":"xyz" }, "responseHeaders":{ "Content-Length":"35", "Date":"Sun,17Mar201908:56:50GMT", "Content-Type":"application/json;charset=UTF-8" } }
当然打印出来是在一行中的,我进行了一下格式化。我们还可以在日志中增加请求的时间,耗费的时间以及异常信息等。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。