分布式通过AOP进行操作日志记录

一、公共类

定义业务操作类型

        根据需要自定义,标识某方法执行的操作

/**
* 业务操作类型
*
* @author ruoyi
*/
public enum BusinessType
{
/**
* 其它
*/
OTHER,
/**
* 新增
*/
INSERT,
/**
* 修改
*/
UPDATE,
/**
* 删除
*/
DELETE,
/**
* 授权
*/
GRANT,
/**
* 导出
*/
EXPORT,
/**
* 导入
*/
IMPORT,
/**
* 强退
*/
FORCE,
/**
* 生成代码
*/
GENCODE,
/**
* 清空数据
*/
CLEAN,
}

定义操作人类别

        根据需要定义操作人类型()

/**
* 操作人类别
*
* @author ruoyi
*/
public enum OperatorType
{
/**
* 其它
*/
OTHER,
/**
* 后台用户
*/
MANAGE,
/**
* 手机端用户
*/
MOBILE
}

自定义操作日志记录注解

@Target

  • @Target 说明了Annotation所修饰的对象范围
  • 取值(ElementType)有:    
    • 1.CONSTRUCTOR:用于描述构造器 
    • 2.FIELD:用于描述域    
    • 3.LOCAL_VARIABLE:用于描述局部变量
    • 4.METHOD:用于描述方法
    • 5.PACKAGE:用于描述包
    • 6.PARAMETER:用于描述参数
    • 7.TYPE:用于描述类、接口(包括注解类型) 或enum声明
    • 8.ANNOTATION_TYPE: 注释类型声明
    • 9.TYPE_PARAMETER: 类型参数声明
    • 10.TYPE_USE: 类型的使用

@Retention

  • @Retention定义了该Annotation被保留的时间长短:
    • 某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。使用这个meta-Annotation可以对 Annotation的“生命周期”限制。
  • 取值(RetentionPoicy)有:    
    • 1.SOURCE:在源文件中有效(即源文件保留)
    • 2.CLASS:在class文件中有效(即class保留)
    • 3.RUNTIME:在运行时有效(即运行时保留)

        这3个生命周期分别对应于:Java源文件(.java文件) ---> .class文件 ---> 内存中的字节码。

@Documented

  • @Documented用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。
import com.deerChain.log.enums.BusinessType;
import com.deerChain.log.enums.OperatorType;
import java.lang.annotation.*;
/**
* 自定义操作日志记录注解
*
* @author ruoyi
*
*/
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log
{
/**
* 模块
*/
public String title() default "";
/**
* 功能
*/
public BusinessType businessType() default BusinessType.OTHER;
/**
* 操作人类别
*/
public OperatorType operatorType() default OperatorType.MANAGE;
/**
* 是否保存请求的参数
*/
public boolean isSaveRequestData() default true;
}

