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){
Mapheaders=newHashMap<>();
EnumerationheaderNames=request.getHeaderNames();
while(headerNames.hasMoreElements()){
StringheaderName=headerNames.nextElement();
headers.put(headerName,request.getHeader(headerName));
}
returnheaders;
}
privateMapgetResponsetHeaders(ContentCachingResponseWrapperresponse){
Mapheaders=newHashMap<>();
CollectionheaderNames=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"
}
}
当然打印出来是在一行中的,我进行了一下格式化。我们还可以在日志中增加请求的时间,耗费的时间以及异常信息等。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。