采购网站有哪些,手机制作最简单钓鱼网站,遵义公共资源交易中心官网,网页设计与制作怎么把图片加进去之前的项目中一直使用的是数据库表记录用户操作日志的#xff0c;但随着时间的推移#xff0c;数据库log单表是越来越大「不考虑删除」#xff0c;再加上近期项目中需要用到Elasticsearch#xff0c;所以干脆把这些用户日志迁移到ES上来了。 环境#xff1a;SpringBoot2.2…之前的项目中一直使用的是数据库表记录用户操作日志的但随着时间的推移数据库log单表是越来越大「不考虑删除」再加上近期项目中需要用到Elasticsearch所以干脆把这些用户日志迁移到ES上来了。 环境SpringBoot2.2.6 Elasticsearch6.8.8 如果你还不了解Elasticsearch的话可以参考之前的几篇文章
ES基本概念https://www.cnblogs.com/niceyoo/p/10864783.html重温ES基础https://www.cnblogs.com/niceyoo/p/11329426.htmlES-Windows集群搭建https://www.cnblogs.com/niceyoo/p/11343697.htmlES-Docker集群搭建https://www.cnblogs.com/niceyoo/p/11342903.htmlMacOS中ES搭建https://www.cnblogs.com/niceyoo/p/12936325.html
由于之前就是使用的AOP注解方式实现日志记录而本次依旧采用这种方式所以改动不大把保存至数据库换成ES就可以了开始吧。 文章最后我会提供源码的正文描述部分有省略~ 1、引入依赖文件
pom.xml文件中引入需要的es、aop所需的依赖
?xml version1.0 encodingUTF-8?
project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersionparentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion2.2.6.RELEASE/versionrelativePath/ !-- lookup parent from repository --/parentgroupIdcom.example/groupIdartifactIddemo/artifactIdversion0.0.1-SNAPSHOT/versionnamedemo/namedescriptionDemo project for Spring Boot/descriptionpropertiesjava.version1.8/java.version/propertiesdependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-devtools/artifactIdscoperuntime/scopeoptionaltrue/optional/dependencydependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdoptionaltrue/optional/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scopeexclusionsexclusiongroupIdorg.junit.vintage/groupIdartifactIdjunit-vintage-engine/artifactId/exclusion/exclusions/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-aop/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-elasticsearch/artifactId/dependency!-- Gson --dependencygroupIdcom.google.code.gson/groupIdartifactIdgson/artifactIdversion2.8.6/version/dependency!-- Hutool工具包 --dependencygroupIdcn.hutool/groupIdartifactIdhutool-all/artifactIdversion5.3.2/version/dependency/dependenciesbuildpluginsplugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactId/plugin/plugins/build
/project2、修改yml配置文件
加入elasticsearch的配置信息
server:port: 6666servlet:context-path: /tomcat:uri-encoding: UTF-8spring:# Elasticsearchdata:elasticsearch:client:reactive:# 要连接的ES客户端 多个逗号分隔endpoints: 127.0.0.1:9300# 暂未使用ES 关闭其持久化存储repositories:enabled: true3、Log实体
使用了lombok「 Data 注解」简化 set\getspring-data-elasticsearch提供了Document、Id、Field注解其中Document作用在实体类上指向文档地址Id、Field作用于成员变量上分别表示主键、字段。
Data
Document(indexName log, type log, shards 1, replicas 0, refreshInterval -1)
public class EsLog implements Serializable{private static final long serialVersionUID 1L;/*** 主键*/Idprivate String id SnowFlakeUtil.nextId().toString();/*** 创建者*/private String createBy;/*** 创建时间*/JsonFormat(timezone GMT8, pattern yyyy-MM-dd HH:mm:ss)Field(type FieldType.Date, index false, format DateFormat.custom, pattern yyyy-MM-dd HH:mm:ss)private Date createTime new Date();/*** 时间戳 查询时间范围时使用*/private Long timeMillis System.currentTimeMillis();/*** 方法操作名称*/private String name;/*** 日志类型*/private Integer logType;/*** 请求链接*/private String requestUrl;/*** 请求类型*/private String requestType;/*** 请求参数*/private String requestParam;/*** 请求用户*/private String username;/*** ip*/private String ip;/*** 花费时间*/private Integer costTime;/*** 转换请求参数为Json* param paramMap*/public void setMapToParams(MapString, String[] paramMap) {this.requestParam ObjectUtil.mapToString(paramMap);}
}4、Dao层
数据操作层有两种方式实现对Elasticsearch数据的修改一是使用ElasticsearchTemplate二是通过ElasticsearchRepository接口本文基于后者接口方式。
用过SpringDataJPA的小伙伴就不陌生了如下实现接口就跟JPA通过方法名称生成SQL一样简单。
/*** esc dao*/
public interface EsLogDao extends ElasticsearchRepositoryEsLog, String {/*** 通过类型获取* param type* return*/PageEsLog findByLogType(Integer type, Pageable pageable);
}默认情况下ElasticsearchRepository提供了findById()、findAll()、findAllById()、search()等方法供我们方便使用。
5、自定义注解
自定义 SystemLog 注解用于标记需要记录日志的方法。
Target({ElementType.PARAMETER, ElementType.METHOD})
Retention(RetentionPolicy.RUNTIME)
Documented
public interface SystemLog {/*** 日志名称* return*/String description() default ;/*** 日志类型* return*/LogType type() default LogType.OPERATION;
}6、编写切面、通知
步骤5中自定义了注解那么接下来就是定位注解以及对定位后的方法进行业务处理部分了而对我们来说就是把日志记录至Elasticsearch中。
/*** 日志管理*/
Aspect
Component
Slf4j
public class SystemLogAspect {private static final ThreadLocalDate beginTimeThreadLocal new NamedThreadLocalDate(ThreadLocal beginTime);Autowiredprivate EsLogService esLogService;Autowired(required false)private HttpServletRequest request;/*** Controller层切点,注解方式*/Pointcut(annotation(com.example.demo.annotation.SystemLog))public void controllerAspect() {}/*** 前置通知 (在方法执行之前返回)用于拦截Controller层记录用户的操作的开始时间* param joinPoint 切点* throws InterruptedException*/Before(controllerAspect())public void doBefore(JoinPoint joinPoint) throws InterruptedException{//线程绑定变量该数据只有当前请求的线程可见Date beginTime new Date();beginTimeThreadLocal.set(beginTime);}/*** 后置通知(在方法执行之后并返回数据) 用于拦截Controller层无异常的操作* param joinPoint 切点*/AfterReturning(controllerAspect())public void after(JoinPoint joinPoint){try {String username ;String description getControllerMethodInfo(joinPoint).get(description).toString();int type (int)getControllerMethodInfo(joinPoint).get(type);MapString, String[] logParams request.getParameterMap();EsLog esLog new EsLog();//请求用户esLog.setUsername(小伟);//日志标题esLog.setName(description);//日志类型esLog.setLogType(type);//日志请求urlesLog.setRequestUrl(request.getRequestURI());//请求方式esLog.setRequestType(request.getMethod());//请求参数esLog.setMapToParams(logParams);//请求开始时间long beginTime beginTimeThreadLocal.get().getTime();long endTime System.currentTimeMillis();//请求耗时Long logElapsedTime endTime - beginTime;esLog.setCostTime(logElapsedTime.intValue());//调用线程保存至ESThreadPoolUtil.getPool().execute(new SaveEsSystemLogThread(esLog, esLogService));} catch (Exception e) {log.error(AOP后置通知异常, e);}}/*** 保存日志至ES*/private static class SaveEsSystemLogThread implements Runnable {private EsLog esLog;private EsLogService esLogService;public SaveEsSystemLogThread(EsLog esLog, EsLogService esLogService) {this.esLog esLog;this.esLogService esLogService;}Overridepublic void run() {esLogService.saveLog(esLog);}}/*** 获取注解中对方法的描述信息 用于Controller层注解* param joinPoint 切点* return 方法描述* throws Exception*/public static MapString, Object getControllerMethodInfo(JoinPoint joinPoint) throws Exception{MapString, Object map new HashMapString, Object(16);//获取目标类名String targetName joinPoint.getTarget().getClass().getName();//获取方法名String methodName joinPoint.getSignature().getName();//获取相关参数Object[] arguments joinPoint.getArgs();//生成类对象Class targetClass Class.forName(targetName);//获取该类中的方法Method[] methods targetClass.getMethods();String description ;Integer type null;for(Method method : methods) {if(!method.getName().equals(methodName)) {continue;}Class[] clazzs method.getParameterTypes();if(clazzs.length ! arguments.length) {//比较方法中参数个数与从切点中获取的参数个数是否相同原因是方法可以重载哦continue;}description method.getAnnotation(SystemLog.class).description();type method.getAnnotation(SystemLog.class).type().ordinal();map.put(description, description);map.put(type, type);}return map;}}7、EsLogService接口类
EsLogService中我们编写几个常用的接口方法增删改查
/*** 日志操作service*/
public interface EsLogService {/*** 添加日志* param esLog* return*/EsLog saveLog(EsLog esLog);/*** 通过id删除日志* param id*/void deleteLog(String id);/*** 删除全部日志*/void deleteAll();/*** 分页搜索获取日志* param type* param key* param searchVo* param pageable* return*/PageEsLog findAll(Integer type, String key, SearchVo searchVo, Pageable pageable);
}我们简单看一下这个 findAll 方法的实现类吧其他方法就是直接调用ElasticsearchRepository提供的findById()、findAll()、findAllById()、save()等方法。
/*** param type 类型* param key 搜索的关键字* param searchVo* param pageable* return*/
Override
public PageEsLog findAll(Integer type, String key, SearchVo searchVo, Pageable pageable) {if(typenullStrUtil.isBlank(key)StrUtil.isBlank(searchVo.getStartDate())){// 无过滤条件获取全部return logDao.findAll(pageable);}else if(type!nullStrUtil.isBlank(key)StrUtil.isBlank(searchVo.getStartDate())){// 仅有typereturn logDao.findByLogType(type, pageable);}QueryBuilder qb;QueryBuilder qb0 QueryBuilders.termQuery(logType, type);QueryBuilder qb1 QueryBuilders.multiMatchQuery(key, name, requestUrl, requestType,requestParam,username,ip);// 在有type条件下if(StrUtil.isNotBlank(key)StrUtil.isBlank(searchVo.getStartDate())StrUtil.isBlank(searchVo.getEndDate())){// 仅有keyqb QueryBuilders.boolQuery().must(qb0).must(qb1);}else if(StrUtil.isBlank(key)StrUtil.isNotBlank(searchVo.getStartDate())StrUtil.isNotBlank(searchVo.getEndDate())){// 仅有时间范围Long start DateUtil.parse(searchVo.getStartDate()).getTime();Long end DateUtil.endOfDay(DateUtil.parse(searchVo.getEndDate())).getTime();QueryBuilder qb2 QueryBuilders.rangeQuery(timeMillis).gte(start).lte(end);qb QueryBuilders.boolQuery().must(qb0).must(qb2);}else{// 两者都有Long start DateUtil.parse(searchVo.getStartDate()).getTime();Long end DateUtil.endOfDay(DateUtil.parse(searchVo.getEndDate())).getTime();QueryBuilder qb2 QueryBuilders.rangeQuery(timeMillis).gte(start).lte(end);qb QueryBuilders.boolQuery().must(qb0).must(qb1).must(qb2);}//多字段搜索return logDao.search(qb, pageable);
}8、controller层测试方法
/*** 日志操作controller*/
Slf4j
RestController
RequestMapping(/log)
public class LogController {Autowiredprivate EsLogService esLogService;/*** 测试*/SystemLog(description 测试, type LogType.OPERATION)RequestMapping(value /getA, method RequestMethod.GET)public ResultObject getA(String va){return ResultUtil.success(测试成功);}/*** 查询全部* param type es 中的logType 不能为空* param key 查询的关键字* param searchVo* param pageVo* return*/RequestMapping(value /getAll, method RequestMethod.GET)public ResultObject getAll(RequestParam(required false) Integer type,RequestParam String key,SearchVo searchVo,PageVo pageVo){PageEsLog es esLogService.findAll(type, key, searchVo, PageUtil.initPage(pageVo));return ResultUtil.data(es);}/*** 批量删除* param ids* return*/RequestMapping(value /delByIds, method RequestMethod.POST)public ResultObject delByIds(RequestParam String[] ids){for(String id : ids){esLogService.deleteLog(id);}return ResultUtil.success(删除成功);}/*** 全部删除* return*/RequestMapping(value /delAll, method RequestMethod.POST)public ResultObject delAll(){esLogService.deleteAll();return ResultUtil.success(删除成功);}
}以 getA()方法为例直接通过浏览器调用http://127.0.0.1:6666/log/getA然后在 ES 中查询一下是否保存成功 以getAll()方法为例再测试一下查询方法在浏览器输入 http://127.0.0.1:8888/log/getAll?keytype2返回如下
9、最后补充
本节是我拆分出来的一个demo经测试增删改查是没问题、同时查询方法加入了分页查询具体代码细节可以下载本节源码自行查看。
源码下载链接https://niceyoo.lanzous.com/id0yikf
如果你觉得本篇文章对你有所帮助不如右上角关注一下我~ 18年专科毕业后期间一度迷茫最近我创建了一个公众号用来记录自己的成长。