定义日志类

        定义需要记录的日志信息

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import org.hibernate.annotations.SelectBeforeUpdate;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
/**
* 操作日志
* @author Administrator
*
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
@SelectBeforeUpdate
@DynamicInsert
@DynamicUpdate
@Table(name="t_manager_log")
public class ManagerLoginLog implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) //自动增长
@Column(name = "id",length =11)
private Integer id;
@Column(name="managerId",length=44)
@NotNull
private String managerId;//操作人ID
@Column(name="createTime",length=10)
@NotNull
private Integer createTime;//添加时间
@Column(name = "ip")
private String ip;//IP地址
@Column(name = "mapper")
private String mapper;//操作的Api地址
@Column(name = "param")
private String param;//传入参数
@Column(name = "businessType")
private Integer businessType;//业务类型
@Column(name = "title")
private String title;//操作模块
@Column(name = "operatorType")
private Integer operatorType;//操作类别0=其他 1=后台 2=手机端
@Column(name = "jsonResult")
private String jsonResult;//返回参数
@Column(name = "requestMethod")
private String requestMethod;//请求方式
@Column(name = "errorMsg")
private String errorMsg;//错误信息
@Column(name = "method")
private String method;//方法名称
}

二、定义切面类

在需要使用日志记录的模块定义切面类

Spring AOP 相关注解

  • @Aspect :将一个 java 类定义为切面类。
  • @Pointcut :定义一个切入点,可以是一个规则表达式。
  • @Before :在切入点开始处切入内容。
  • @After :在切入点结尾处切入内容。
  • @AfterReturning :在切入点 return 内容之后切入内容(可以用来对处理返回值做一些加工处理)。
  • @Around :在切入点前后切入内容,并自己控制何时执行切入点自身的内容。
  • @AfterThrowing :用来处理当切入内容部分抛出异常之后的处理逻辑。

其中 @Before 、 @After 、 @AfterReturning 、 @Around 、 @AfterThrowing 都属于通知。

import com.alibaba.fastjson.JSON;
import net.sf.json.JSONObject;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;
import redis.clients.jedis.Jedis;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.*;
/**
* 操作日志记录处理
*
* @author ruoyi
*/
@Aspect
@Component
public class LogAspect
{
private static final Logger log = LoggerFactory.getLogger(LogAspect.class);
@Autowired
private ManagerFeign managerFeign;
@Autowired
private EnterpriseFeign enterpriseFeign;
@Autowired
private HttpServletRequest request;
// 配置织入点
@Pointcut("@annotation(com.deerChain.log.annotation.Log)")
public void logPointCut()
{
}
/**
* 处理完请求后执行
*
* @param joinPoint 切点
*/
@AfterReturning(pointcut = "logPointCut()", returning = "jsonResult")
public void doAfterReturning(JoinPoint joinPoint, Object jsonResult)
{
handleLog(joinPoint, null, jsonResult);
}
/**
* 拦截异常操作
*
* @param joinPoint 切点
* @param e 异常
*/
@AfterThrowing(value = "logPointCut()", throwing = "e")
public void doAfterThrowing(JoinPoint joinPoint, Exception e)
{
handleLog(joinPoint, e, null);
}
protected void handleLog(final JoinPoint joinPoint, final Exception e, Object jsonResult)
{
try
{
// 获得注解
Log controllerLog = getAnnotationLog(joinPoint);
if (controllerLog == null)
{
return;
}
// *========数据库日志=========*//
ManagerLoginLog operLog = new ManagerLoginLog();
// 请求的地址
String ip = utils.getIPAddress(request);
operLog.setIp(ip);
// 返回参数
AjaxResult ajaxResult = (AjaxResult)jsonResult;
operLog.setJsonResult("{ statusCode: "+ajaxResult.getStatusCode()+", message: "+ajaxResult.getMessage()+", data: "+ajaxResult.getData()+" }");
operLog.setCreateTime(utils.getNowTimeSecond());
operLog.setMapper(request.getRequestURI());
// 获取当前登录人id (该项目存在redis中,通过request取出前台传过来的token获得)
EnterpriseAccount account = enterpriseAccount.getAccount(request);
if (account != null) {
if (!utils.isNullorEmpty(account.getId()))
{
operLog.setManagerId(account.getId());
}
}
if (e != null)
{
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(request.getMethod());
// 管理端
operLog.setType(1);
// 处理设置注解上的参数
getControllerMethodDescription(joinPoint, controllerLog, operLog);
// 保存数据库
managerFeign.saveManagerLoginLog(operLog);
}
catch (Exception exp)
{
// 记录本地异常日志
log.error("==前置通知异常==");
log.error("异常信息:{}", exp.getMessage());
exp.printStackTrace();
}
}
/**
* 获取注解中对方法的描述信息 用于Controller层注解
*
* @param log 日志
* @param operLog 操作日志
* @throws Exception
*/
public void getControllerMethodDescription(JoinPoint joinPoint, Log log, ManagerLoginLog operLog) throws Exception
{
// 设置action动作
operLog.setBusinessType(log.businessType().ordinal());
// 设置标题
operLog.setTitle(log.title());
// 设置操作人类别
try {
operLog.setOperatorType(IsWebOrPhone.isWebOrPhone(request)?2:1);
} catch (Exception e) {
operLog.setOperatorType(0);
}
// 是否需要保存request,参数和值
if (log.isSaveRequestData())
{
//获得所有的参数名称
Enumeration<String> em = request.getParameterNames();
Map<String,Object> map = new HashMap<>();
while (em.hasMoreElements()) {
String name = em.nextElement();
if(!"_".equals(name)){
String value = request.getParameter(name);
map.put(name, value);
}
}
String param;
if(map.size() != 0){
JSONObject json = JSONObject.fromObject(map);
param = json.toString();
}else{
param= HttpHelper.getBodyString(request);
}
operLog.setParam(param);
// 获取参数的信息,传入到数据库中。
//setRequestValue(joinPoint, operLog);
}
}
/**
* 是否存在注解,如果存在就获取
*/
private Log getAnnotationLog(JoinPoint joinPoint) throws Exception
{
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
if (method != null)
{
return method.getAnnotation(Log.class);
}
return null;
}
}

三、使用@Log()注解为需要记录日志的方法添加日志

  • title: 记录方法信息
  • businessType: 记录该方法执行的操作
  • operatorType: 记录操作人类型
@Log(title = "会员取消订单", businessType = BusinessType.UPDATE,operatorType = OperatorType.MOBILE)
@PostMapping(value = "/orders/cancelling")
public AjaxResult cancelling(@RequestParam("json")String json) throws Exception{
return ordersFeigin.cancelling(json);
}

posted @   初学者入坑  阅读(32)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示