切面编程(操作日志)

Posted jockming

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了切面编程(操作日志)相关的知识,希望对你有一定的参考价值。

自定义注解

import java.lang.annotation.*;
 
/**
 * @author wzm
 */
//注解会在class中存在,运行时可通过反射获取
@Retention(RetentionPolicy.RUNTIME)
//目标是方法
@Target({ElementType.METHOD, ElementType.PARAMETER})
//表示是否将注解信息添加在java文档中
@Documented
public @interface Log {
 
    //这个用户所做的是什么操作
    String value() default "未标注操作";
 
    /**
     * 1 2 3 4 / 增 删 改 查
     * 0 未设置
     * @return int
     */
    int type() default 0;
 
}
 

切面类

SysLogAspect.java

import com.thyc.fabric.annotation.Log;
import com.thyc.fabric.common.utils.IpUtil;
import com.thyc.fabric.entity.business.OperationLogger;
import com.thyc.fabric.service.business.OperationLoggerService;
import com.thyc.fabric.shiro.JWTUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
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.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
 
/**
 * @author wzm
 */
@Aspect
@Component
@Slf4j
public class SysLogAspect {
 
    @Autowired
    private OperationLoggerService operationLoggerService;
 
    public SysLogAspect() {
    }
 
    private static OperationLogger operationLogger = new OperationLogger();
 
    /**
     * 注解切点
     */
    @Pointcut("@annotation(com.thyc.fabric.annotation.Log)")
    public void pointCut() {
    }
 
    @Around("pointCut()")
    public Object around(ProceedingJoinPoint pjd) throws Throwable {
        Object object = null;
        try {
            // 开始执行时间
            long starTime = System.currentTimeMillis();
            object = pjd.proceed();
 
            // 获取request
            ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = null;
            if (servletRequestAttributes != null) {
                request = servletRequestAttributes.getRequest();
            }
            if (request != null) {
                // 通过token解析操作用户
                String token = (String) SecurityUtils.getSubject().getPrincipal();
                if (token != null) {
                    String username = JWTUtil.getUsername(token);
                    operationLogger.setCreator(username);
                }
                // 获取访问端(web android ios)
                operationLogger.setAccessType(request.getHeader("accessType"));
 
            }
            MethodSignature methodName1 = (MethodSignature) pjd.getSignature();
            Method method = methodName1.getMethod();
 
            //method方法上面的注解
            Annotation[] annotations = method.getAnnotations();
            boolean flag = false;
            for (Annotation annotation : annotations) {
                if (Log.class.equals(annotation.annotationType())) {
                    flag = true;
                }
            }
            if (flag) {
                // 类名
                String className = pjd.getTarget().getClass().getName();
                operationLogger.setLogClassName(className);
 
                // 操作描述
                String operation = method.getAnnotation(Log.class).value();
                operationLogger.setOperationContent(operation);
 
                // 操作类型
                Integer operationType = method.getAnnotation(Log.class).type();
                operationLogger.setOperationType(operationType);
 
                // ip地址
                String ip = IpUtil.getIp(request);
                operationLogger.setRemoteIpAddress(ip);
 
                // 城市信息
                String cityInfo = IpUtil.getCityInfo(ip);
                operationLogger.setCityInfo(cityInfo);
 
                // 设置方法名
                operationLogger.setLogMethodName(method.getName());
 
                Signature signature = pjd.getSignature();
                MethodSignature methodSignature = (MethodSignature) signature;
                String[] argNames = methodSignature.getParameterNames();
 
                // 如果有参数
                if (argNames.length > 0) {
                    // 参数名列表
                    List<String> paramNameList =
                            new ArrayList<>(Arrays.asList(methodSignature.getParameterNames()).subList(0, argNames.length));
 
                    // 参数类型
                    List<String> paramTypeList = new ArrayList<>();
                    Parameter[] parameters = method.getParameters();
                    for (Parameter parameter : parameters) {
                        String paramType = parameter.getType().getName();
                        paramTypeList.add(paramType);
                    }
 
                    // 参数的值
                    List<Object> paramValueList = new ArrayList<>();
                    Object[] params = pjd.getArgs();
                    Collections.addAll(paramValueList, params);
 
                    operationLogger.setParamsTypeList(paramTypeList.toString());
                    operationLogger.setParamsNameList(paramNameList.toString());
                    operationLogger.setParamsValueList(paramValueList.toString());
                } else {
                    // 没有参数的话就都设置为none
                    operationLogger.setParamsTypeList("none");
                    operationLogger.setParamsNameList("none");
                    operationLogger.setParamsValueList("none");
                }
            }
            // 设置异常信息
            operationLogger.setExceptionInfo(null);
 
            // 执行时间 = 执行结束时间 - 开始时间 (单位:毫秒)
            long execTime = System.currentTimeMillis() - starTime;
            operationLogger.setExecTime(String.valueOf(execTime));
        } catch (Exception e) {
            operationLogger.setExceptionInfo(e.getMessage());
            operationLoggerService.addLogger(operationLogger);
            throw e;
        }
        log.info("[" + operationLogger.getRemoteIpAddress() + "][" + operationLogger.getOperationContent() + "]["
                + operationLogger.getLogClassName() + " - " + operationLogger.getLogMethodName() + "]" + " - "
                + (StringUtils.isNotBlank(operationLogger.getExecTime()) ? operationLogger.getExecTime() : "0") + "ms");
 
        // 录入数据库
        operationLoggerService.addLogger(operationLogger);
        // 执行方法,获取返回参数
        return object;
    }
}

 

