写点什么

elk 客户端与 springboot 整合

作者:小黄鸡1992
  • 2021 年 12 月 03 日
  • 本文字数:5728 字

    阅读完需:约 19 分钟

elk客户端与springboot整合

通过上文的学习,相信大家已经将 springboot 打印的日志同步到了 elasticsearch​,接下来需要在系统模块中按条件查询出相应的数据。由于博主这里的需求比较简单,且使用场景单一,所以使用了 spring 提供的 start 作为客户端。下文将介绍如何整合。

一.ElasticsearchTemplate 查询语法

在使用 ElasticsearchTemplate 时只需要掌握提供的 API 即可,下文将常用的查询 API 总结出来,供小伙伴们使用。

        // 返回对象        Result<List<ActionLogVO>> result = new Result();        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();        BoolQueryBuilder bool = QueryBuilders.boolQuery();          //must为and的语法        //matchPhraseQuery为将type分词 然后将第二个参数匹配 可以模糊查询使用        bool.must(QueryBuilders.matchPhraseQuery("Type", "日志"));         //wildcardQuery模糊查询使用  但是字段需要未英文         bool.must(QueryBuilders.wildcardQuery("userName", "*admin*"));                 //通过时间筛选        List<QueryBuilder> filters = bool.filter();                                   filters.add(QueryBuilders.rangeQuery("time").gte(sd.parse("2021-01-19 17:28:41"))        .lte(sd.parse("2021-01-19 17:28:43")));           //分页(第一个参数PageNum从第0页开始 第二个参数pageSize)        builder.withPageable(PageRequest.of(1, 10));         //排序        builder.withSort(SortBuilders.fieldSort("time").order(SortOrder.ASC));          // 构造查询条件        builder.withQuery(bool);        NativeSearchQuery query = builder.build();        Iterable<XXXVO> resultIter = XXXXService.search(query);
复制代码

二.条件查询

1.修改 pom 文件

        <!-- elasticsearch -->        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>            <version>2.3.7.RELEASE</version>        </dependency>
复制代码

2.修改 application.yml

spring:  elasticsearch:    rest:      uris: ip:9200      #上文安装时配置的账号密码      username: xxxx            password: xxxx  data:    elasticsearch:      repositories:        enabled: true      client:        reactive:#          username: xxx 这种写法是不对的#          password: xxx 这种写法是不对的#          endpoints: ip:9200 这种写法是不对的          use-ssl: false
复制代码

注意:

  1. 配置 es 地址要写在楼主这个位置,否则会默认为 localhost:9200,如果 es 安装在其他服务器将连接失败。

  2. 与旧版本的配置不同,开启的端口号为 9200 。

3.新增 config 文件

如果不加入以下文件会有 java.lang.IllegalStateException: availableProcessors is already set to [4], rejecting [4]异常。

@Configurationpublic class ElasticSearchConfig {    /**     * 防止netty的bug     * java.lang.IllegalStateException: availableProcessors is already set to [4], rejecting [4]     */    @PostConstruct    void init() {        System.setProperty("es.set.netty.runtime.available.processors", "false");    }}
复制代码

4.接口编写

主要调用 ElasticsearchRestTemplate 下的 API,详细解释见下文注释。

    @Autowired    ElasticsearchRestTemplate elasticsearchTemplate;
