MDC实现RestTemplate、okHttp的请求方式日志traceId链路跟踪

导读:本篇文章讲解 MDC实现RestTemplate、okHttp的请求方式日志traceId链路跟踪,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

项目代码:

https://gitee.com/gangye/springboot_mutiDemos/releases/v1.1

案例:在springboot_demoB应用使用RestTemplate调动springboot_demoA的服务,将B服务的traceId塞入到A的traceId中

MDC实现RestTemplate、okHttp的请求方式日志traceId链路跟踪MDC实现RestTemplate、okHttp的请求方式日志traceId链路跟踪

 成功将traceId塞入,下面直接上代码,相应的代码已上传资源,在之前的v1.0上增加了RestTemplate和okhttp请求方式的拦截做MDC的traceId日志处理
https://gitee.com/gangye/springboot_mutiDemos/releases/v1.1

此处我在RestTemplate中使用了okHttp的方式去连接请求,因为okHttp请求可以设置连接池,自定义超时时间,服务代理等功能,所以我使用了okhttp的方式,没有使用默认的RestTemplate,当然若使用原生的RestTemplate,我也写了相应的拦截功能作为参考(亲测也可实现功能)

在application.yml中增加okHttp的连接配置

MDC实现RestTemplate、okHttp的请求方式日志traceId链路跟踪

 编写RestTemplateConfig类,创建RestTemplate的Bean,和配置okHttpClient的Bean,后期若使用okHttp请求连接可以直接使用okHttpClient的Bean

import com.csrcb.interceptor.OkHttpLoggerInterceptor;
import com.csrcb.interceptor.RestTemplateInterator;
import okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

import java.util.Arrays;
import java.util.concurrent.TimeUnit;

/**
 * @Classname RestTemplateConfig
 * @Description RestTemplate配置,内部使用okhttp请求连接,使用okhttp的请求连接池,超时等特性
 * @Date 2021/8/18 15:02
 * @Created by gangye
 */
@Configuration
public class RestTemplateConfig {
    @Value("${ok.http.connect-timeout}")
    private Integer connectTimeout;

    @Value("${ok.http.read-timeout}")
    private Integer readTimeout;

    @Value("${ok.http.write-timeout}")
    private Integer writeTimeout;

    @Value("${ok.http.max-idle-connections}")
    private Integer maxIdleConnections;

    @Value("${ok.http.keep-alive-duration}")
    private Long keepAliveDuration;

    @Bean
    public RestTemplate okHttpRestTemplate(){
        ClientHttpRequestFactory factory = okHttpClientRequestFactory();
        RestTemplate restTemplate = new RestTemplate(factory);
        // 可以添加消息转换
        //restTemplate.setMessageConverters(...);
        // 可以增加拦截器
//        restTemplate.setInterceptors(Arrays.asList(new RestTemplateInterator()));
        return restTemplate;

        /*
        //此情形用于使用纯RestTemplate的情形下使用拦截器,传递traceId等日志链路
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.setInterceptors(Arrays.asList(new RestTemplateInterator()));
        return restTemplate;
        */

    }

    @Bean
    public ClientHttpRequestFactory okHttpClientRequestFactory(){
        //使用自定义的okHttpClient
        return new OkHttp3ClientHttpRequestFactory(okHttpClient());
    }

    @Bean
    public OkHttpClient okHttpClient(){
        return new OkHttpClient.Builder()
                .connectTimeout(connectTimeout,TimeUnit.SECONDS)
                .readTimeout(readTimeout, TimeUnit.SECONDS)
                .writeTimeout(writeTimeout,TimeUnit.SECONDS)
                .connectionPool(new ConnectionPool(maxIdleConnections,keepAliveDuration,TimeUnit.SECONDS))
                // 设置代理
//                .proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 8888)))
                // 拦截器
                .addInterceptor(new OkHttpLoggerInterceptor())
                .build();
    }
}

编写okHttp的拦截器OkHttpLoggerInterceptor,此时需要日志打印的也可以在拦截器中编写请求的url,请求报文以及返回报文日志