service层

OperationLoggerService.java
import com.baomidou.mybatisplus.service.IService;
import com.thyc.fabric.common.exception.BaseException;
import com.thyc.fabric.entity.business.OperationLogger;
 
/**
* 操作日志记录 Service层
*
* @author wzm
* @version 1.0
*/
public interface OperationLoggerService extends IService<OperationLogger> {
 
    /**
     * 添加日志(配置了aop切面)
     *
     * @param operationLogger log
     * @throws BaseException e
     */
    void addLogger(OperationLogger operationLogger) throws BaseException;
}
 
OperationLoggerServiceImpl.java
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.thyc.fabric.common.exception.BaseException;
import com.thyc.fabric.dao.business.OperationLoggerMapper;
import com.thyc.fabric.entity.business.OperationLogger;
import com.thyc.fabric.service.business.OperationLoggerService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
 
import javax.annotation.Resource;
import java.util.Date;
 
/**
 * 操作日志记录 ServiceImpl层
 *
 * @author wzm
 * @version 1.0
 */
@Service
@Slf4j
public class OperationLoggerServiceImpl extends ServiceImpl<OperationLoggerMapper, OperationLogger> implements OperationLoggerService {
 
    @Resource
    private OperationLoggerMapper operationLoggerMapper;
 
    /**
     * 添加日志(配置了aop切面)
     *
     * @param operationLogger 日志对象
     */
    @Transactional(rollbackFor = {Exception.class})
    @Override
    public void addLogger(OperationLogger operationLogger) throws BaseException {
        //尚未添加logger实体和dao
        try {
            operationLogger.setCreateTime(new Date());
            int result = operationLoggerMapper.insert(operationLogger);
            //操作成功
            if (result < 1) {
                log.warn("新增日志失败");
            }
        } catch (Exception e) {
            //手动回滚
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            throw new BaseException(e.getMessage());
        }
    }
 
}
 

Dao层

OperationLoggerMapper.java
import com.baomidou.mybatisplus.mapper.BaseMapper;
import com.thyc.fabric.entity.business.OperationLogger;
import com.thyc.fabric.vo.business.LogVO;
 
import java.util.List;
 
/**
* @author wzm
*/
public interface OperationLoggerMapper extends BaseMapper<OperationLogger> {
 
    /**
     * 查询操作日志
     *
     * @param type 日志类型
     * @param star 开始时间
     * @param end  截止时间
     * @return list vo
     * @author wzh
     * @date 2019/9/29 0029 17:53
     */
    List<LogVO> queryLogList(Integer type, String star, String end);
}
 
OperationLoggerMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.thyc.fabric.dao.business.OperationLoggerMapper">
 
    <resultMap id="OperationLoggerMap" type="com.thyc.fabric.entity.business.OperationLogger">
        <result column="id" property="id" />
        <result column="operation_content" property="operationContent" />
        <result column="operation_type" property="operationType" />
        <result column="log_class_name" property="logClassName" />
        <result column="log_method_name" property="logMethodName" />
        <result column="params_type_list" property="paramsTypeList" />
        <result column="params_name_list" property="paramsNameList" />
        <result column="params_value_list" property="paramsValueList" />
        <result column="exception_info" property="exceptionInfo" />
        <result column="remote_ip_address" property="remoteIpAddress" />
        <result column="access_type" property="accessType" />
        <result column="exec_time" property="execTime" />
        <result column="city_info" property="cityInfo" />
        <result column="creator" property="creator" />
        <result column="modifier" property="modifier" />
        <result column="create_time" property="createTime" />
        <result column="modify_time" property="modifyTime" />
        <result column="valid" property="valid" />
    </resultMap>
 
    <select id="queryLogList" resultType="com.thyc.fabric.vo.business.LogVO">
        select
        log.id as logId,
        u.nick_name as nickName,
        log.operation_type as typeId,
        log.operation_content as `desc`,
        log.remote_ip_address as ip,
        log.log_class_name as className,
        log.log_method_name as methodName,
        log.exception_info as exceptionInfo,
        log.create_time as createTime
        from
        t_operation_logger as log
        inner join t_sys_user as u
        on u.id = log.user_id
        where  log.valid = 1 AND u.valid = 1
        <if test="type != null">
            AND log.operation_type = #{type}
        </if>
        <if test="type == null">
            AND log.operation_type <![CDATA[!=]]> 4
        </if>
        <if test="star != null and end != null">
            AND DATE_FORMAT(log.create_time,‘%Y-%m-%d‘) BETWEEN STR_TO_DATE(#{star},‘%Y-%m-%d‘) AND STR_TO_DATE(#{end},‘%Y-%m-%d‘)
        </if>
        order by log.create_time desc
    </select>
</mapper>
 

日志实体类

实体类:
import com.baomidou.mybatisplus.annotations.TableField;
import com.thyc.fabric.entity.BaseEntity;
import com.baomidou.mybatisplus.annotations.TableName;
import lombok.Data;
import java.io.Serializable;
 
/**
 *
 * @author wzm
 */
@TableName("t_operation_logger")
@Data
public class OperationLogger extends BaseEntity<OperationLogger>{
 
    private static final long serialVersionUID = 1L;
 
    /**
     * 操作内容
     */
    @TableField("operation_content")
    private String operationContent;
    /**
     * 操作类型( 0-未设置, 1-新增, 2-删除, 3-修改, 4-查询, 5-登录, 6-登出, 7-转账)
     */
    @TableField("operation_type")
    private Integer operationType;
    /**
     * 类名
     */
    @TableField("log_class_name")
    private String logClassName;
    /**
     * 方法名
     */
    @TableField("log_method_name")
    private String logMethodName;
    /**
     * 参数类型列表
     */
    @TableField("params_type_list")
    private String paramsTypeList;
    /**
     * 参数名称列表
     */
    @TableField("params_name_list")
    private String paramsNameList;
    /**
     * 参数值列表
     */
    @TableField("params_value_list")
    private String paramsValueList;
    /**
     * 异常信息
     */
    @TableField("exception_info")
    private String exceptionInfo;
    /**
     * 远程IP地址
     */
    @TableField("remote_ip_address")
    private String remoteIpAddress;
    /**
     * 访问类型(0-web, 1-android, 2-ios)
     */
    @TableField("access_type")
    private String accessType;
    /**
     * 执行时间
     */
    @TableField("exec_time")
    private String execTime;
    /**
     * 城市信息
     */
    @TableField("city_info")
    private String cityInfo;
 
    @Override
    protected Serializable pkVal() {
        return this.id;
    }
 
}

 

控制层

注解加到方法是以记录该接口的行为。
@PostMapping("oss_file_uploader")
@Log(value = "oss文件上传", type = 1)
@ApiOperation(value = "oss文件上传", notes = "文件上传接口", produces = "application/json")
public ApiResult ossUploadFile(@RequestParam(value = "file") MultipartFile file) throws BusinessException {
    return commonService.ossUploadFile(file);
}

 

以上是关于切面编程(操作日志)的主要内容,如果未能解决你的问题,请参考以下文章

springboot-aop面向切面编程

springboot通过切面编程实现系统请求操作日志记录

java怎么运用切面编程生成日志

Spring Boot @Aspect 切面编程实现访问请求日志记录

springboot配置aop切面日志打印

Spring AOP(面向切面编程)