springboot+ElasticsearchRestTemplate 实现DSL日志打印

本文介绍了如何在SpringDataElasticsearch的ElasticsearchRestTemplate中,通过Java反射机制获取并打印ES查询的DSL语句,以便于开发和问题追踪。作者展示了如何扩展CustomTemplate类来实现实时日志功能。

需求说明

近期项目碰到一个问题,需要使用ES中的java api中的ElasticsearchRestTemplate,调用
org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate#search方法,需要实现DSL语句日志打印;

调研阶段

其中跟踪源码发现

@Override
	public <T> SearchHits<T> search(Query query, Class<T> clazz, IndexCoordinates index) {
		SearchRequest searchRequest = requestFactory.searchRequest(query, clazz, index);
		SearchResponse response = execute(client -> client.search(searchRequest, RequestOptions.DEFAULT));

		ReadDocumentCallback<T> documentCallback = new ReadDocumentCallback<>(elasticsearchConverter, clazz, index);
		SearchDocumentResponseCallback<SearchHits<T>> callback = new ReadSearchDocumentResponseCallback<>(clazz, index);

		return callback.doWith(SearchDocumentResponseBuilder.from(response, getEntityCreator(documentCallback)));
	}

在SearchRequest 这个对象中,有一个source的属性,包含了去es请求的完整dsl语句;
为了获取他,接着发现一个问题:

protected RequestFactory requestFactory;

RequestFactory为私有类,无法直接调用,虽然ElasticsearchRestTemplate提供了getRequestFactory()这个方法,但依然无法编译运行

public RequestFactory getRequestFactory() {
		return requestFactory;
	}

解决方案

去拿到这个requestFactory,目前有且仅有的方法就是通过Java反射的方法,然后实现了拿到
SearchRequest 这个对象;

 Method searchRequestMethod = ReflectionUtils.findMethod(Class.forName("org.springframework.data.elasticsearch.core.RequestFactory"), "searchRequest", Query.class, Class.class, IndexCoordinates.class);
                searchRequestMethod.setAccessible(true);
                SearchRequest searchRequest = (SearchRequest) ReflectionUtils.invokeMethod(searchRequestMethod, getRequestFactory(), query, clazz, index);

接着通过代码提示发现没有source的get关键字的方法,但实际上还是有提供同类的方法,只不过没有get关键字而已

 public SearchSourceBuilder source() {
        return source;
    }

好了,接下来该有的准备都有了,只需要对ElasticsearchRestTemplate进行扩展即可,以下是实现代码:

package com.example.methodtest;

import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.document.SearchDocumentResponseBuilder;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Method;

/**
 * 实现dsl日志打印
 *
 * @author AaronCgt
 * @since 2024/3/12/012
 */
@Component
@Slf4j
public class CustomTemplate extends ElasticsearchRestTemplate {


    public CustomTemplate(RestHighLevelClient client, ElasticsearchConverter elasticsearchConverter) {
        super(client, elasticsearchConverter);
    }

    public <T> SearchHits<T> search(Query query, Class<T> clazz, boolean isDebug)  {
        IndexCoordinates index=getIndexCoordinatesFor(clazz);
        try {
            if (!isDebug){
                return search(query,clazz,index);
            }else{
                Method searchRequestMethod = ReflectionUtils.findMethod(Class.forName("org.springframework.data.elasticsearch.core.RequestFactory"), "searchRequest", Query.class, Class.class, IndexCoordinates.class);
                searchRequestMethod.setAccessible(true);
                SearchRequest searchRequest = (SearchRequest) ReflectionUtils.invokeMethod(searchRequestMethod, getRequestFactory(), query, clazz, index);
                log.info("DSL:{}", searchRequest.source().toString());

                SearchResponse response = execute(client -> client.search(searchRequest, RequestOptions.DEFAULT));

                ReadDocumentCallback<T> documentCallback = new ReadDocumentCallback<>(elasticsearchConverter, clazz, index);
                SearchDocumentResponseCallback<SearchHits<T>> callback = new ReadSearchDocumentResponseCallback<>(clazz, index);

                return callback.doWith(SearchDocumentResponseBuilder.from(response, getEntityCreator(documentCallback)));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    
}

至此,需求实现。可以根据实际需求,调用这个类,进行日志打印,例如平时开发接口,预留打印日志参数,需要时传入,实现接口级别DSL语句打印,也可以用于生产坏境出现问题时候对DSL的复现和跟踪!

jar包版本说明

为了准确性,以下提供jar包版本仅供各位参考:
在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值