Spring Boot 通过AOP+自定义注解实现日志管理

2022-11-01 11:59:39

Spring Boot 通过AOP+自定义注解实现日志管理

操作日志的记录:
记录用户的行为(Controller 请求日志),这叫做业务运行日志
业务运行日志的作用:
1,记录用户的行为 用于后续的分析
2,记录用户的所有的操作

1:操作日志数据结构表

SET NAMES utf8mb4;SET FOREIGN_KEY_CHECKS=0;DROPTABLEIFEXISTS`xcy_operation_log`;CREATETABLE`xcy_operation_log`(`id`bigint(10)UNSIGNEDNOTNULLAUTO_INCREMENTCOMMENT'操作日志自增id',`user_id`bigint(10)UNSIGNEDNOTNULLDEFAULT0COMMENT'操作人id',`ad_account`varchar(50)CHARACTERSET utf8mb4COLLATE utf8mb4_general_ciNOTNULLCOMMENT'操作人AD账号',`operation_ip`varchar(100)CHARACTERSET utf8mb4COLLATE utf8mb4_general_ciNOTNULLDEFAULT''COMMENT'请求ip地址',`operation_model`varchar(100)CHARACTERSET utf8mb4COLLATE utf8mb4_general_ciNOTNULLDEFAULT''COMMENT'操作模块',`operation_type`varchar(100)CHARACTERSET utf8mb4COLLATE utf8mb4_general_ciNOTNULLDEFAULT''COMMENT'操作类型',`operation_desc`varchar(255)CHARACTERSET utf8mb4COLLATE utf8mb4_general_ciNOTNULLDEFAULT''COMMENT'操作描述',`operation_time`datetime(0)NOTNULLDEFAULTCURRENT_TIMESTAMP(0)COMMENT'操作时间',`operation_uri`varchar(255)CHARACTERSET utf8mb4COLLATE utf8mb4_general_ciNOTNULLDEFAULT''COMMENT'请求uri',`operation_method`varchar(255)CHARACTERSET utf8mb4COLLATE utf8mb4_general_ciNOTNULLDEFAULT''COMMENT'请求方法',`request_param`textCHARACTERSET utf8mb4COLLATE utf8mb4_general_ciNULLCOMMENT'请求参数',`response_param`longtextCHARACTERSET utf8mb4COLLATE utf8mb4_general_ciNULLCOMMENT'响应参数',`result`textCHARACTERSET utf8mb4COLLATE utf8mb4_general_ciNULLCOMMENT'操作结果',`create_time`datetime(0)NOTNULLCOMMENT'创建时间',`update_time`datetime(0)NOTNULLCOMMENT'修改时间',PRIMARYKEY(`id`)USINGBTREE,INDEX`user_id`(`user_id`)USINGBTREE,INDEX`operation_model`(`operation_model`)USINGBTREE,INDEX`ad_account`(`ad_account`)USINGBTREE,INDEX`id`(`id`)USINGBTREE)ENGINE=InnoDBAUTO_INCREMENT=1CHARACTERSET= utf8mb4COLLATE= utf8mb4_general_ciCOMMENT='操作日志表' ROW_FORMAT= Dynamic;SET FOREIGN_KEY_CHECKS=1;

2:操作日志实体类

@Data@EqualsAndHashCode(callSuper=false)@Accessors(chain=true)@TableName("xcy_operation_log")publicclassOperationLogimplementsSerializable{privatestaticfinallong serialVersionUID=1L;/**
     * 操作日志自增id
     */@TableId(value="id", type=IdType.AUTO)privateLong id;/**
     * 操作人id
     */privateLong userId;/**
     * 操作人AD账号
     */privateString adAccount;/**
     * 请求ip地址
     */privateString operationIp;/**
     * 操作模块
     */privateString operationModel;/**
     * 操作类型
     */privateString operationType;/**
     * 操作描述
     */privateString operationDesc;/**
     * 操作时间
     */privateLocalDateTime operationTime;/**
     * 请求uri
     */privateString operationUri;/**
     * 请求方法
     */privateString operationMethod;/**
     * 请求参数
     */privateString requestParam;/**
     * 响应参数
     */privateString responseParam;/**
     * 操作结果
     */privateString result;/**
     * 创建时间
     */privateLocalDateTime createTime;/**
     * 修改时间
     */privateLocalDateTime updateTime;}

3:操作日志服务类

publicinterfaceOperationLogServiceextendsIService<OperationLog>{}

4:操作日志实现类

@ServicepublicclassOperationLogServiceImplextendsServiceImpl<OperationLogMapper,OperationLog>implementsOperationLogService{}

5:mapper接口层

publicinterfaceOperationLogMapperextendsBaseMapper<OperationLog>{}

6:mapper.xml层

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPEmapperPUBLIC"-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mappernamespace="com.xc.contract.dao.mapper.OperationLogMapper"></mapper>

7:操作日志注解类

@Target(ElementType.METHOD)//注解放置的目标位置即方法级别@Retention(RetentionPolicy.RUNTIME)//注解在哪个阶段执行@Documentedpublic@interfaceOperLog{/**
     * 操作模块
     * @return
     */Stringmodule()default"";/**
     * 操作类型
     * @return
     */Stringtype()default"";/**
     * 操作说明
     * @return
     */Stringdesc()default"";}