import com.csrcb.constants.MyConstant;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import okio.Buffer;
import okio.BufferedSource;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
import org.springframework.http.HttpStatus;

import java.io.IOException;
import java.util.UUID;

/**
 * @Classname OkHttpLoggerInterceptor
 * @Description okhttp请求的拦截器,将traceId及parentSpanId塞入
 * @Date 2021/8/18 17:24
 * @Created by gangye
 */
@Slf4j
public class OkHttpLoggerInterceptor implements Interceptor {

    @Override
    public Response intercept(Chain chain) throws IOException {
        //目前默认使用的是post请求,且格式是utf8,使用的是application/json
        Request request = chain.request();
        //copy all headers to newheaders
        Headers.Builder headers = request.headers().newBuilder();
        //add traceid | spanid | parentspanid to headers
        if (StringUtils.isNotBlank(MDC.get(MyConstant.TRACE_ID_MDC_FIELD))){
            headers.add(MyConstant.TRACE_ID_HTTP_FIELD, MDC.get(MyConstant.TRACE_ID_MDC_FIELD));//设置X-B3-TraceId
        }
        if (StringUtils.isNotBlank(MyConstant.SPAN_ID_MDC_FIELD)){
            headers.add(MyConstant.PARENT_SPAN_ID_HTTP_FIELD,MDC.get(MyConstant.SPAN_ID_MDC_FIELD));
        }
        String spanIdNew = UUID.randomUUID().toString().replace("-","").substring(0,16);
        headers.add(MyConstant.SPAN_ID_HTTP_FIELD, spanIdNew);//设置X-B3-SpanId供外部使用
        //rebuild a new request
        request = request.newBuilder().headers(headers.build()).build();

        Buffer buffer = new Buffer();
        request.body().writeTo(buffer);
        String requestBody = buffer.readUtf8();
        String requestUrl = request.url().toString();
        String[] url = requestUrl.split("/");
        log.info("[Request Addr]: " + request.url());
        log.info("[Service Name]: " + url[url.length - 1]+ "; [Request Body]: " + requestBody);
        Response response = chain.proceed(request);
        BufferedSource source = response.body().source();
        source.request(Long.MAX_VALUE);
        buffer = source.buffer();
        String responseBody = buffer.readUtf8();
        log.info("[Response Status Code]: " + response.code() + "; [Resonse Status Text]: " + HttpStatus.valueOf(response.code()).name());
        log.info("[Service Name]: " + url[url.length - 1]+ "; [Response Body]: " + responseBody);
        return response.newBuilder().body(ResponseBody.create(response.body().contentType(), responseBody)).build();
    }
}

至于如使用原生的RestTemplate去请求,RestTemplate也可以定义自己的拦截器

代码如下供参考,不过个人任然推荐在RestTemplat中使用okHttp

import com.csrcb.constants.MyConstant;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;

import java.io.IOException;
import java.util.UUID;

/**
 * @Classname RestTemplateInterator
 * @Description restTemplate请求的拦截器,将traceId及parentSpanId塞入
 * @Date 2021/8/19 9:56
 * @Created by gangye
 */
public class RestTemplateInterator implements ClientHttpRequestInterceptor {
    @Override
    public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes, ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
        String traceId = MDC.get(MyConstant.TRACE_ID_HTTP_FIELD);
        if (StringUtils.isNotBlank(traceId)) {
            httpRequest.getHeaders().add(MyConstant.TRACE_ID_HTTP_FIELD, traceId);
        }
        String spanIdNew = UUID.randomUUID().toString().replace("-","").substring(0,16);
        httpRequest.getHeaders().add(MyConstant.SPAN_ID_HTTP_FIELD, spanIdNew);//设置X-B3-SpanId供外部使用
        if (StringUtils.isNotBlank(MDC.get(MyConstant.SPAN_ID_MDC_FIELD))){
            httpRequest.getHeaders().add(MyConstant.PARENT_SPAN_ID_HTTP_FIELD,MDC.get(MyConstant.SPAN_ID_MDC_FIELD));
        }
        return clientHttpRequestExecution.execute(httpRequest, bytes);
    }
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/12285.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!