解决 Spring Data Elasticsearch log 打印日志问题

4,584 阅读1分钟

Spring boot 版本

我用的 Spring boot 版本是 2.4.x,其实 2.2.x 以上都可以使用这种方法

第一种解决方案

application.properties 配置文件加上 :

logging.level.org.springframework.data.elasticsearch.client.WIRE : trace

但必须要自定义初始化 RestHighLevelClient bean,如下:

    @Bean(destroyMethod = "close")
    public RestHighLevelClient restClient() {

        ClientConfiguration clientConfiguration = ClientConfiguration.builder()
                .connectedTo(esHost +":" + esPort).usingSsl()
                .build();

        RestHighLevelClient client = RestClients.create(clientConfiguration).rest();
        return client;
    }

原理是方法 RestClients.create 做了http拦截,如果用默认的 RestHighLevelClient 将没有这个功能,所以会不起效果。

参考:stackoverflow 高赞回答

第二种解决方案

上述方案虽然可以解决问题,但是不能使用 ElasticsearchRestClientProperties 作为配置文件,由于不想再多弄一份elasticsearch配置,可以根据第一种方案的实现原理,自定义实现 RestClientBuilderCustomizer 来扩展 RestHighLevelClient 特性,实现类如下:

package com.example.config;

import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpResponse;
import org.apache.http.HttpResponseInterceptor;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.apache.http.protocol.HttpContext;
import org.elasticsearch.client.RestClientBuilder;
import org.springframework.boot.autoconfigure.elasticsearch.RestClientBuilderCustomizer;
import org.springframework.data.elasticsearch.client.ClientLogger;
import org.springframework.data.elasticsearch.client.RestClients;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

import java.io.ByteArrayOutputStream;
import java.io.IOException;

/**
 *  自定义一些特性
 *
 * @see org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientAutoConfiguration
 */
@Component
public class CustomRestClientBuilderCustomizer implements RestClientBuilderCustomizer {

    /**
     * Name of whose value can be used to correlate log messages for this request.
     */
    private static final String LOG_ID_ATTRIBUTE = RestClients.class.getName() + ".LOG_ID";


    @Override
    public void customize(RestClientBuilder builder) {

    }

    @Override
    public void customize(HttpAsyncClientBuilder builder) {
        if (ClientLogger.isEnabled()) {
            HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();

            builder.addInterceptorLast((HttpRequestInterceptor) interceptor);
            builder.addInterceptorLast((HttpResponseInterceptor) interceptor);
        }
    }

    /**
     * 由于自动装配 RestHighLevelClient 没有使用 spring data elasticsearch 相关的类,导致打印日志不能兼容
     *
     * @see RestClients.HttpLoggingInterceptor
     */
    private static class HttpLoggingInterceptor implements HttpResponseInterceptor, HttpRequestInterceptor {

        @Override
        public void process(HttpRequest request, HttpContext context) throws IOException {

            String logId = (String) context.getAttribute(LOG_ID_ATTRIBUTE);

            if (logId == null) {
                logId = ClientLogger.newLogId();
                context.setAttribute(LOG_ID_ATTRIBUTE, logId);
            }

            if (request instanceof HttpEntityEnclosingRequest && ((HttpEntityEnclosingRequest) request).getEntity() != null) {

                HttpEntityEnclosingRequest entityRequest = (HttpEntityEnclosingRequest) request;
                HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity();
                ByteArrayOutputStream buffer = new ByteArrayOutputStream();
                entity.writeTo(buffer);

                if (!entity.isRepeatable()) {
                    entityRequest.setEntity(new ByteArrayEntity(buffer.toByteArray()));
                }

                ClientLogger.logRequest(logId, request.getRequestLine().getMethod(), request.getRequestLine().getUri(), "",
                        buffer::toString);
            } else {
                ClientLogger.logRequest(logId, request.getRequestLine().getMethod(), request.getRequestLine().getUri(), "");
            }
        }

        @Override
        public void process(HttpResponse response, HttpContext context) {
            String logId = (String) context.getAttribute(LOG_ID_ATTRIBUTE);
            ClientLogger.logRawResponse(logId, HttpStatus.resolve(response.getStatusLine().getStatusCode()));
        }
    }

}

这样使用 HttpLoggingInterceptor 对 HttpAsyncClient 做拦截,打印日志。