How to elegantly print Http request logs in Spring Boot


Preface

In the process of project development, it is often necessary to use the Http protocol to request third-party interfaces, and for all third-party requests, it is strongly recommended to print request logs for problem tracking. The most common way is to encapsulate a tool class for Http requests, and customize some log printing in it, but this method is really low. This article will share how to print Http request logs elegantly in Spring Boot.


1. Spring-rest-template-logger in practice

First, we demonstrate the use of the general log printing component spring-rest-template-logger found on github.

Github address: Spring RestTemplate Logger

1. Introduce dependencies

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

2, Place RestTemplate

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

3. Configure the log level of RestTemplate request

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

4. Unit testing

@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. Log printing

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":"查询域名不能为空"}

It can be seen that we simply configured the LoggingCustomizer in the RestTemplate to realize the general printing of logs for http requests, and printed the input parameters and response results of the request in Request and Response respectively.

6. Customize log printing format

Customized log printing format can be implemented by inheriting the LogFormatter interface.

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

Default log printing format class 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);
    }
    ……
 }   

You can refer to the implementation of DefaultLogFormatter to customize the log printing formatting class MyLogFormatter.

2. Principle analysis

First analyze the LoggingCustomizer class. By viewing the source code, it is found that LoggingCustomizer inherits from RestTemplateCustomizer. RestTemplateCustomizer represents the customizer of RestTemplate. RestTemplateCustomizer is a function interface with only one method customize, which is used to expand and customize the properties of RestTemplate.

@FunctionalInterface
public interface RestTemplateCustomizer {
    
    
    void customize(RestTemplate restTemplate);
}

The core method customize of LoggingCustomizer:

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));
    }
}

Core methods in log interceptor:

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;
    }
}

Principle summary :
By adding a custom logging interceptor LoggingInterceptor to the restTemplate object, use the custom formatting class DefaultLogFormatter to print the http request log.

3. Optimization and improvement

You can learn from the implementation of spring-rest-template-logger and use the Spring Boot Start automated configuration principle to declare the automated configuration class RestTemplateAutoConfiguration and automatically configure RestTemplate.

Here are just some ideas. After understanding the principles of related components, we can easily customize general log printing components.

@Configuration
public class RestTemplateAutoConfiguration {
    
    

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

Summarize

The elegant printing here is not a format for http request log printing, but a log printing method with strong versatility, low code intrusion, and customizability by injecting interceptors into RestTemplate.

  • 在Spring Boot中http请求都推荐采用RestTemplate模板类, without the need to customize static tool classes for http requests, such as HttpUtil
  • It is recommended to use @Bean in the configuration class to configure the RestTemplate class into a unified and universal singleton object and inject it into the Spring container instead of re-declaring it every time it is used.
  • It is recommended to use interceptors to achieve universal printing of http request logs

Guess you like

Origin blog.csdn.net/w1014074794/article/details/131371860