From 456117e989997b5776a4cf4ca1cc59743eb17adb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=BC=A0=E5=B0=8F=E4=B8=9C?=
<13396135+anton-aoi@user.noreply.gitee.com>
Date: Wed, 22 Nov 2023 11:15:47 +0800
Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0AOP=E6=97=A5=E5=BF=97?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
pom.xml | 24 +
.../february/common/log/aspect/LogAspect.java | 500 +++++++++---------
.../february/common/log/aspect/WebLog.java | 16 +
.../common/log/aspect/WebLogAspect.java | 87 +++
4 files changed, 377 insertions(+), 250 deletions(-)
create mode 100644 src/main/java/com/february/common/log/aspect/WebLog.java
create mode 100644 src/main/java/com/february/common/log/aspect/WebLogAspect.java
diff --git a/pom.xml b/pom.xml
index 8b626b4..2fc37e0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -28,6 +28,30 @@
3.6.3
compile
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-aop
+
+
+
+ com.google.code.gson
+ gson
+ 2.8.9
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+
diff --git a/src/main/java/com/february/common/log/aspect/LogAspect.java b/src/main/java/com/february/common/log/aspect/LogAspect.java
index 0a61c17..9a85e69 100644
--- a/src/main/java/com/february/common/log/aspect/LogAspect.java
+++ b/src/main/java/com/february/common/log/aspect/LogAspect.java
@@ -1,250 +1,250 @@
-package com.february.common.log.aspect;
-
-import java.util.Collection;
-import java.util.Map;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import com.february.system.domain.SysOperLog;
-import org.apache.commons.lang3.ArrayUtils;
-import org.aspectj.lang.JoinPoint;
-import org.aspectj.lang.annotation.AfterReturning;
-import org.aspectj.lang.annotation.AfterThrowing;
-import org.aspectj.lang.annotation.Aspect;
-import org.aspectj.lang.annotation.Before;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.core.NamedThreadLocal;
-import org.springframework.http.HttpMethod;
-import org.springframework.stereotype.Component;
-import org.springframework.validation.BindingResult;
-import org.springframework.web.multipart.MultipartFile;
-import com.alibaba.fastjson2.JSON;
-import com.february.common.core.utils.ServletUtils;
-import com.february.common.core.utils.StringUtils;
-import com.february.common.core.utils.ip.IpUtils;
-import com.february.common.log.annotation.Log;
-import com.february.common.log.enums.BusinessStatus;
-import com.february.common.log.filter.PropertyPreExcludeFilter;
-import com.february.common.log.service.AsyncLogService;
-import com.february.common.security.utils.SecurityUtils;
-
-/**
- * 操作日志记录处理
- *
- * @author february
- */
-@Aspect
-@Component
-public class LogAspect
-{
- private static final Logger log = LoggerFactory.getLogger(LogAspect.class);
-
- /** 排除敏感属性字段 */
- public static final String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" };
-
- /** 计算操作消耗时间 */
- private static final ThreadLocal TIME_THREADLOCAL = new NamedThreadLocal("Cost Time");
-
- @Autowired
- private AsyncLogService asyncLogService;
-
- /**
- * 处理请求前执行
- */
- @Before(value = "@annotation(controllerLog)")
- public void boBefore(JoinPoint joinPoint, Log controllerLog)
- {
- TIME_THREADLOCAL.set(System.currentTimeMillis());
- }
-
- /**
- * 处理完请求后执行
- *
- * @param joinPoint 切点
- */
- @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")
- public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult)
- {
- handleLog(joinPoint, controllerLog, null, jsonResult);
- }
-
- /**
- * 拦截异常操作
- *
- * @param joinPoint 切点
- * @param e 异常
- */
- @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e")
- public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e)
- {
- handleLog(joinPoint, controllerLog, e, null);
- }
-
- protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult)
- {
- try
- {
- // *========数据库日志=========*//
- SysOperLog operLog = new SysOperLog();
- operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
- // 请求的地址
- String ip = IpUtils.getIpAddr();
- operLog.setOperIp(ip);
- operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255));
- String username = SecurityUtils.getUsername();
- if (StringUtils.isNotBlank(username))
- {
- operLog.setOperName(username);
- }
-
- if (e != null)
- {
- operLog.setStatus(BusinessStatus.FAIL.ordinal());
- operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
- }
- // 设置方法名称
- String className = joinPoint.getTarget().getClass().getName();
- String methodName = joinPoint.getSignature().getName();
- operLog.setMethod(className + "." + methodName + "()");
- // 设置请求方式
- operLog.setRequestMethod(ServletUtils.getRequest().getMethod());
- // 处理设置注解上的参数
- getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult);
- // 设置消耗时间
- operLog.setCostTime(System.currentTimeMillis() - TIME_THREADLOCAL.get());
- // 保存数据库
- asyncLogService.saveSysLog(operLog);
- }
- catch (Exception exp)
- {
- // 记录本地异常日志
- log.error("异常信息:{}", exp.getMessage());
- exp.printStackTrace();
- }
- finally
- {
- TIME_THREADLOCAL.remove();
- }
- }
-
- /**
- * 获取注解中对方法的描述信息 用于Controller层注解
- *
- * @param log 日志
- * @param operLog 操作日志
- * @throws Exception
- */
- public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog, Object jsonResult) throws Exception
- {
- // 设置action动作
- operLog.setBusinessType(log.businessType().ordinal());
- // 设置标题
- operLog.setTitle(log.title());
- // 设置操作人类别
- operLog.setOperatorType(log.operatorType().ordinal());
- // 是否需要保存request,参数和值
- if (log.isSaveRequestData())
- {
- // 获取参数的信息,传入到数据库中。
- setRequestValue(joinPoint, operLog, log.excludeParamNames());
- }
- // 是否需要保存response,参数和值
- if (log.isSaveResponseData() && StringUtils.isNotNull(jsonResult))
- {
- operLog.setJsonResult(StringUtils.substring(JSON.toJSONString(jsonResult), 0, 2000));
- }
- }
-
- /**
- * 获取请求的参数,放到log中
- *
- * @param operLog 操作日志
- * @throws Exception 异常
- */
- private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog, String[] excludeParamNames) throws Exception
- {
- String requestMethod = operLog.getRequestMethod();
- Map, ?> paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest());
- if (StringUtils.isEmpty(paramsMap)
- && (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)))
- {
- String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames);
- operLog.setOperParam(StringUtils.substring(params, 0, 2000));
- }
- else
- {
- operLog.setOperParam(StringUtils.substring(JSON.toJSONString(paramsMap, excludePropertyPreFilter(excludeParamNames)), 0, 2000));
- }
- }
-
- /**
- * 参数拼装
- */
- private String argsArrayToString(Object[] paramsArray, String[] excludeParamNames)
- {
- String params = "";
- if (paramsArray != null && paramsArray.length > 0)
- {
- for (Object o : paramsArray)
- {
- if (StringUtils.isNotNull(o) && !isFilterObject(o))
- {
- try
- {
- String jsonObj = JSON.toJSONString(o, excludePropertyPreFilter(excludeParamNames));
- params += jsonObj.toString() + " ";
- }
- catch (Exception e)
- {
- }
- }
- }
- }
- return params.trim();
- }
-
- /**
- * 忽略敏感属性
- */
- public PropertyPreExcludeFilter excludePropertyPreFilter(String[] excludeParamNames)
- {
- return new PropertyPreExcludeFilter().addExcludes(ArrayUtils.addAll(EXCLUDE_PROPERTIES, excludeParamNames));
- }
-
- /**
- * 判断是否需要过滤的对象。
- *
- * @param o 对象信息。
- * @return 如果是需要过滤的对象,则返回true;否则返回false。
- */
- @SuppressWarnings("rawtypes")
- public boolean isFilterObject(final Object o)
- {
- Class> clazz = o.getClass();
- if (clazz.isArray())
- {
- return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
- }
- else if (Collection.class.isAssignableFrom(clazz))
- {
- Collection collection = (Collection) o;
- for (Object value : collection)
- {
- return value instanceof MultipartFile;
- }
- }
- else if (Map.class.isAssignableFrom(clazz))
- {
- Map map = (Map) o;
- for (Object value : map.entrySet())
- {
- Map.Entry entry = (Map.Entry) value;
- return entry.getValue() instanceof MultipartFile;
- }
- }
- return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
- || o instanceof BindingResult;
- }
-}
+//package com.february.common.log.aspect;
+//
+//import java.util.Collection;
+//import java.util.Map;
+//import javax.servlet.http.HttpServletRequest;
+//import javax.servlet.http.HttpServletResponse;
+//
+//import com.february.system.domain.SysOperLog;
+//import org.apache.commons.lang3.ArrayUtils;
+//import org.aspectj.lang.JoinPoint;
+//import org.aspectj.lang.annotation.AfterReturning;
+//import org.aspectj.lang.annotation.AfterThrowing;
+//import org.aspectj.lang.annotation.Aspect;
+//import org.aspectj.lang.annotation.Before;
+//import org.slf4j.Logger;
+//import org.slf4j.LoggerFactory;
+//import org.springframework.beans.factory.annotation.Autowired;
+//import org.springframework.core.NamedThreadLocal;
+//import org.springframework.http.HttpMethod;
+//import org.springframework.stereotype.Component;
+//import org.springframework.validation.BindingResult;
+//import org.springframework.web.multipart.MultipartFile;
+//import com.alibaba.fastjson2.JSON;
+//import com.february.common.core.utils.ServletUtils;
+//import com.february.common.core.utils.StringUtils;
+//import com.february.common.core.utils.ip.IpUtils;
+//import com.february.common.log.annotation.Log;
+//import com.february.common.log.enums.BusinessStatus;
+//import com.february.common.log.filter.PropertyPreExcludeFilter;
+//import com.february.common.log.service.AsyncLogService;
+//import com.february.common.security.utils.SecurityUtils;
+//
+///**
+// * 操作日志记录处理
+// *
+// * @author february
+// */
+//@Aspect
+//@Component
+//public class LogAspect
+//{
+// private static final Logger log = LoggerFactory.getLogger(LogAspect.class);
+//
+// /** 排除敏感属性字段 */
+// public static final String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" };
+//
+// /** 计算操作消耗时间 */
+// private static final ThreadLocal TIME_THREADLOCAL = new NamedThreadLocal("Cost Time");
+//
+// @Autowired
+// private AsyncLogService asyncLogService;
+//
+// /**
+// * 处理请求前执行
+// */
+// @Before(value = "@annotation(controllerLog)")
+// public void boBefore(JoinPoint joinPoint, Log controllerLog)
+// {
+// TIME_THREADLOCAL.set(System.currentTimeMillis());
+// }
+//
+// /**
+// * 处理完请求后执行
+// *
+// * @param joinPoint 切点
+// */
+// @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")
+// public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult)
+// {
+// handleLog(joinPoint, controllerLog, null, jsonResult);
+// }
+//
+// /**
+// * 拦截异常操作
+// *
+// * @param joinPoint 切点
+// * @param e 异常
+// */
+// @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e")
+// public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e)
+// {
+// handleLog(joinPoint, controllerLog, e, null);
+// }
+//
+// protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult)
+// {
+// try
+// {
+// // *========数据库日志=========*//
+// SysOperLog operLog = new SysOperLog();
+// operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
+// // 请求的地址
+// String ip = IpUtils.getIpAddr();
+// operLog.setOperIp(ip);
+// operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255));
+// String username = SecurityUtils.getUsername();
+// if (StringUtils.isNotBlank(username))
+// {
+// operLog.setOperName(username);
+// }
+//
+// if (e != null)
+// {
+// operLog.setStatus(BusinessStatus.FAIL.ordinal());
+// operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
+// }
+// // 设置方法名称
+// String className = joinPoint.getTarget().getClass().getName();
+// String methodName = joinPoint.getSignature().getName();
+// operLog.setMethod(className + "." + methodName + "()");
+// // 设置请求方式
+// operLog.setRequestMethod(ServletUtils.getRequest().getMethod());
+// // 处理设置注解上的参数
+// getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult);
+// // 设置消耗时间
+// operLog.setCostTime(System.currentTimeMillis() - TIME_THREADLOCAL.get());
+// // 保存数据库
+// asyncLogService.saveSysLog(operLog);
+// }
+// catch (Exception exp)
+// {
+// // 记录本地异常日志
+// log.error("异常信息:{}", exp.getMessage());
+// exp.printStackTrace();
+// }
+// finally
+// {
+// TIME_THREADLOCAL.remove();
+// }
+// }
+//
+// /**
+// * 获取注解中对方法的描述信息 用于Controller层注解
+// *
+// * @param log 日志
+// * @param operLog 操作日志
+// * @throws Exception
+// */
+// public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog, Object jsonResult) throws Exception
+// {
+// // 设置action动作
+// operLog.setBusinessType(log.businessType().ordinal());
+// // 设置标题
+// operLog.setTitle(log.title());
+// // 设置操作人类别
+// operLog.setOperatorType(log.operatorType().ordinal());
+// // 是否需要保存request,参数和值
+// if (log.isSaveRequestData())
+// {
+// // 获取参数的信息,传入到数据库中。
+// setRequestValue(joinPoint, operLog, log.excludeParamNames());
+// }
+// // 是否需要保存response,参数和值
+// if (log.isSaveResponseData() && StringUtils.isNotNull(jsonResult))
+// {
+// operLog.setJsonResult(StringUtils.substring(JSON.toJSONString(jsonResult), 0, 2000));
+// }
+// }
+//
+// /**
+// * 获取请求的参数,放到log中
+// *
+// * @param operLog 操作日志
+// * @throws Exception 异常
+// */
+// private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog, String[] excludeParamNames) throws Exception
+// {
+// String requestMethod = operLog.getRequestMethod();
+// Map, ?> paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest());
+// if (StringUtils.isEmpty(paramsMap)
+// && (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)))
+// {
+// String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames);
+// operLog.setOperParam(StringUtils.substring(params, 0, 2000));
+// }
+// else
+// {
+// operLog.setOperParam(StringUtils.substring(JSON.toJSONString(paramsMap, excludePropertyPreFilter(excludeParamNames)), 0, 2000));
+// }
+// }
+//
+// /**
+// * 参数拼装
+// */
+// private String argsArrayToString(Object[] paramsArray, String[] excludeParamNames)
+// {
+// String params = "";
+// if (paramsArray != null && paramsArray.length > 0)
+// {
+// for (Object o : paramsArray)
+// {
+// if (StringUtils.isNotNull(o) && !isFilterObject(o))
+// {
+// try
+// {
+// String jsonObj = JSON.toJSONString(o, excludePropertyPreFilter(excludeParamNames));
+// params += jsonObj.toString() + " ";
+// }
+// catch (Exception e)
+// {
+// }
+// }
+// }
+// }
+// return params.trim();
+// }
+//
+// /**
+// * 忽略敏感属性
+// */
+// public PropertyPreExcludeFilter excludePropertyPreFilter(String[] excludeParamNames)
+// {
+// return new PropertyPreExcludeFilter().addExcludes(ArrayUtils.addAll(EXCLUDE_PROPERTIES, excludeParamNames));
+// }
+//
+// /**
+// * 判断是否需要过滤的对象。
+// *
+// * @param o 对象信息。
+// * @return 如果是需要过滤的对象,则返回true;否则返回false。
+// */
+// @SuppressWarnings("rawtypes")
+// public boolean isFilterObject(final Object o)
+// {
+// Class> clazz = o.getClass();
+// if (clazz.isArray())
+// {
+// return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
+// }
+// else if (Collection.class.isAssignableFrom(clazz))
+// {
+// Collection collection = (Collection) o;
+// for (Object value : collection)
+// {
+// return value instanceof MultipartFile;
+// }
+// }
+// else if (Map.class.isAssignableFrom(clazz))
+// {
+// Map map = (Map) o;
+// for (Object value : map.entrySet())
+// {
+// Map.Entry entry = (Map.Entry) value;
+// return entry.getValue() instanceof MultipartFile;
+// }
+// }
+// return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
+// || o instanceof BindingResult;
+// }
+//}
diff --git a/src/main/java/com/february/common/log/aspect/WebLog.java b/src/main/java/com/february/common/log/aspect/WebLog.java
new file mode 100644
index 0000000..8cb7f62
--- /dev/null
+++ b/src/main/java/com/february/common/log/aspect/WebLog.java
@@ -0,0 +1,16 @@
+package com.february.common.log.aspect;
+import java.lang.annotation.*;
+
+/**
+ * 切面类
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+@Documented
+public @interface WebLog {
+ /**
+ * 日志描述信息
+ * @return
+ **/
+ String description() default "";
+}
diff --git a/src/main/java/com/february/common/log/aspect/WebLogAspect.java b/src/main/java/com/february/common/log/aspect/WebLogAspect.java
new file mode 100644
index 0000000..29b75d7
--- /dev/null
+++ b/src/main/java/com/february/common/log/aspect/WebLogAspect.java
@@ -0,0 +1,87 @@
+package com.february.common.log.aspect;
+
+import com.google.gson.Gson;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import java.lang.reflect.Method;
+
+/**
+ * AOP日志
+ */
+@Aspect
+@Component
+public class WebLogAspect {
+
+ private final static Logger logger = LoggerFactory.getLogger(WebLogAspect.class);
+ private static final Gson gson = new Gson(); // 用于重复使用Gson实例
+ private static final String LINE_SEPARATOR = System.lineSeparator();
+
+ @Pointcut("@annotation(com.february.common.log.aspect.WebLog)")
+ public void webLog() {}
+
+ @Before("webLog()")
+ public void doBefore(JoinPoint joinPoint) {
+ // 开始打印请求日志
+ ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+ if (attributes != null) {
+ HttpServletRequest request = attributes.getRequest();
+
+ // 获取 @WebLog 注解的描述信息
+ String methodDescription;
+ try {
+ methodDescription = getAspectLogDescription(joinPoint);
+ } catch (Exception e) {
+ methodDescription = "无法获取方法描述信息";
+ logger.error("获取方法描述信息时发生异常: ", e);
+ }
+
+ // 打印请求相关参数
+ logger.info("========================================== Start ==========================================");
+ logger.info("URL : {}", request.getRequestURL().toString());
+ logger.info("Description : {}", methodDescription);
+ logger.info("HTTP Method : {}", request.getMethod());
+ logger.info("Class Method : {}.{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
+ logger.info("IP : {}", request.getRemoteAddr());
+ logger.info("Request Args : {}", gson.toJson(joinPoint.getArgs()));
+ }
+ }
+
+ @After("webLog()")
+ public void doAfter() {
+ logger.info("=========================================== End ==========================================={}", LINE_SEPARATOR);
+ }
+
+ @Around("webLog()")
+ public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
+ long startTime = System.currentTimeMillis();
+ Object result = proceedingJoinPoint.proceed();
+ logger.info("Response Args : {}", gson.toJson(result));
+ logger.info("Time-Consuming : {} ms", System.currentTimeMillis() - startTime);
+ return result;
+ }
+
+ private String getAspectLogDescription(JoinPoint joinPoint) throws ClassNotFoundException {
+ String targetName = joinPoint.getTarget().getClass().getName();
+ String methodName = joinPoint.getSignature().getName();
+ Object[] arguments = joinPoint.getArgs();
+ Class> targetClass = Class.forName(targetName);
+ Method[] methods = targetClass.getMethods();
+ for (Method method : methods) {
+ if (method.getName().equals(methodName) && method.getParameterTypes().length == arguments.length) {
+ WebLog webLog = method.getAnnotation(WebLog.class);
+ if (webLog != null) {
+ return webLog.description();
+ }
+ }
+ }
+ return "";
+ }
+}