SpringBoot中如何打印Http請(qǐng)求日志
前言
在項(xiàng)目開發(fā)過程中經(jīng)常需要使用Http協(xié)議請(qǐng)求第三方接口,而所有針對(duì)第三方的請(qǐng)求都強(qiáng)烈推薦打印請(qǐng)求日志,以便問題追蹤。最常見的做法是封裝一個(gè)Http請(qǐng)求的工具類,在里面定制一些日志打印,但這種做法真的比較low,本文將分享一下在Spring Boot中如何優(yōu)雅的打印Http請(qǐng)求日志。
一、spring-rest-template-logger實(shí)戰(zhàn)
1、引入依賴
<dependency> <groupId>org.hobsoft.spring</groupId> <artifactId>spring-rest-template-logger</artifactId> <version>2.0.0</version> </dependency>
2、配置RestTemplate
@Configuration public class RestTemplateConfig { @Bean public RestTemplate restTemplate() { RestTemplate restTemplate = new RestTemplateBuilder() .customizers(new LoggingCustomizer()) .build(); return restTemplate; } }
3、配置RestTemplate請(qǐng)求的日志級(jí)別
logging.level.org.hobsoft.spring.resttemplatelogger.LoggingCustomizer = DEBUG
4、單元測(cè)試
@SpringBootTest @Slf4j class LimitApplicationTests { @Resource RestTemplate restTemplate; @Test void testHttpLog() throws Exception{ String requestUrl = "https://api.uomg.com/api/icp"; //構(gòu)建json請(qǐng)求參數(shù) JSONObject params = new JSONObject(); params.put("domain", "www.baidu.com"); //請(qǐng)求頭聲明請(qǐng)求格式為json HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); //創(chuàng)建請(qǐng)求實(shí)體 HttpEntity<String> entity = new HttpEntity<>(params.toString(), headers); //執(zhí)行post請(qǐng)求,并響應(yīng)返回字符串 String resText = restTemplate.postForObject(requestUrl, entity, String.class); System.out.println(resText); } }
5、日志打印
2023-06-25 10:43:44.780 DEBUG [Sleuth,b6ac76a169b1af33,b6ac76a169b1af33] 63501 --- [ main] o.h.s.r.LoggingCustomizer : Request: POST https://api.uomg.com/api/icp {"domain":"www.baidu.com"}
2023-06-25 10:43:45.849 DEBUG [Sleuth,b6ac76a169b1af33,b6ac76a169b1af33] 63501 --- [ main] o.h.s.r.LoggingCustomizer : Response: 200 {"code":200901,"msg":"查詢域名不能為空"}
{"code":200901,"msg":"查詢域名不能為空"}
可以看見,我們只是簡(jiǎn)單的向RestTemplate中配置了LoggingCustomizer,就實(shí)現(xiàn)了http請(qǐng)求的日志通用化打印,在Request和Response中分別打印出了請(qǐng)求的入?yún)⒑晚憫?yīng)結(jié)果。
6、自定義日志打印格式
可以通過繼承LogFormatter接口實(shí)現(xiàn)自定義的日志打印格式。
RestTemplate restTemplate = new RestTemplateBuilder() .customizers(new LoggingCustomizer(LogFactory.getLog(LoggingCustomizer.class), new MyLogFormatter())) .build();
默認(rèn)的日志打印格式化類DefaultLogFormatter:
public class DefaultLogFormatter implements LogFormatter { private static final Charset DEFAULT_CHARSET; public DefaultLogFormatter() { } //格式化請(qǐng)求 public String formatRequest(HttpRequest request, byte[] body) { String formattedBody = this.formatBody(body, this.getCharset(request)); return String.format("Request: %s %s %s", request.getMethod(), request.getURI(), formattedBody); } //格式化響應(yīng) public String formatResponse(ClientHttpResponse response) throws IOException { String formattedBody = this.formatBody(StreamUtils.copyToByteArray(response.getBody()), this.getCharset(response)); return String.format("Response: %s %s", response.getStatusCode().value(), formattedBody); } …… }
可以參考DefaultLogFormatter的實(shí)現(xiàn),定制自定的日志打印格式化類MyLogFormatter。
二、原理分析
首先分析下LoggingCustomizer類,通過查看源碼發(fā)現(xiàn),LoggingCustomizer繼承自RestTemplateCustomizer,RestTemplateCustomizer表示RestTemplate的定制器,RestTemplateCustomizer是一個(gè)函數(shù)接口,只有一個(gè)方法customize,用來擴(kuò)展定制RestTemplate的屬性。
@FunctionalInterface public interface RestTemplateCustomizer { void customize(RestTemplate restTemplate); }
LoggingCustomizer的核心方法customize:
public class LoggingCustomizer implements RestTemplateCustomizer{ public void customize(RestTemplate restTemplate) { restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(restTemplate.getRequestFactory())); //向restTemplate中注入了日志攔截器 restTemplate.getInterceptors().add(new LoggingInterceptor(this.log, this.formatter)); } }
日志攔截器中的核心方法:
public class LoggingInterceptor implements ClientHttpRequestInterceptor { private final Log log; private final LogFormatter formatter; public LoggingInterceptor(Log log, LogFormatter formatter) { this.log = log; this.formatter = formatter; } public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { if (this.log.isDebugEnabled()) { //打印http請(qǐng)求的入?yún)? this.log.debug(this.formatter.formatRequest(request, body)); } ClientHttpResponse response = execution.execute(request, body); if (this.log.isDebugEnabled()) { //打印http請(qǐng)求的響應(yīng)結(jié)果 this.log.debug(this.formatter.formatResponse(response)); } return response; } }
原理小結(jié):
通過向restTemplate對(duì)象中添加自定義的日志攔截器LoggingInterceptor,使用自定義的格式化類DefaultLogFormatter來打印http請(qǐng)求的日志。
三、優(yōu)化改進(jìn)
可以借鑒spring-rest-template-logger的實(shí)現(xiàn),通過Spring Boot Start自動(dòng)化配置原理, 聲明自動(dòng)化配置類RestTemplateAutoConfiguration,自動(dòng)配置RestTemplate。
這里只是提供一些思路,搞清楚相關(guān)組件的原理后,我們就可以輕松定制通用的日志打印組件。
@Configuration public class RestTemplateAutoConfiguration { @Bean @ConditionalOnMissingBean public RestTemplate restTemplate() { RestTemplate restTemplate = new RestTemplateBuilder() .customizers(new LoggingCustomizer()) .build(); return restTemplate; } }
總結(jié)
這里的優(yōu)雅打印并不是針對(duì)http請(qǐng)求日志打印的格式,而是通過向RestTemplate中注入攔截器實(shí)現(xiàn)通用性強(qiáng)、代碼侵入性小、具有可定制性的日志打印方式。
在Spring Boot中http請(qǐng)求都推薦采用RestTemplate模板類
,而無需自定義http請(qǐng)求的靜態(tài)工具類,比如HttpUtil- 推薦在配置類中采用@Bean將RestTemplate類配置成統(tǒng)一通用的單例對(duì)象注入到Spring 容器中,而不是每次使用的時(shí)候重新聲明。
- 推薦采用攔截器實(shí)現(xiàn)http請(qǐng)求日志的通用化打印
到此這篇關(guān)于SpringBoot中如何打印Http請(qǐng)求日志的文章就介紹到這了,更多相關(guān)SpringBoot打印Http請(qǐng)求日志內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java利用釘釘機(jī)器人實(shí)現(xiàn)發(fā)送群消息
這篇文章主要為大家詳細(xì)介紹了Java語言如何通過釘釘機(jī)器人發(fā)送群消息通知,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2022-09-09基于Springboot執(zhí)行多個(gè)定時(shí)任務(wù)并動(dòng)態(tài)獲取定時(shí)任務(wù)信息
這篇文章主要為大家詳細(xì)介紹了基于Springboot執(zhí)行多個(gè)定時(shí)任務(wù)并動(dòng)態(tài)獲取定時(shí)任務(wù)信息,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-04-04java實(shí)現(xiàn)時(shí)間控制的幾種方案
這篇文章主要介紹了java實(shí)現(xiàn)時(shí)間控制的幾種方案,本文從多個(gè)方面給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-07-07Java?Lombok實(shí)現(xiàn)手機(jī)號(hào)碼校驗(yàn)的示例代碼
手機(jī)號(hào)碼校驗(yàn)通常是系統(tǒng)開發(fā)中最基礎(chǔ)的功能之一,本文主要介紹了Java?Lombok實(shí)現(xiàn)手機(jī)號(hào)碼校驗(yàn)的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07java 基礎(chǔ)教程之多線程詳解及簡(jiǎn)單實(shí)例
這篇文章主要介紹了java 基礎(chǔ)教程之多線程詳解及簡(jiǎn)單實(shí)例的相關(guān)資料,線程的基本屬性、如何創(chuàng)建線程、線程的狀態(tài)切換以及線程通信,需要的朋友可以參考下2017-03-03