@Log(operationName = "日志-查询登录日志") @PostMapping("/selectLoginLog") public Result<List<LoginLogVO>> selectLoginLog(@RequestBody LoginLogInputVO loginLogInputVO) throws ParseException { // 日期格式化 SimpleDateFormat sd = new SimpleDateFormat(DateFormatEnum.YYYY_MM_DD_HH_MM_SS.getFormat()); // 返回对象 Result<List<LoginLogVO>> result = new Result(); NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder(); BoolQueryBuilder bool = QueryBuilders.boolQuery(); // 用户名不为空 if (!CommonUtil.isEmpty(loginLogInputVO.getUserName())) { bool.must(QueryBuilders.wildcardQuery("userName", "*" + loginLogInputVO.getUserName() + "*")); } // 时间不为空 if (!CommonUtil.isEmpty(loginLogInputVO.getBeginTime()) && !CommonUtil.isEmpty(loginLogInputVO.getEndTime())) { List<QueryBuilder> filters = bool.filter(); filters.add(QueryBuilders.rangeQuery("time") .gte(sd.parse(loginLogInputVO.getBeginTime() + DateFormatEnum.BEGIN_HH_MM_SS.getFormat())) .lte(sd.parse(loginLogInputVO.getEndTime() + DateFormatEnum.END_HH_MM_SS.getFormat()))); } // 分页查询 if (!CommonUtil.isEmpty(loginLogInputVO.getPageSize()) && !CommonUtil.isEmpty(loginLogInputVO.getPageNum())) { // 从第0页开始 builder.withPageable(PageRequest.of(loginLogInputVO.getPageNum() - 1, loginLogInputVO.getPageSize())); } builder.withSort(SortBuilders.fieldSort("time").order(SortOrder.ASC)); // 构造查询条件 builder.withQuery(bool); NativeSearchQuery query = builder.build(); //查询的索引 IndexCoordinates indexCoordinates = IndexCoordinates.of("loginlog-*"); //执行查询 SearchHits<LoginLogVO> resultIter = elasticsearchTemplate.search(query, LoginLogVO.class, indexCoordinates); // 格式化输出 List<LoginLogVO> resultList = new ArrayList<>(); List<SearchHit<LoginLogVO>> SearchHitList = resultIter.getSearchHits(); for (SearchHit<LoginLogVO> loginLogVOSearchHit : SearchHitList) { resultList.add(loginLogVOSearchHit.getContent()); } // 分页返回 if (!CommonUtil.isEmpty(loginLogInputVO.getPageSize()) && !CommonUtil.isEmpty(loginLogInputVO.getPageNum())) { result.setTotal(resultIter.getTotalHits()); result.setPageNum(loginLogInputVO.getPageNum()); result.setPageSize(loginLogInputVO.getPageSize()); } result.setData(resultList); return result; }
复制代码


这里的 loginlog-*索引指的是以 loginlog-开头的所有索引。

5.实体类

1.elasticsearch 客户端查询使用

在执行 elasticsearchTemplate.search 方法时,需要特殊的实体类。

@Document(indexName = "loginlog-*", shards = 1, replicas = 0)public class LoginLogVO {
@Id private String id;
/** * 信息 */ @Field(type = FieldType.Keyword, analyzer = "ik_max_word") private String message;
@Field(type = FieldType.Date, store = true, format = DateFormat.date_time) private Date time;}
复制代码

解释如下:

  • @Document:标示映射到 Elasticsearch 文档上的领域对象,其中 indexName 表示索引名称,shards 表示默认分片数,replicas 表示默认副本数量。

  • @Id:表示是文档的 id,文档可以认为是 mysql 中表行的概念

  • @Field:文档中字段的类型,其中 analyzer 表示分词器类型,format 表示格式化类型,类型种类包括 Text(会进行分词并建了索引的字符类型),Integer,Long,Date,Float,Double,Boolean 等。

2.入参 PO

为普通的实体类,传入查询参数。

public class LoginLogInputVO extends BaseEntity {
/** * 用户账号 */ private String userName;
/** * 开始时间 */ private String beginTime;
/** * 结束时间 */ private String endTime;}
复制代码


因为楼主前后端约定的时间类型为 string,所以需要转换类型然后传回。

6.注意

elasticsearchTemplate 每次只能查询一万条。所以不建议将所有页数都返回,建议使用如下分页方式。请参考百度。

三.导出时间断内的数据

1.接口编写

    @ApiOperation(value = "导出详细日志接口", notes = "导出详细日志接口")    @PostMapping("/exportDetailLog")    public void exportDetailLog(@ApiParam(name = "导出详细日志接口输入参数实体", value = "导出详细日志接口输入参数实体",        required = false) @RequestBody HandleDetailLogVO handleDetailLogVO) throws IOException, ParseException {        SimpleDateFormat sd = new SimpleDateFormat(DateFormatEnum.YYYY_MM_DD_HH_MM_SS.getFormat());        String beginTime = handleDetailLogVO.getBeginTime() + DateFormatEnum.BEGIN_HH_MM_SS.getFormat();        String endTime = handleDetailLogVO.getEndTime() + DateFormatEnum.END_HH_MM_SS.getFormat();        String path = handleDetailLogVO.getPath() + "/" + handleDetailLogVO.getBeginTime() + "-"            + handleDetailLogVO.getEndTime() + ".txt";        // 根据时间查询        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();        BoolQueryBuilder bool = QueryBuilders.boolQuery();        List<QueryBuilder> filters = bool.filter();        //构建查询条件        filters.add(QueryBuilders.rangeQuery("time").gte(sd.parse(beginTime)).lte(sd.parse(endTime)));        builder.withQuery(bool);        NativeSearchQuery query = builder.build();        //查询的索引        IndexCoordinates indexCoordinates = IndexCoordinates.of("loginlog-*");        // 查询前1w条        SearchScrollHits<DetailLogVO> scroll =            elasticsearchTemplate.searchScrollStart(3000, query, DetailLogVO.class, indexCoordinates);        List<SearchHit<DetailLogVO>> resultList = new ArrayList<>();        while (scroll.hasSearchHits()) {            List<SearchHit<DetailLogVO>> searchHitList = scroll.getSearchHits();            resultList.addAll(searchHitList);            scroll = elasticsearchTemplate.searchScrollContinue(scroll.getScrollId(), 3000, DetailLogVO.class,                indexCoordinates);        }        // 导出为text流        Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(path, true), "utf-8"));        for (int i = 0; i < resultList.size(); i++) {            out.write(resultList.get(i).getContent().getMessage());            out.write("\r\n");        }        out.flush();        out.close();        // 删除索引        elasticsearchTemplate.delete(query, DetailLogVO.class, indexCoordinates);    }
复制代码


注意:因为有 1w 条查询限制,所以循环查询。但是到处的日志不能超过 8g。

2.实体类

主要为入参携带查询时间的条件。

public class HandleDetailLogVO {     /**     * 结束时间     */    @ApiModelProperty(value = "结束时间", name = "结束时间")    private String endTime;     /**     * 开始时间     */    @ApiModelProperty(value = "开始时间", name = "开始时间")    private String beginTime;     /**     * 导出路径     */    @ApiModelProperty(value = "导出路径", name = "导出路径")    private String path;}
复制代码

四.删除时间段内的数据

1.接口编写

同样通过 API 删除时间段内的数据。详情见代码注释。

    @ApiOperation(value = "清除详细日志接口", notes = "清除详细日志接口")    @PostMapping("/clearDetailLog")    public void clearDetailLog(@ApiParam(name = "清除详细日志接口输入参数实体", value = "清除详细日志接口输入参数实体",        required = false) @RequestBody HandleDetailLogVO handleDetailLogVO) throws IOException, ParseException {        SimpleDateFormat sd = new SimpleDateFormat(DateFormatEnum.YYYY_MM_DD_HH_MM_SS.getFormat());        String beginTime = handleDetailLogVO.getBeginTime() + DateFormatEnum.BEGIN_HH_MM_SS.getFormat();        String endTime = handleDetailLogVO.getEndTime() + DateFormatEnum.END_HH_MM_SS.getFormat();        // 根据时间查询        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();        BoolQueryBuilder bool = QueryBuilders.boolQuery();        List<QueryBuilder> filters = bool.filter();        filters.add(QueryBuilders.rangeQuery("time").gte(sd.parse(beginTime)) .lte(sd.parse(endTime)));        builder.withQuery(bool);        //构建查询条件        NativeSearchQuery query = builder.build();        //指定删除索引的名称        IndexCoordinates indexCoordinates = IndexCoordinates.of("datalog-*");        // 调用API删除索引        elasticsearchTemplate.delete(query, DetailLogVO.class, indexCoordinates);    }
复制代码

2.实体类

携带查询条件的入参实体。

public class HandleDetailLogVO {         /**         * 结束时间         */        @ApiModelProperty(value = "结束时间", name = "结束时间")        private String endTime;         /**         * 开始时间         */        @ApiModelProperty(value = "开始时间", name = "开始时间")        private String beginTime;}
复制代码


发布于: 3 小时前阅读数: 5
用户头像

小黄鸡1992

关注

小黄鸡加油 2021.07.13 加入

一位技术落地与应用的博主,带你从入门,了解和使用各项顶流开源项目。

评论

发布
暂无评论
elk客户端与springboot整合