摘要:在使用開發(fā)的時(shí)候希望把,,等等的信息記錄到我們的日志中,方便我們排查問題,也能對(duì)系統(tǒng)的數(shù)據(jù)做一些統(tǒng)計(jì)。使用了來攔截并分發(fā)請(qǐng)求,我們只要自己實(shí)現(xiàn)一個(gè)并在其中對(duì)請(qǐng)求和響應(yīng)做處理打印到日志中即可。
在使用Spring Boot開發(fā) web api 的時(shí)候希望把 request,request header ,response reponse header , uri, method 等等的信息記錄到我們的日志中,方便我們排查問題,也能對(duì)系統(tǒng)的數(shù)據(jù)做一些統(tǒng)計(jì)。
Spring 使用了 DispatcherServlet 來攔截并分發(fā)請(qǐng)求,我們只要自己實(shí)現(xiàn)一個(gè) DispatcherServlet 并在其中對(duì)請(qǐng)求和響應(yīng)做處理打印到日志中即可。
我們實(shí)現(xiàn)一個(gè)自己的分發(fā) Servlet ,它繼承于 DispatcherServlet,我們實(shí)現(xiàn)自己的 doDispatch(HttpServletRequest request, HttpServletResponse response) 方法。
public class LoggableDispatcherServlet extends DispatcherServlet { private static final Logger logger = LoggerFactory.getLogger("HttpLogger"); private static final ObjectMapper mapper = new ObjectMapper(); @Override protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request); ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response); //創(chuàng)建一個(gè) json 對(duì)象,用來存放 http 日志信息 ObjectNode rootNode = mapper.createObjectNode(); rootNode.put("uri", requestWrapper.getRequestURI()); rootNode.put("clientIp", requestWrapper.getRemoteAddr()); rootNode.set("requestHeaders", mapper.valueToTree(getRequestHeaders(requestWrapper))); String method = requestWrapper.getMethod(); rootNode.put("method", method); try { super.doDispatch(requestWrapper, responseWrapper); } finally { if(method.equals("GET")) { rootNode.set("request", mapper.valueToTree(requestWrapper.getParameterMap())); } else { JsonNode newNode = mapper.readTree(requestWrapper.getContentAsByteArray()); rootNode.set("request", newNode); } rootNode.put("status", responseWrapper.getStatus()); JsonNode newNode = mapper.readTree(responseWrapper.getContentAsByteArray()); rootNode.set("response", newNode); responseWrapper.copyBodyToResponse(); rootNode.set("responseHeaders", mapper.valueToTree(getResponsetHeaders(responseWrapper))); logger.info(rootNode.toString()); } } private MapgetRequestHeaders(HttpServletRequest request) { Map headers = new HashMap<>(); Enumeration headerNames = request.getHeaderNames(); while (headerNames.hasMoreElements()) { String headerName = headerNames.nextElement(); headers.put(headerName, request.getHeader(headerName)); } return headers; } private Map getResponsetHeaders(ContentCachingResponseWrapper response) { Map headers = new HashMap<>(); Collection headerNames = response.getHeaderNames(); for (String headerName : headerNames) { headers.put(headerName, response.getHeader(headerName)); } return headers; }
在 LoggableDispatcherServlet 中,我們可以通過 HttpServletRequest 中的 InputStream 或 reader 來獲取請(qǐng)求的數(shù)據(jù),但如果我們直接在這里讀取了流或內(nèi)容,到后面的邏輯將無(wú)法進(jìn)行下去,所以需要實(shí)現(xiàn)一個(gè)可以緩存的 HttpServletRequest。好在 Spring 提供這樣的類,就是 ContentCachingRequestWrapper 和 ContentCachingResponseWrapper, 根據(jù)官方的文檔這兩個(gè)類正好是來干這個(gè)事情的,我們只要將 HttpServletRequest 和 HttpServletResponse 轉(zhuǎn)化即可。
HttpServletRequest wrapper that caches all content read from the input stream and reader, and allows this content to be retrieved via a byte array.Used e.g. by AbstractRequestLoggingFilter. Note: As of Spring Framework 5.0, this wrapper is built on the Servlet 3.1 API.
HttpServletResponse wrapper that caches all content written to the output stream and writer, and allows this content to be retrieved via a byte array.
Used e.g. by ShallowEtagHeaderFilter. Note: As of Spring Framework 5.0, this wrapper is built on the Servlet 3.1 API.
實(shí)現(xiàn)好我們的 LoggableDispatcherServlet后,接下來就是要指定使用 LoggableDispatcherServlet 來分發(fā)請(qǐng)求。
@SpringBootApplication public class SbDemoApplication implements ApplicationRunner { public static void main(String[] args) { SpringApplication.run(SbDemoApplication.class, args); } @Bean public ServletRegistrationBean dispatcherRegistration() { return new ServletRegistrationBean(dispatcherServlet()); } @Bean(name = DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) public DispatcherServlet dispatcherServlet() { return new LoggableDispatcherServlet(); } }
增加一個(gè)簡(jiǎn)單的 Controller 來測(cè)試一下
@RestController @RequestMapping("/hello") public class HelloController { @RequestMapping(value = "/word", method = RequestMethod.POST) public Object hello(@RequestBody Object object) { return object; } }
使用 curl 發(fā)送一個(gè) Post 請(qǐng)求:
$ curl --header "Content-Type: application/json" --request POST --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, 17 Mar 2019 08:56:50 GMT", "Content-Type":"application/json;charset=UTF-8" } }
當(dāng)然打印出來是在一行中的,我進(jìn)行了一下格式化。我們還可以在日志中增加請(qǐng)求的時(shí)間,耗費(fèi)的時(shí)間以及異常信息等。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/73727.html
摘要:接口日志有啥用在我們?nèi)粘5拈_發(fā)過程中,我們可以通過接口日志去查看這個(gè)接口的一些詳細(xì)信息。在切入點(diǎn)返回內(nèi)容之后切入內(nèi)容可以用來對(duì)處理返回值做一些加工處理。 接口日志有啥用 在我們?nèi)粘5拈_發(fā)過程中,我們可以通過接口日志去查看這個(gè)接口的一些詳細(xì)信息。比如客戶端的IP,客戶端的類型,響應(yīng)的時(shí)間,請(qǐng)求的類型,請(qǐng)求的接口方法等等,我們可以對(duì)這些數(shù)據(jù)進(jìn)行統(tǒng)計(jì)分析,提取出我們想要的信息。 怎么拿到接口...
摘要:學(xué)習(xí)筆記使用很容易創(chuàng)建一個(gè)獨(dú)立運(yùn)行運(yùn)行內(nèi)嵌容器準(zhǔn)生產(chǎn)級(jí)別的基于框架的項(xiàng)目,使用你可以不用或者只需要很少的配置。異常消息如果這個(gè)錯(cuò)誤是由異常引起的。錯(cuò)誤發(fā)生時(shí)請(qǐng)求的路徑。 Spring-Boot 1.5 學(xué)習(xí)筆記 使用Spring Boot很容易創(chuàng)建一個(gè)獨(dú)立運(yùn)行(運(yùn)行jar,內(nèi)嵌Servlet容器)、準(zhǔn)生產(chǎn)級(jí)別的基于Spring框架的項(xiàng)目,使用Spring Boot你可以不用或者只需要很...
摘要:概述之前講過容器的可視化監(jiān)控,即監(jiān)控容器的運(yùn)行情況,包括使用率內(nèi)存占用網(wǎng)絡(luò)狀況以及磁盤空間等等一系列信息。實(shí)戰(zhàn)一下中添加依賴啟動(dòng)應(yīng)用程序之后,只要在瀏覽器中輸入端點(diǎn)信息就能獲得應(yīng)用的一些狀態(tài)信息。 showImg(https://segmentfault.com/img/remote/1460000014684947); 概述 之前講過Docker容器的可視化監(jiān)控,即監(jiān)控容器的運(yùn)行情...
摘要:通用的應(yīng)用程序?qū)傩源碇鳈C(jī)代理端口嵌入式服務(wù)器配置屬性服務(wù)器應(yīng)該綁定到的網(wǎng)絡(luò)地 通用的應(yīng)用程序?qū)傩?② sendgrid(SendGridAutoConfiguration) spring.sendgrid.api-key= # SendGrid API key spring.sendgrid.proxy.host= # SendGrid 代理主機(jī) spring.sendgrid.pr...
閱讀 3549·2021-11-18 13:22
閱讀 2564·2021-09-23 11:53
閱讀 734·2019-08-30 13:17
閱讀 1351·2019-08-30 13:12
閱讀 904·2019-08-29 15:43
閱讀 1108·2019-08-29 12:53
閱讀 2833·2019-08-26 18:27
閱讀 1505·2019-08-26 11:52