SpringBoot中如何打印Http请求日志

 更新时间:2024年06月23日 15:03:24   作者:斗者_2013  
所有针对第三方的请求都强烈推荐打印请求日志,本文主要介绍了SpringBoot中如何打印Http请求日志,具有一定的参考价值,感兴趣的可以了解一下

前言

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

一、spring-rest-template-logger实战

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请求的日志级别

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

4、单元测试

@SpringBootTest
@Slf4j
class LimitApplicationTests {

    @Resource
    RestTemplate restTemplate;

    @Test
    void testHttpLog() throws Exception{
        String requestUrl = "https://api.uomg.com/api/icp";
        //构建json请求参数
        JSONObject params = new JSONObject();
        params.put("domain", "www.baidu.com");
        //请求头声明请求格式为json
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        //创建请求实体
        HttpEntity<String> entity = new HttpEntity<>(params.toString(), headers);
        //执行post请求,并响应返回字符串
        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":"查询域名不能为空"}

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

6、自定义日志打印格式

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

RestTemplate restTemplate = new RestTemplateBuilder()
	.customizers(new LoggingCustomizer(LogFactory.getLog(LoggingCustomizer.class), new MyLogFormatter()))
	.build();

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

public class DefaultLogFormatter implements LogFormatter {
    private static final Charset DEFAULT_CHARSET;

    public DefaultLogFormatter() {
    }

    //格式化请求
    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);
    }

   //格式化响应
    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的实现,定制自定的日志打印格式化类MyLogFormatter。

二、原理分析

首先分析下LoggingCustomizer类,通过查看源码发现,LoggingCustomizer继承自RestTemplateCustomizer,RestTemplateCustomizer表示RestTemplate的定制器,RestTemplateCustomizer是一个函数接口,只有一个方法customize,用来扩展定制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请求的入参
            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。

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

@Configuration
public class RestTemplateAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public RestTemplate restTemplate() {
        RestTemplate restTemplate = new RestTemplateBuilder()
                .customizers(new LoggingCustomizer())
                .build();
        return restTemplate;
    }
}

总结

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

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

到此这篇关于SpringBoot中如何打印Http请求日志的文章就介绍到这了,更多相关SpringBoot打印Http请求日志内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家! 

相关文章

  • Java利用钉钉机器人实现发送群消息

    Java利用钉钉机器人实现发送群消息

    这篇文章主要为大家详细介绍了Java语言如何通过钉钉机器人发送群消息通知,文中的示例代码讲解详细,感兴趣的小伙伴可以了解一下
    2022-09-09
  • 基于Springboot执行多个定时任务并动态获取定时任务信息

    基于Springboot执行多个定时任务并动态获取定时任务信息

    这篇文章主要为大家详细介绍了基于Springboot执行多个定时任务并动态获取定时任务信息,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-04-04
  • Fluent Mybatis让你摆脱Xml文件的技巧

    Fluent Mybatis让你摆脱Xml文件的技巧

    Fluent-Mybatis类似于Mybatis-Plus是对Mybatis进一步的封装,可以只用一个实体类对象,通过代码生成器,在编译的过程中生成所需要的各类文件,简化了项目的基础构建,提高开发效率,本文重点给大家介绍Fluent Mybaits让你摆脱Xml文件的技巧,一起看看吧
    2021-08-08
  • java实现时间控制的几种方案

    java实现时间控制的几种方案

    这篇文章主要介绍了java实现时间控制的几种方案,本文从多个方面给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-07-07
  • Java之CMS和G1垃圾回收过程的异同说明

    Java之CMS和G1垃圾回收过程的异同说明

    CMS垃圾回收器主要基于并发-清理算法,目的是减少停顿时间,通过四个主要阶段进行垃圾回收:初始标记、并发标记、重新标记和并发清理,G1垃圾回收器采用标记-整理算法,是JDK9后的默认垃圾收集器,设计为全功能全代收集器
    2024-09-09
  • Java Lombok实现手机号码校验的示例代码

    Java Lombok实现手机号码校验的示例代码

    手机号码校验通常是系统开发中最基础的功能之一,本文主要介绍了Java Lombok实现手机号码校验的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-07-07
  • Java如何获取Cookie和Session

    Java如何获取Cookie和Session

    Cookie 和 Session之间主要是通过 SessionId 关联起来的, SessionId是 Cookie 和 Session 之间的桥梁,这篇文章主要介绍了Java获取Cookie和Session的方法,需要的朋友可以参考下
    2024-01-01
  • Java中Integer方法实例详解

    Java中Integer方法实例详解

    这篇文章主要给大家介绍了关于Java中Integer方法的相关资料,Java中的Integer是int的包装类型,文中通过代码实例介绍的非常详细,需要的朋友可以参考下
    2023-08-08
  • java 基础教程之多线程详解及简单实例

    java 基础教程之多线程详解及简单实例

    这篇文章主要介绍了java 基础教程之多线程详解及简单实例的相关资料,线程的基本属性、如何创建线程、线程的状态切换以及线程通信,需要的朋友可以参考下
    2017-03-03
  • java方法重写实例分析

    java方法重写实例分析

    这篇文章主要介绍了java方法重写,较为详细的讲述了Java方法重写的注意事项,并附带实例加以说明,需要的朋友可以参考下
    2014-09-09

最新评论