8:操作日志切面处理类

@Slf4j@Aspect@ComponentpublicclassOperationLogAspect{@AutowiredprivateOperationLogService logService;/**
     * 设置操作日志切入点 记录操作日志 在注解的位置切入代码
     */@Pointcut("@annotation(OperLog)")publicvoidoperLogPoinCut(){}/**
     * 正常返回通知,拦截用户操作日志,连接点正常执行完成后执行, 如果连接点抛出异常,则不会执行
     * @param joinPoint 切入点
     * @param data 返回结果
     */@AfterReturning(value="operLogPoinCut()", returning="data")publicvoidsaveOperLog(JoinPoint joinPoint,Object data){SessionPayload sessionPayload=SessionPayload.current();// 获取RequestAttributesRequestAttributes requestAttributes=RequestContextHolder.getRequestAttributes();// 从获取RequestAttributes中获取HttpServletRequest的信息HttpServletRequest request=(HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);//操作日志OperationLog operationLog=newOperationLog();try{// 从切面织入点处通过反射机制获取织入点处的方法MethodSignature signature=(MethodSignature) joinPoint.getSignature();//获取切入点所在的方法Method method= signature.getMethod();OperLog annotation= method.getAnnotation(OperLog.class);if(annotation!=null){//操作模块
                operationLog.setOperationModel(annotation.module());//操作类型
                operationLog.setOperationType(annotation.type());//操作描述
                operationLog.setOperationDesc(annotation.desc());}//获取请求的类名String className= joinPoint.getTarget().getClass().getName();String methodName= method.getName();//请求的方法名
            methodName= className+"."+methodName;//获取请求参数Map<String,String> map=converMap(request.getParameterMap());String requestParam=JsonUtil.toJson(map);//操作方法
            operationLog.setOperationMethod(methodName);//请求参数
            operationLog.setRequestParam(requestParam);//响应参数
            operationLog.setResponseParam(JsonUtil.toJson(data));//操作员
            operationLog.setUserId(sessionPayload.getUserId());
            operationLog.setAdAccount(sessionPayload.getAccount());
            operationLog.setOperationTime(LocalDateTime.now());//请求uri
            operationLog.setOperationUri(request.getRequestURI());//请求ip
            operationLog.setOperationIp(IpAddressUtil.getIpAddr(request));//操作结果ReturnData returnData=(ReturnData) data;if(returnData!=null){
                operationLog.setResult(returnData.getMessage());}else{
                operationLog.setResult("");}//创建时间
            operationLog.setCreateTime(LocalDateTime.now());//修改时间
            operationLog.setUpdateTime(LocalDateTime.now());//保存日志
            logService.save(operationLog);}catch(Exception e){
            log.error(e.getMessage());}}/**
     * 转换request参数
     * @param paramMap
     * @return
     */publicMap<String,String>converMap(Map<String,String[]> paramMap){Map<String,String> rtnMap=newHashMap<String,String>();for(String key: paramMap.keySet()){
            rtnMap.put(key, paramMap.get(key)[0]);}return rtnMap;}}

9:RequestWrapper类(无需)
RequestWrapper继承了HttpServletRequestWrapper,初始化的时候读取Request的整个请求体。然后重载了getInputStream和getReader方法,这两个方法会从类变量body中读取内容

importjavax.servlet.ReadListener;importjavax.servlet.ServletInputStream;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletRequestWrapper;importjava.io.*;publicclassRequestWrapperextendsHttpServletRequestWrapper{privatefinalString body;publicRequestWrapper(HttpServletRequest request){super(request);StringBuilder stringBuilder=newStringBuilder();BufferedReader bufferedReader=null;InputStream inputStream=null;try{
            inputStream= request.getInputStream();if(inputStream!=null){
                bufferedReader=newBufferedReader(newInputStreamReader(inputStream));char[] charBuffer=newchar[128];int bytesRead=-1;while((bytesRead= bufferedReader.read(charBuffer))>0){
                    stringBuilder.append(charBuffer,0, bytesRead);}}else{
                stringBuilder.append("");}}catch(IOException ex){}finally{if(inputStream!=null){try{
                    inputStream.close();}catch(IOException e){
                    e.printStackTrace();}}if(bufferedReader!=null){try{
                    bufferedReader.close();}catch(IOException e){
                    e.printStackTrace();}}}
        body= stringBuilder.toString();}@OverridepublicServletInputStreamgetInputStream()throwsIOException{finalByteArrayInputStream byteArrayInputStream=newByteArrayInputStream(body.getBytes());ServletInputStream servletInputStream=newServletInputStream(){@OverridepublicbooleanisFinished(){returnfalse;}@OverridepublicbooleanisReady(){returnfalse;}@OverridepublicvoidsetReadListener(ReadListener readListener){}@Overridepublicintread()throwsIOException{return byteArrayInputStream.read();}};return servletInputStream;}@OverridepublicBufferedReadergetReader()throwsIOException{returnnewBufferedReader(newInputStreamReader(this.getInputStream()));}publicStringgetBody(){returnthis.body;}}

10:使用你定义的自定义注解
在这里插入图片描述
***在这里插入图片描述

  • 作者:李知夏
  • 原文链接:https://blog.csdn.net/lixuanhong/article/details/127248586
    更新时间:2022-11-01 11:59:39