From 33889b72f970fbb88d2a0f9be69c868f218a1b26 Mon Sep 17 00:00:00 2001 From: "583641232@qq.com" <583641232@qq.com> Date: Wed, 22 Jan 2025 16:18:43 +0800 Subject: [PATCH] :sparkles: v4 --- client/pom.xml | 15 +- .../alog/client/task/ALogConfig.java | 26 +++ .../alog/client/task/Monitor.java | 9 +- .../alog/common/annotation/ActionLog.java | 20 +- .../common/annotation/IgnoreRecordField.java | 21 ++ .../common/annotation/NeedRecordField.java | 2 + .../alog/common/utils/IdWorker.java | 23 +++ example/pom.xml | 5 - .../clientdemo/aspectj/ActionLogAspect.java | 182 ++++++++++-------- .../demo/controller/DemoUserController.java | 25 +-- .../clientdemo/utils/BeanCompareUtils.java | 72 +++++++ .../alog/clientdemo/utils/BeanCopyUtils.java | 111 ----------- .../alog/clientdemo/utils/StringUtils.java | 1 - example/src/main/resources/application.yml | 8 +- worker/pom.xml | 10 +- .../alog/worker/disruptor/TracerConsumer.java | 4 +- worker/src/main/resources/application.yml | 12 +- worker/src/main/resources/jlog.sql | 2 +- 18 files changed, 302 insertions(+), 246 deletions(-) create mode 100644 client/src/main/java/com/inscloudtech/alog/client/task/ALogConfig.java create mode 100644 common/src/main/java/com/inscloudtech/alog/common/annotation/IgnoreRecordField.java create mode 100644 example/src/main/java/com/inscloudtech/alog/clientdemo/utils/BeanCompareUtils.java delete mode 100644 example/src/main/java/com/inscloudtech/alog/clientdemo/utils/BeanCopyUtils.java diff --git a/client/pom.xml b/client/pom.xml index c5066d7..f321776 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -17,12 +17,17 @@ alog-common 0.1.1 + + + org.springframework.boot + spring-boot-starter-aop + - + - - org.springframework.boot - spring-boot-starter-aop - com.inscloudtech diff --git a/example/src/main/java/com/inscloudtech/alog/clientdemo/aspectj/ActionLogAspect.java b/example/src/main/java/com/inscloudtech/alog/clientdemo/aspectj/ActionLogAspect.java index 47c7129..6ca679d 100644 --- a/example/src/main/java/com/inscloudtech/alog/clientdemo/aspectj/ActionLogAspect.java +++ b/example/src/main/java/com/inscloudtech/alog/clientdemo/aspectj/ActionLogAspect.java @@ -2,8 +2,6 @@ package com.inscloudtech.alog.clientdemo.aspectj; import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.inscloudtech.alog.client.tracerholder.TracerHolder; import com.inscloudtech.alog.clientdemo.utils.*; import com.inscloudtech.alog.common.enums.BusinessStatus; import com.inscloudtech.alog.common.enums.BusinessType; @@ -23,10 +21,7 @@ import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; +import java.lang.reflect.*; import java.util.Arrays; import java.util.Collection; import java.util.List; @@ -34,7 +29,7 @@ import java.util.Map; import java.util.stream.Collectors; /** - * 操作日志记录处理 + * 行为日志记录处理 * * @author inscloudtech */ @@ -46,69 +41,10 @@ public class ActionLogAspect { public Object doAround(ProceedingJoinPoint joinPoint, ActionLog actionLog) throws Throwable { ActionLogMessage actionLogMessage = new ActionLogMessage(); Object[] args = joinPoint.getArgs(); - Object arg = args[0]; //当操作行为是修改数据时,需要上报,修改前后数据值 if(actionLog.businessType().equals(BusinessType.UPDATE)){ - Class mapperClass = actionLog.mapperClass(); - //service,mapper对应的实体类 - Class entityClass = null; - boolean isMapperClass = BaseMapper.class.isAssignableFrom(mapperClass); - //不是service类 - if(isMapperClass){ - //获取Mapper对应的entity - Type superClass = mapperClass.getGenericInterfaces()[0]; - if (superClass instanceof ParameterizedType) { - // 获取类型参数数组,即 [<当前接口的 Class 对象>, , ] - Type[] typeArr = ((ParameterizedType) superClass).getActualTypeArguments(); - // 第二个类型参数就是 SysUser 的 Class 对象 - entityClass = (Class) typeArr[1]; - } - }else{ - entityClass = actionLog.entityClass(); - } - - //获取tableId字段 - String tableIdField = ""; - Field[] fields = entityClass.getDeclaredFields(); - for(Field field : fields) { - field.setAccessible(true); - if (field.isAnnotationPresent(TableId.class)) { - tableIdField = field.getName(); - break; - } - } - Object afterEntity = null; - try { - // 将 arg 转换为 entityClass 类型的对象 - afterEntity = entityClass.cast(arg); - // 获取 tableIdField 对应的 Field 对象 - Field idField = entityClass.getDeclaredField(tableIdField); - idField.setAccessible(true); - // 获取 id 的值 - Object idValue = idField.get(afterEntity); - System.out.println("ID 的值为: " + idValue); - } catch (IllegalAccessException | NoSuchFieldException e) { - e.printStackTrace(); - } - - Long businessId = 0L; - //根据mapper查询一条修改前的数据 - Object serviceObj = SpringUtils.getBean(mapperClass); -// Method method = clazz.getMethod(methodName, Serializable.class);//Long.class - String methodName = actionLog.methodName(); - List methodList = Arrays.stream(mapperClass.getMethods()).filter(method -> method.getName().equals(methodName)).collect(Collectors.toList()); - Method method = methodList.get(0);// - Object[] argsz = new Object[]{businessId}; - Object beforeEntity = method.invoke(serviceObj, argsz); - //比较两个对象字段变化 - Map changeMap = BeanCopyUtils.getChangedFields(beforeEntity,afterEntity); - String before = null == changeMap.get("before")?null:changeMap.get("before"); - String after = null == changeMap.get("after")?null:changeMap.get("after"); - //保存更新前后信息 - actionLogMessage.setBeforeValue(before); - actionLogMessage.setAfterValue(after); - actionLogMessage.setBusinessId(businessId); + setBeforeAfterValue(args,actionLog,actionLogMessage); } Object result; @@ -124,9 +60,104 @@ public class ActionLogAspect { // 计算响应时间 long responseTime = endTime - startTime; actionLogMessage.setResponseTime(responseTime); + // 请求的地址 + String ip = ServletUtils.getClientIP(); + actionLogMessage.setOperIp(ip); + actionLogMessage.setOperUrl(StringUtils.sub(ServletUtils.getRequest().getRequestURI(), 0, 255)); + + //从业务系统获取传入 +// LoginUser loginUser = LoginHelper.getLoginUser();//获取当前用户,应用自行实现 +// loginUser.getUserId(); +// loginUser.getNickname(); +// loginUser.getDeptName(); + actionLogMessage.setOperUserId("2322434253"); + actionLogMessage.setOperUserName("演示用户"); + actionLogMessage.setDeptName("演示部门"); + handleLog(joinPoint, actionLog, null, result,actionLogMessage); return result; } + + /** + * 示例是基于mybatis-plus mapper类的的selectById方法获取数据库值,与修改后比较变化值,并记录。 + * 应用方可重写该方法,最终能获取到: + * 更新前值(beforeValue),更新新后值(afterValue),业务数据id(businessId) + * 即可。 + */ + private void setBeforeAfterValue(Object[] args,ActionLog actionLog,ActionLogMessage actionLogMessage){ + Object arg = args[0]; + Class mapperClass = actionLog.mapperClass(); + + Class entityClass = actionLog.entityClass(); + + //获取tableId字段 + String tableIdField = ""; + Field[] fields = entityClass.getDeclaredFields(); + for(Field field : fields) { + field.setAccessible(true); + if (field.isAnnotationPresent(TableId.class)) { + tableIdField = field.getName(); + break; + } + } + Object afterEntity = null; + Long businessId = null; + try { + // 将 arg 转换为 entityClass 类型的对象 + afterEntity = entityClass.cast(arg); + // 获取 tableIdField 对应的 Field 对象 + Field idField = entityClass.getDeclaredField(tableIdField); + idField.setAccessible(true); + // 获取 id 的值 + businessId = (Long)idField.get(afterEntity); + } catch (IllegalAccessException | NoSuchFieldException e) { + e.printStackTrace(); + } + + //根据mapper查询一条修改前的数据 + Object query = SpringUtils.getBean(mapperClass); + String methodName = actionLog.methodName(); + List methodList = Arrays.stream(mapperClass.getMethods()).filter(method -> method.getName().equals(methodName)).collect(Collectors.toList()); + Method method = methodList.get(0);// + Object[] argsz = new Object[]{businessId}; + Object beforeEntity = null; + try { + beforeEntity = method.invoke(query, argsz); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } + //比较两个对象字段变化 + Map changeMap = BeanCompareUtils.getChangedFields(beforeEntity,afterEntity); + String before = null == changeMap.get("before")?null:changeMap.get("before"); + String after = null == changeMap.get("after")?null:changeMap.get("after"); + //保存更新前后信息 + actionLogMessage.setBeforeValue(before); + actionLogMessage.setAfterValue(after); + actionLogMessage.setBusinessId(businessId); + } + + + + + + + + + + + + + + + + + + /** + * ==========================================以下方法无需修改=============================================== + */ + /** * 排除敏感属性字段 */ @@ -135,17 +166,7 @@ public class ActionLogAspect { protected void handleLog(final JoinPoint joinPoint, ActionLog actionLog, final Exception e, Object jsonResult,ActionLogMessage actionLogMessage) { try { - actionLogMessage.setStatus(BusinessStatus.SUCCESS.ordinal()); - // 请求的地址 - String ip = ServletUtils.getClientIP(); - actionLogMessage.setOperIp(ip); - actionLogMessage.setOperUrl(StringUtils.sub(ServletUtils.getRequest().getRequestURI(), 0, 255)); - - //从业务系统获取传入 - actionLogMessage.setOperUserId("2322434253"); - actionLogMessage.setOperUserName("演示用户"); - if (e != null) { actionLogMessage.setStatus(BusinessStatus.FAIL.ordinal()); actionLogMessage.setErrorMsg(StringUtils.sub(e.getMessage(), 0, 2000)); @@ -158,7 +179,7 @@ public class ActionLogAspect { actionLogMessage.setRequestMethod(ServletUtils.getRequest().getMethod()); // 处理设置注解上的参数 getControllerMethodDescription(joinPoint, actionLog, actionLogMessage, jsonResult); - long tracerId = IdWorker.nextId(); + long tracerId = IdWorker.nextIdWithAppId(); actionLogMessage.setLogId(tracerId); actionLogMessage.setCreateTime(System.currentTimeMillis()); UdpSender.offerActionLogger(actionLogMessage); @@ -169,11 +190,6 @@ public class ActionLogAspect { } } - public static void main(String[] args) { - long l = IdWorker.nextId(); - System.out.println((l+"").length()); - } - /** * 获取注解中对方法的描述信息 用于Controller层注解 diff --git a/example/src/main/java/com/inscloudtech/alog/clientdemo/demo/controller/DemoUserController.java b/example/src/main/java/com/inscloudtech/alog/clientdemo/demo/controller/DemoUserController.java index 66a5e19..adc4382 100644 --- a/example/src/main/java/com/inscloudtech/alog/clientdemo/demo/controller/DemoUserController.java +++ b/example/src/main/java/com/inscloudtech/alog/clientdemo/demo/controller/DemoUserController.java @@ -49,6 +49,18 @@ public class DemoUserController { return new Response(demoUserService.page(page, Wrappers.query(DemoUser))); } + /** + * 新增示例用户 + * @param + * @return Response + */ + @ActionLog(businessName = "用户管理", businessType = BusinessType.INSERT) + @PostMapping + public Response save(@RequestBody DemoUser demoUser) { + return new Response(demoUserService.save(demoUser)); + } + + /** * 通过id查询示例用户 @@ -56,27 +68,18 @@ public class DemoUserController { * @return Response */ @GetMapping("/{id}" ) + @ActionLog(businessName = "用户管理", businessType = BusinessType.SELECT) public Response getById(@PathVariable("id" ) Long id) { return new Response(demoUserService.getById(id)); } - /** - * 新增示例用户 - * @param - * @return Response - */ - @ActionLog(businessName = "用户管理", businessType = BusinessType.INSERT) - @PostMapping - public Response save(@RequestBody DemoUser demoUser) { - return new Response(demoUserService.save(demoUser)); - } /** * 修改示例用户 * @param * @return Response */ - @ActionLog(businessName = "用户管理",mapperClass = DemoUserMapper.class, businessType = BusinessType.UPDATE) + @ActionLog(businessName = "用户管理",mapperClass = DemoUserMapper.class, businessType = BusinessType.UPDATE,entityClass = DemoUser.class) @PutMapping public Response updateById(@RequestBody DemoUser demoUser) { return new Response(demoUserService.updateById(demoUser)); diff --git a/example/src/main/java/com/inscloudtech/alog/clientdemo/utils/BeanCompareUtils.java b/example/src/main/java/com/inscloudtech/alog/clientdemo/utils/BeanCompareUtils.java new file mode 100644 index 0000000..3ee82e9 --- /dev/null +++ b/example/src/main/java/com/inscloudtech/alog/clientdemo/utils/BeanCompareUtils.java @@ -0,0 +1,72 @@ +package com.inscloudtech.alog.clientdemo.utils; + + + +import com.inscloudtech.alog.common.annotation.IgnoreRecordField; +import com.inscloudtech.alog.common.annotation.NeedRecordField; + + +import java.lang.reflect.Field; +import java.util.*; + +/** + * bean字段值比较 + * + * @author inscloudtech + */ + +public class BeanCompareUtils { + + /** + * 排除敏感属性字段 + */ + public static final List EXCLUDE_FIELDS = new ArrayList(); + + /** + * 获取变更内容 + * @param oldBean 更改前的Bean + * @param newBean 更改后的Bean + * @param + * @return + */ + public static Map getChangedFields(T oldBean, T newBean){ + Field[] fields = newBean.getClass().getDeclaredFields(); + StringBuilder beforeBuilder = new StringBuilder(); + StringBuilder afterBuilder = new StringBuilder(); + for(Field field : fields) { + field.setAccessible(true); + String fieldName = field.getName(); + if (field.isAnnotationPresent(IgnoreRecordField.class) || EXCLUDE_FIELDS.contains(fieldName)) { + continue; + } + + try { + Object oldValue = field.get(oldBean); + Object newValue = field.get(newBean); + if(null != newValue && !Objects.equals(newValue, oldValue)) { + if (field.isAnnotationPresent(NeedRecordField.class)) { + String tempFieldName = field.getAnnotation(NeedRecordField.class).fieldName(); + fieldName = StringUtils.isEmpty(tempFieldName)?fieldName:tempFieldName; + } + beforeBuilder.append(fieldName); + beforeBuilder.append(": 【更改前:"); + beforeBuilder.append(oldValue); + beforeBuilder.append("】,"); + + afterBuilder.append(fieldName); + afterBuilder.append(": 【更改后:"); + afterBuilder.append(newValue); + afterBuilder.append("】,"); + + } + } catch (Exception e) { + e.printStackTrace(); + } + } + Map result = new HashMap<>(2); + result.put("before",beforeBuilder.toString()); + result.put("after",afterBuilder.toString()); + return result; + } + +} diff --git a/example/src/main/java/com/inscloudtech/alog/clientdemo/utils/BeanCopyUtils.java b/example/src/main/java/com/inscloudtech/alog/clientdemo/utils/BeanCopyUtils.java deleted file mode 100644 index 64c0dc3..0000000 --- a/example/src/main/java/com/inscloudtech/alog/clientdemo/utils/BeanCopyUtils.java +++ /dev/null @@ -1,111 +0,0 @@ -package com.inscloudtech.alog.clientdemo.utils; - - - -import com.inscloudtech.alog.common.annotation.NeedRecordField; - - -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.*; - -/** - * bean深拷贝工具(基于 cglib 性能优异) - *

- * 重点 cglib 不支持 拷贝到链式对象 - * 例如: 源对象 拷贝到 目标(链式对象) - * 请区分好`浅拷贝`和`深拷贝`再做使用 - * - * @author inscloudtech - */ - -public class BeanCopyUtils { - - /** - * 获取变更内容 - * @param oldBean 更改前的Bean - * @param newBean 更改后的Bean - * @param - * @return - */ - public static Map getChangedFields(T oldBean, T newBean){ - Field[] fields = newBean.getClass().getDeclaredFields(); - StringBuilder beforeBuilder = new StringBuilder(); - StringBuilder afterBuilder = new StringBuilder(); - String fieldName = ""; - for(Field field : fields) { - field.setAccessible(true); - if (!field.isAnnotationPresent(NeedRecordField.class)) { - continue; - } - - try { - Object oldValue = field.get(oldBean); - Object newValue = field.get(newBean); - if(null != newValue && !Objects.equals(newValue, oldValue)) { - fieldName = field.getAnnotation(NeedRecordField.class).fieldName(); - beforeBuilder.append(fieldName); - beforeBuilder.append(": 【更改前:"); - beforeBuilder.append(oldValue); - beforeBuilder.append("】,"); - - afterBuilder.append(fieldName); - afterBuilder.append(": 【更改后:"); - afterBuilder.append(newValue); - afterBuilder.append("】,"); - - } - } catch (Exception e) { - e.printStackTrace(); - } - } - Map result = new HashMap<>(2); - result.put("before",beforeBuilder.toString()); - result.put("after",afterBuilder.toString()); - return result; - } - - - /** - * 利用反射通过get方法获取bean中字段fieldName的值 - * @param bean - * @param fieldName - * @return - * @throws Exception - */ - public static Object getFieldValue(Object bean, String fieldName) - throws Exception { - StringBuffer result = new StringBuffer(); - String methodName = result.append("get") - .append(fieldName.substring(0, 1).toUpperCase()) - .append(fieldName.substring(1)).toString(); - - Object rObject = null; - Method method = null; - - @SuppressWarnings("rawtypes") - Class[] classArr = new Class[0]; - method = bean.getClass().getMethod(methodName, classArr); - rObject = method.invoke(bean, new Object[0]); - - return rObject; - } - - - - private static void setFieldValue(Object bean, String fieldName, Object value) - throws Exception { - StringBuffer result = new StringBuffer(); - String methodName = result.append("set") - .append(fieldName.substring(0, 1).toUpperCase()) - .append(fieldName.substring(1)).toString(); - - /** - * 利用发射调用bean.set方法将value设置到字段 - */ - Class[] classArr = new Class[1]; - classArr[0]="java.lang.String".getClass(); - Method method=bean.getClass().getMethod(methodName,classArr); - method.invoke(bean,value); - } -} diff --git a/example/src/main/java/com/inscloudtech/alog/clientdemo/utils/StringUtils.java b/example/src/main/java/com/inscloudtech/alog/clientdemo/utils/StringUtils.java index b158d3d..c5722d9 100644 --- a/example/src/main/java/com/inscloudtech/alog/clientdemo/utils/StringUtils.java +++ b/example/src/main/java/com/inscloudtech/alog/clientdemo/utils/StringUtils.java @@ -3,7 +3,6 @@ package com.inscloudtech.alog.clientdemo.utils; -import org.springframework.util.AntPathMatcher; import java.util.*; import java.util.function.Function; diff --git a/example/src/main/resources/application.yml b/example/src/main/resources/application.yml index 5ea929e..1bf7d56 100644 --- a/example/src/main/resources/application.yml +++ b/example/src/main/resources/application.yml @@ -27,9 +27,11 @@ spring: - #worker的地址 -aLog-workers: - - 127.0.0.1:39999 + +aLog: + appId: 4667442 + workers: + - 127.0.0.1:39999 #apollo.meta=http://127.0.0.1:8080 diff --git a/worker/pom.xml b/worker/pom.xml index 18d180d..bc87327 100644 --- a/worker/pom.xml +++ b/worker/pom.xml @@ -55,11 +55,11 @@ true - - com.baomidou - mybatis-plus-boot-starter - 3.4.3 - + + + + + diff --git a/worker/src/main/java/com/inscloudtech/alog/worker/disruptor/TracerConsumer.java b/worker/src/main/java/com/inscloudtech/alog/worker/disruptor/TracerConsumer.java index 155881e..3d40a65 100644 --- a/worker/src/main/java/com/inscloudtech/alog/worker/disruptor/TracerConsumer.java +++ b/worker/src/main/java/com/inscloudtech/alog/worker/disruptor/TracerConsumer.java @@ -3,6 +3,7 @@ package com.inscloudtech.alog.worker.disruptor; import com.inscloudtech.alog.common.constant.Constant; import com.inscloudtech.alog.common.constant.LogTypeEnum; import com.inscloudtech.alog.common.model.RunLogMessage; +import com.inscloudtech.alog.common.utils.FastJsonUtils; import com.inscloudtech.alog.common.utils.ProtostuffUtils; import com.inscloudtech.alog.common.utils.ZstdUtils; import com.inscloudtech.alog.common.model.TracerBean; @@ -86,8 +87,9 @@ public class TracerConsumer implements WorkHandler { } else if (LogTypeEnum.SPAN.equals(tracerData.getType())){ dealFilterModel(tracerData.getTracerBeanList()); }else{ + String s = FastJsonUtils.convertObjectToJSON(tracerData.getActionLogs()); // ACTION_LOG - System.out.println("tracerData.getActionLogs() = " + tracerData.getActionLogs()); + logger.info("接收到行为日志{}",s); } } diff --git a/worker/src/main/resources/application.yml b/worker/src/main/resources/application.yml index de2575c..63356d0 100644 --- a/worker/src/main/resources/application.yml +++ b/worker/src/main/resources/application.yml @@ -9,12 +9,12 @@ queue: server: port: 8086 -spring: - datasource: - driverClassName: ru.yandex.clickhouse.ClickHouseDriver - url: jdbc:clickhouse://192.168.3.20:8123/default - username: default - password: +#spring: +# datasource: +# driverClassName: ru.yandex.clickhouse.ClickHouseDriver +# url: jdbc:clickhouse://192.168.3.20:8123/default +# username: default +# password: #ck信息,自行修改 clickhouse: diff --git a/worker/src/main/resources/jlog.sql b/worker/src/main/resources/jlog.sql index 7d4ee0d..a63f279 100644 --- a/worker/src/main/resources/jlog.sql +++ b/worker/src/main/resources/jlog.sql @@ -38,7 +38,7 @@ CREATE TABLE tracer_log ( CREATE TABLE action_log ( log_id Int64 COMMENT '日志主键', - app_id Int64 COMMENT '应用id', + app_id Int8 COMMENT '应用id', title String COMMENT '分类名称', method String COMMENT '方法名称', request_method String COMMENT '请求方式',