0


Spring Boot中如何优雅的打印Http请求日志

文章目录


前言

在项目开发过程中经常需要使用Http协议请求第三方接口,而所有针对第三方的请求都强烈推荐打印请求日志,以便问题追踪。最常见的做法是封装一个Http请求的工具类,在里面定制一些日志打印,但这种做法真的比较low,本文将分享一下在Spring Boot中如何优雅的打印Http请求日志。


一、spring-rest-template-logger实战

首先我们演示下在github上找到的通用日志打印组件spring-rest-template-logger的使用。

Github地址:Spring RestTemplate Logger

1、引入依赖

<dependency><groupId>org.hobsoft.spring</groupId><artifactId>spring-rest-template-logger</artifactId><version>2.0.0</version></dependency>

2、配置RestTemplate

@ConfigurationpublicclassRestTemplateConfig{@BeanpublicRestTemplaterestTemplate(){RestTemplate restTemplate =newRestTemplateBuilder().customizers(newLoggingCustomizer()).build();return restTemplate;}}

3、配置RestTemplate请求的日志级别

logging.level.org.hobsoft.spring.resttemplatelogger.LoggingCustomizer= DEBUG

4、单元测试

@SpringBootTest@Slf4jclassLimitApplicationTests{@ResourceRestTemplate restTemplate;@TestvoidtestHttpLog()throwsException{String requestUrl ="https://api.uomg.com/api/icp";//构建json请求参数JSONObject params =newJSONObject();
        params.put("domain","www.baidu.com");//请求头声明请求格式为jsonHttpHeaders headers =newHttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);//创建请求实体HttpEntity<String> entity =newHttpEntity<>(params.toString(), headers);//执行post请求,并响应返回字符串String resText = restTemplate.postForObject(requestUrl, entity,String.class);System.out.println(resText);}}

5、日志打印

2023-06-2510: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-2510:43:45.849 DEBUG [Sleuth,b6ac76a169b1af33,b6ac76a169b1af33]63501---[           main]o.h.s.r.LoggingCustomizer:Response:200{"code":200901,"msg":"查询域名不能为空"}{"code":200901,"msg":"查询域名不能为空"}

可以看见,我们只是简单的向RestTemplate中配置了LoggingCustomizer,就实现了http请求的日志通用化打印,在Request和Response中分别打印出了请求的入参和响应结果。

6、自定义日志打印格式

可以通过继承LogFormatter接口实现自定义的日志打印格式。

RestTemplate restTemplate =newRestTemplateBuilder().customizers(newLoggingCustomizer(LogFactory.getLog(LoggingCustomizer.class),newMyLogFormatter())).build();

默认的日志打印格式化类DefaultLogFormatter:

publicclassDefaultLogFormatterimplementsLogFormatter{privatestaticfinalCharset DEFAULT_CHARSET;publicDefaultLogFormatter(){}//格式化请求publicStringformatRequest(HttpRequest request,byte[] body){String formattedBody =this.formatBody(body,this.getCharset(request));returnString.format("Request: %s %s %s", request.getMethod(), request.getURI(), formattedBody);}//格式化响应publicStringformatResponse(ClientHttpResponse response)throwsIOException{String formattedBody =this.formatBody(StreamUtils.copyToByteArray(response.getBody()),this.getCharset(response));returnString.format("Response: %s %s", response.getStatusCode().value(), formattedBody);}
    ……
 }

可以参考DefaultLogFormatter的实现,定制自定的日志打印格式化类MyLogFormatter。

二、原理分析

首先分析下LoggingCustomizer类,通过查看源码发现,LoggingCustomizer继承自RestTemplateCustomizer,RestTemplateCustomizer表示RestTemplate的定制器,RestTemplateCustomizer是一个函数接口,只有一个方法customize,用来扩展定制RestTemplate的属性。

@FunctionalInterfacepublicinterfaceRestTemplateCustomizer{voidcustomize(RestTemplate restTemplate);}

LoggingCustomizer的核心方法customize:

publicclassLoggingCustomizerimplementsRestTemplateCustomizer{publicvoidcustomize(RestTemplate restTemplate){
        restTemplate.setRequestFactory(newBufferingClientHttpRequestFactory(restTemplate.getRequestFactory()));//向restTemplate中注入了日志拦截器
        restTemplate.getInterceptors().add(newLoggingInterceptor(this.log,this.formatter));}}

日志拦截器中的核心方法:

publicclassLoggingInterceptorimplementsClientHttpRequestInterceptor{privatefinalLog log;privatefinalLogFormatter formatter;publicLoggingInterceptor(Log log,LogFormatter formatter){this.log = log;this.formatter = formatter;}publicClientHttpResponseintercept(HttpRequest request,byte[] body,ClientHttpRequestExecution execution)throwsIOException{if(this.log.isDebugEnabled()){//打印http请求的入参this.log.debug(this.formatter.formatRequest(request, body));}ClientHttpResponse response = execution.execute(request, body);if(this.log.isDebugEnabled()){//打印http请求的响应结果this.log.debug(this.formatter.formatResponse(response));}return response;}}

原理小结
通过向restTemplate对象中添加自定义的日志拦截器LoggingInterceptor,使用自定义的格式化类DefaultLogFormatter来打印http请求的日志。

三、优化改进

可以借鉴spring-rest-template-logger的实现,通过Spring Boot Start自动化配置原理, 声明自动化配置类RestTemplateAutoConfiguration,自动配置RestTemplate。

这里只是提供一些思路,搞清楚相关组件的原理后,我们就可以轻松定制通用的日志打印组件。

@ConfigurationpublicclassRestTemplateAutoConfiguration{@Bean@ConditionalOnMissingBeanpublicRestTemplaterestTemplate(){RestTemplate restTemplate =newRestTemplateBuilder().customizers(newLoggingCustomizer()).build();return restTemplate;}}

总结

这里的优雅打印并不是针对http请求日志打印的格式,而是通过向RestTemplate中注入拦截器实现通用性强、代码侵入性小、具有可定制性的日志打印方式。

  • 在Spring Boot中http请求都推荐采用RestTemplate模板类,而无需自定义http请求的静态工具类,比如HttpUtil
  • 推荐在配置类中采用@Bean将RestTemplate类配置成统一通用的单例对象注入到Spring 容器中,而不是每次使用的时候重新声明。
  • 推荐采用拦截器实现http请求日志的通用化打印
标签: spring boot http java

本文转载自: https://blog.csdn.net/w1014074794/article/details/131371860
版权归原作者 斗者_2013 所有, 如有侵权,请联系我们删除。

“Spring Boot中如何优雅的打印Http请求日志”的评论:

还没有评论