初始化项目
This commit is contained in:
31
xtools-app-sys/xtools-app-sys-log-bus-elasticsearch/pom.xml
Normal file
31
xtools-app-sys/xtools-app-sys-log-bus-elasticsearch/pom.xml
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.xujun</groupId>
|
||||
<artifactId>xtools-app-sys</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
<artifactId>xtools-app-sys-log-bus-elasticsearch</artifactId>
|
||||
|
||||
<!-- 依赖 -->
|
||||
<dependencies>
|
||||
<!-- 项目模块 begin -->
|
||||
<dependency>
|
||||
<groupId>org.xujun</groupId>
|
||||
<artifactId>xtools-app-sys-biz</artifactId>
|
||||
</dependency>
|
||||
<!-- 项目模块 end -->
|
||||
|
||||
<!-- SpringBoot begin -->
|
||||
<!-- SpringBoot-elasticsearch -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
|
||||
</dependency>
|
||||
<!-- SpringBoot end -->
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,44 @@
|
||||
package xtools.app.sys.log.bus.elasticsearch.config;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
|
||||
import xtools.app.sys.log.bus.elasticsearch.service.impl.EsSysLogServiceImpl;
|
||||
import xtools.app.sys.service.SysLogService;
|
||||
|
||||
/**
|
||||
* <p>Title : SysLogElasticsearchConfig</p>
|
||||
* <p>Description : SysLogElasticsearchConfig</p>
|
||||
* <p>DevelopTools : Idea_x64_v2026.1</p>
|
||||
* <p>DevelopSystem : macOS Sequoia 15.7.5</p>
|
||||
* <p>Company : org.xujun</p>
|
||||
*
|
||||
* @author : XuJun
|
||||
* @version : 1.0.0
|
||||
* @date : 2026/2/14 10:29
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@EnableElasticsearchRepositories(basePackages = "xtools.app.sys.log.bus.elasticsearch.service.base")
|
||||
public class SysLogElasticsearchConfig {
|
||||
|
||||
/**
|
||||
* 日志类型elasticsearch
|
||||
*/
|
||||
private static final String TYPE_ES = "elasticsearch";
|
||||
|
||||
/**
|
||||
* 获取ES日志服务
|
||||
*
|
||||
* @return 日志服务
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "sys.log", name = "type", havingValue = TYPE_ES)
|
||||
public SysLogService getEsSysLogService() {
|
||||
log.info("系统日志存储类型:{}", TYPE_ES);
|
||||
return new EsSysLogServiceImpl();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package xtools.app.sys.log.bus.elasticsearch.convert;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import xtools.app.sys.log.bus.elasticsearch.dto.EsSysLog;
|
||||
import xtools.app.sys.model.entity.SysLog;
|
||||
|
||||
/**
|
||||
* <p>Title : SysLogElasticsearchConvert</p>
|
||||
* <p>Description : SysLogElasticsearchConvert</p>
|
||||
* <p>Company : org.xujun</p>
|
||||
*
|
||||
* @author : xujun
|
||||
* @version : 1.0.0
|
||||
* @date : 2026-02-07 20:09:03
|
||||
*/
|
||||
@Mapper(componentModel = "spring")
|
||||
public interface SysLogElasticsearchConvert {
|
||||
|
||||
/**
|
||||
* 实体转ES
|
||||
*
|
||||
* @param data 实体
|
||||
* @return ES
|
||||
*/
|
||||
EsSysLog entityToEs(SysLog data);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package xtools.app.sys.log.bus.elasticsearch.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.elasticsearch.annotations.Document;
|
||||
import org.springframework.data.elasticsearch.annotations.Field;
|
||||
import org.springframework.data.elasticsearch.annotations.FieldType;
|
||||
|
||||
/**
|
||||
* <p>Title : EsSysLog</p>
|
||||
* <p>Description : EsSysLog</p>
|
||||
* <p>DevelopTools : Idea_x64_v2026.1</p>
|
||||
* <p>DevelopSystem : macOS Sequoia 15.7.5</p>
|
||||
* <p>Company : org.xujun</p>
|
||||
*
|
||||
* @author : XuJun
|
||||
* @version : 1.0.0
|
||||
* @date : 2026/2/13 19:34
|
||||
*/
|
||||
@Data
|
||||
@Document(indexName = EsSysLog.INDEX_NAME)
|
||||
public class EsSysLog {
|
||||
|
||||
/**
|
||||
* ES索引名称
|
||||
**/
|
||||
public static final String INDEX_NAME = "log-sys_v16";
|
||||
|
||||
/**
|
||||
* 主键 ID
|
||||
*/
|
||||
@Id
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 日志ID
|
||||
*/
|
||||
@Field(type = FieldType.Text)
|
||||
private String logId;
|
||||
|
||||
/**
|
||||
* 日志追踪ID
|
||||
*/
|
||||
@Field(type = FieldType.Text)
|
||||
private String traceId;
|
||||
|
||||
/**
|
||||
* 日志父ID
|
||||
*/
|
||||
@Field(type = FieldType.Text)
|
||||
private String parentId;
|
||||
|
||||
/**
|
||||
* 日志线程类型
|
||||
*/
|
||||
@Field(type = FieldType.Text)
|
||||
private String threadType;
|
||||
|
||||
/**
|
||||
* 日志时间
|
||||
*/
|
||||
@Field(type = FieldType.Long)
|
||||
private Long logTime;
|
||||
|
||||
/**
|
||||
* 日志获取索引
|
||||
*/
|
||||
@Field(type = FieldType.Integer)
|
||||
private Integer logIndex;
|
||||
|
||||
/**
|
||||
* 服务名称
|
||||
*/
|
||||
@Field(type = FieldType.Text)
|
||||
private String appName;
|
||||
|
||||
/**
|
||||
* 本机IP
|
||||
*/
|
||||
@Field(type = FieldType.Text)
|
||||
private String localIp;
|
||||
|
||||
/**
|
||||
* 运行端口
|
||||
*/
|
||||
@Field(type = FieldType.Text)
|
||||
private String port;
|
||||
|
||||
/**
|
||||
* 日志标题
|
||||
*/
|
||||
@Field(type = FieldType.Text)
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 日志级别
|
||||
*/
|
||||
@Field(type = FieldType.Integer)
|
||||
private Integer logLevel;
|
||||
|
||||
/**
|
||||
* 日志类型
|
||||
*/
|
||||
@Field(type = FieldType.Text)
|
||||
private String logType;
|
||||
|
||||
/**
|
||||
* 请求IP
|
||||
*/
|
||||
@Field(type = FieldType.Text)
|
||||
private String ip;
|
||||
|
||||
/**
|
||||
* 请求URI
|
||||
*/
|
||||
@Field(type = FieldType.Text)
|
||||
private String uri;
|
||||
|
||||
/**
|
||||
* 日志内容
|
||||
*/
|
||||
@Field(type = FieldType.Text)
|
||||
private String logBody;
|
||||
|
||||
/**
|
||||
* 堆栈信息
|
||||
*/
|
||||
@Field(type = FieldType.Text)
|
||||
private String stackTrace;
|
||||
|
||||
/**
|
||||
* 日志错误信息
|
||||
*/
|
||||
@Field(type = FieldType.Text)
|
||||
private String logError;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
@Field(type = FieldType.Text)
|
||||
private String memo;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package xtools.app.sys.log.bus.elasticsearch.service.base;
|
||||
|
||||
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
|
||||
import org.springframework.stereotype.Component;
|
||||
import xtools.app.sys.log.bus.elasticsearch.dto.EsSysLog;
|
||||
|
||||
/**
|
||||
* <p>Title : EsSysLogRepository</p>
|
||||
* <p>Description : EsSysLogRepository</p>
|
||||
* <p>DevelopTools : Idea_x64_v2026.1</p>
|
||||
* <p>DevelopSystem : macOS Sequoia 15.7.5</p>
|
||||
* <p>Company : org.xujun</p>
|
||||
*
|
||||
* @author : XuJun
|
||||
* @version : 1.0.0
|
||||
* @date : 2026/2/13 19:42
|
||||
*/
|
||||
@Component
|
||||
public interface EsSysLogRepository extends ElasticsearchRepository<EsSysLog, String> {
|
||||
}
|
||||
@@ -0,0 +1,307 @@
|
||||
package xtools.app.sys.log.bus.elasticsearch.service.impl;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import jakarta.annotation.Resource;
|
||||
import xtools.app.sys.log.bus.elasticsearch.convert.SysLogElasticsearchConvert;
|
||||
import xtools.app.sys.log.bus.elasticsearch.dto.EsSysLog;
|
||||
import xtools.app.sys.log.bus.elasticsearch.service.base.EsSysLogRepository;
|
||||
import xtools.app.sys.model.dto.req.SysLogPageReq;
|
||||
import xtools.app.sys.model.dto.resp.SysLogResp;
|
||||
import xtools.app.sys.model.entity.SysLog;
|
||||
import xtools.app.sys.service.SysLogService;
|
||||
import xtools.base.config.BaseParams;
|
||||
import xtools.boot.api.exection.BizError;
|
||||
import xtools.boot.api.model.dto.Result;
|
||||
import xtools.boot.api.model.dto.page.PageReq;
|
||||
import xtools.boot.api.model.dto.page.PageResp;
|
||||
import xtools.boot.elasticsearch.utils.EsQueryUtils;
|
||||
import xtools.boot.elasticsearch.utils.EsUtils;
|
||||
import xtools.boot.log.LogBus;
|
||||
import xtools.boot.log.enums.LogBusBaseType;
|
||||
import xtools.core.CollectionUtils;
|
||||
import xtools.core.enums.LogLevel;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* <p>Title : EsSysLogServiceImpl</p>
|
||||
* <p>Description : EsSysLogServiceImpl</p>
|
||||
* <p>DevelopTools : Idea_x64_v2026.1</p>
|
||||
* <p>DevelopSystem : macOS Sequoia 15.7.5</p>
|
||||
* <p>Company : org.xujun</p>
|
||||
*
|
||||
* @author : XuJun
|
||||
* @version : 1.0.0
|
||||
* @date : 2026/2/13 19:43
|
||||
*/
|
||||
public class EsSysLogServiceImpl implements SysLogService, BaseParams {
|
||||
|
||||
@Resource
|
||||
private EsSysLogRepository esSysLogRepository;
|
||||
|
||||
@Resource
|
||||
private SysLogElasticsearchConvert sysLogElasticsearchConvert;
|
||||
|
||||
/**
|
||||
* 分页查询
|
||||
*
|
||||
* @param pageReq 分页请求
|
||||
* @return 分页结果
|
||||
*/
|
||||
@Override
|
||||
public Result<PageResp<SysLogResp>> page(PageReq<SysLogPageReq> pageReq) {
|
||||
// 查询条件
|
||||
JSONObject req = getPageReq(pageReq.getCurrentPage(), pageReq.getPageSize(), pageReq.getQuery());
|
||||
// 结果
|
||||
JSONObject resp = null;
|
||||
try {
|
||||
resp = EsUtils.exec(getDbUri() + "_search", req);
|
||||
} catch (Exception e) {
|
||||
LogBus.init(LogLevel.ERROR, LogBusBaseType.ELASTICSEARCH).data(req).title("系统日志ES查询失败").error(e).save();
|
||||
}
|
||||
return Result.ok(getPageResult(pageReq.getCurrentPage(), pageReq.getPageSize(), resp));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取分页结果
|
||||
*
|
||||
* @param currentPage 当前页
|
||||
* @param pageSize 页大小
|
||||
* @param resp 查询结果
|
||||
* @return 分页结果
|
||||
*/
|
||||
private PageResp<SysLogResp> getPageResult(Integer currentPage, Integer pageSize, JSONObject resp) {
|
||||
PageResp<SysLogResp> pageResp = new PageResp<>();
|
||||
pageResp.setCurrentPage(currentPage);
|
||||
pageResp.setPageSize(pageSize);
|
||||
pageResp.setTotal(0L);
|
||||
if (CollectionUtils.isEmpty(resp)) {
|
||||
return pageResp;
|
||||
}
|
||||
JSONObject hits = resp.getJSONObject("hits");
|
||||
if (CollectionUtils.isEmpty(hits)) {
|
||||
return pageResp;
|
||||
}
|
||||
Long total = hits.getJSONObject("total").getLong("value");
|
||||
JSONArray resultList = hits.getJSONArray("hits");
|
||||
if (CollectionUtils.isEmpty(resultList)) {
|
||||
return pageResp;
|
||||
}
|
||||
List<SysLogResp> dataList = new ArrayList<>();
|
||||
for (Object obj : resultList) {
|
||||
if (obj instanceof JSONObject item) {
|
||||
JSONObject source = item.getJSONObject("_source");
|
||||
source.remove("logBody");
|
||||
source.remove("stackTrace");
|
||||
source.remove("logError");
|
||||
dataList.add(JSON.to(SysLogResp.class, source));
|
||||
}
|
||||
}
|
||||
pageResp.setData(dataList);
|
||||
pageResp.setTotal(total);
|
||||
return pageResp;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取分页查询条件
|
||||
*
|
||||
* @param currentPage 当前页
|
||||
* @param pageSize 页大小
|
||||
* @param req 请求参数
|
||||
* @return 查询条件
|
||||
*/
|
||||
private JSONObject getPageReq(Integer currentPage, Integer pageSize, SysLogPageReq req) {
|
||||
// 查询条件处理
|
||||
JSONArray must = new JSONArray();
|
||||
EsQueryUtils.setTimeRange(must, "logTime", req.getLogTimeRange());
|
||||
|
||||
EsQueryUtils.setMatchPhrase(must, "traceId", req.getTraceId());
|
||||
EsQueryUtils.setMatchPhrase(must, "logType", req.getLogType());
|
||||
EsQueryUtils.setMatchPhrase(must, "logLevel", req.getLogLevel());
|
||||
|
||||
EsQueryUtils.setWildcard(must, "title", req.getTitle());
|
||||
EsQueryUtils.setWildcard(must, "ip", req.getIp());
|
||||
EsQueryUtils.setWildcard(must, "uri", req.getUri());
|
||||
|
||||
EsQueryUtils.setMatchPhrase(must, "threadType", req.getThreadType());
|
||||
|
||||
EsQueryUtils.setWildcard(must, "appName", req.getAppName());
|
||||
EsQueryUtils.setWildcard(must, "localIp", req.getLocalIp());
|
||||
EsQueryUtils.setWildcard(must, "port", req.getPort());
|
||||
|
||||
JSONObject query = JSONObject.of("bool", JSONObject.of("must", must));
|
||||
|
||||
// 排序条件
|
||||
JSONArray sort = new JSONArray();
|
||||
sort.add(JSONObject.of("logTime", JSONObject.of("order", "desc")));
|
||||
sort.add(JSONObject.of("logIndex", JSONObject.of("order", "desc")));
|
||||
|
||||
// 完整查询器
|
||||
return JSONObject.of("from", ((currentPage - 1) * pageSize), "size", pageSize, "query", query, "sort", sort);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据日志 ID 查询
|
||||
*
|
||||
* @param logId 日志 ID
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public Result<SysLogResp> getByLogId(String logId) {
|
||||
// 查询条件
|
||||
JSONArray fields = new JSONArray();
|
||||
fields.add("logId");
|
||||
JSONObject query = JSONObject.of("multi_match", JSONObject.of("query", logId, "fields", fields));
|
||||
// 完整查询器
|
||||
JSONObject req = JSONObject.of("from", CP_NUM0, "size", CP_NUM1, "query", query);
|
||||
// 结果
|
||||
JSONObject resp = null;
|
||||
try {
|
||||
resp = EsUtils.exec(getDbUri() + "_search", req);
|
||||
} catch (Exception e) {
|
||||
LogBus.init(LogLevel.ERROR, LogBusBaseType.ELASTICSEARCH).data(req).title("系统日志ES查询失败").error(e).save();
|
||||
}
|
||||
List<SysLogResp> result = getQueryByTraceIdResult(resp);
|
||||
if (CollectionUtils.isEmpty(result)) {
|
||||
throw new BizError("日志ID不存在");
|
||||
}
|
||||
SysLogResp data = new SysLogResp();
|
||||
data.setLogBody(result.getFirst().getLogBody());
|
||||
return Result.ok(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据traceId查询
|
||||
*
|
||||
* @param traceId traceId
|
||||
* @return 日志列表
|
||||
*/
|
||||
@Override
|
||||
public Result<List<SysLogResp>> getByTraceId(String traceId) {
|
||||
// 获取查询器
|
||||
JSONObject req = getQueryByTraceIdReq(traceId);
|
||||
// 结果
|
||||
JSONObject resp = null;
|
||||
try {
|
||||
resp = EsUtils.exec(getDbUri() + "_search", req);
|
||||
} catch (Exception e) {
|
||||
LogBus.init(LogLevel.ERROR, LogBusBaseType.ELASTICSEARCH).data(req).title("系统日志ES查询失败").error(e).save();
|
||||
}
|
||||
return Result.ok(getQueryByTraceIdResult(resp));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取查询请求参数
|
||||
*
|
||||
* @param traceId 日志追踪ID
|
||||
* @return 查询请求参数
|
||||
*/
|
||||
private JSONObject getQueryByTraceIdReq(String traceId) {
|
||||
// 查询条件
|
||||
JSONArray fields = new JSONArray();
|
||||
fields.add("traceId");
|
||||
JSONObject query = JSONObject.of("multi_match", JSONObject.of("query", traceId, "fields", fields));
|
||||
// 排序条件
|
||||
JSONArray sort = new JSONArray();
|
||||
sort.add(JSONObject.of("logIndex", JSONObject.of("order", "asc")));
|
||||
sort.add(JSONObject.of("logTime", JSONObject.of("order", "asc")));
|
||||
// 完整查询器
|
||||
return JSONObject.of("from", CP_NUM0, "size", CP_NUM10 * CP_NUM1000, "query", query, "sort", sort);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取查询结果
|
||||
*
|
||||
* @param resp 结果集
|
||||
* @return 查询结果
|
||||
*/
|
||||
private List<SysLogResp> getQueryByTraceIdResult(JSONObject resp) {
|
||||
if (CollectionUtils.isEmpty(resp)) {
|
||||
return null;
|
||||
}
|
||||
JSONArray resultList = resp.getJSONObject("hits").getJSONArray("hits");
|
||||
if (CollectionUtils.isEmpty(resultList)) {
|
||||
return null;
|
||||
}
|
||||
List<SysLogResp> dataList = new ArrayList<>();
|
||||
for (Object obj : resultList) {
|
||||
if (obj instanceof JSONObject item) {
|
||||
JSONObject source = item.getJSONObject("_source");
|
||||
dataList.add(JSON.to(SysLogResp.class, source));
|
||||
}
|
||||
}
|
||||
return dataList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存
|
||||
*
|
||||
* @param sysLog 日志
|
||||
* @return 是否保存成功
|
||||
*/
|
||||
@Override
|
||||
public boolean save(SysLog sysLog) {
|
||||
EsSysLog esSysLog = sysLogElasticsearchConvert.entityToEs(sysLog);
|
||||
esSysLogRepository.save(esSysLog);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
*
|
||||
* @param startTime 开始时间
|
||||
* @param endTime 结束时间
|
||||
* @return 删除数量
|
||||
*/
|
||||
@Override
|
||||
public long delete(Instant startTime, Instant endTime) {
|
||||
if (Objects.isNull(endTime)) {
|
||||
return CP_NUM0;
|
||||
}
|
||||
long end = endTime.toEpochMilli();
|
||||
long start = CP_NUM0;
|
||||
if (Objects.nonNull(startTime)) {
|
||||
start = startTime.toEpochMilli();
|
||||
}
|
||||
JSONObject req = JSONObject.of(
|
||||
"query",
|
||||
JSONObject.of(
|
||||
"range",
|
||||
JSONObject.of(
|
||||
"logTime",
|
||||
JSONObject.of(
|
||||
"gte", start,
|
||||
"lte", end
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
// 结果
|
||||
JSONObject resp = null;
|
||||
try {
|
||||
resp = EsUtils.exec(getDbUri() + "_delete_by_query", req);
|
||||
} catch (Exception e) {
|
||||
LogBus.init(LogLevel.ERROR, LogBusBaseType.ELASTICSEARCH).data(req).title("系统日志ES删除失败").error(e).save();
|
||||
}
|
||||
if (CollectionUtils.isEmpty(resp)) {
|
||||
return CP_NUM0;
|
||||
}
|
||||
Long deleted = resp.getLong("deleted");
|
||||
return Objects.nonNull(deleted) ? deleted : CP_NUM0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取DB的请求路径
|
||||
*
|
||||
* @return DB的请求路径
|
||||
*/
|
||||
private String getDbUri() {
|
||||
return CP_SLASH + EsSysLog.INDEX_NAME + CP_SLASH;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user