spring aop收集日志的方法

第一步:

自定义一个aop注解,实现对目标方法的拦截

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Created by wrr on 2018/3/27.
 * 注解
 */
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)  
public @interface  SystemUserLogAnnotation {

   String description() default " ";
   
}

第二步:

写一个实体类,在程序运行中自动注入到实体类中,利用实现类,实现日志信息入库操作。

package cn.ac.bcc.ebap.modules.sys.log.entity;
import java.io.Serializable;

import cn.ac.bcc.ebap.common.utils.StringUtils;
import com.fasterxml.jackson.annotation.JsonFormat;

import javax.persistence.*;
import java.util.Date;
import java.util.Map;
import cn.ac.bcc.ebap.common.persistence.BaseEntity;
import org.apache.commons.collections.map.HashedMap;

/**
 * Created by wrr on 2018/3/27.
 */
@Table(name = "sys_user_log")
public class SystemUserLog extends BaseEntity<SystemUserLog>{
    @Column(name = "log_id")
    private String logId;           //日志主键
    @Column(name = "log_type")
    private String type;            //日志类型
    @Column(name = "log_title")
    private String title;           //日志标题
    @Column(name = "remote_addr")
    private String remoteAddr;          //请求地址
    @Column(name = "request_uri")
    private String requestUri;          //URI
    @Column(name = "method")
    private String method;          //请求方式
    @Column(name = "params")
    private String params;          //提交参数
    @Column(name = "exception")
    private String exception;           //异常
    @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    @Column(name = "operate_date")
    private Date operateDate;           //开始时间
    @Column(name = "timeout")
    private String timeout;         //结束时间
    @Column(name = "user_id")
    private String userId;          //用户ID
    public String getLogId() {
        return logId;
    }
    public void setLogId(String logId) {
        this.logId= logId;
    }


    public String getType() {
        return type;
    }
    public void setType(String type) {
        this.type = type;
    }


    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }


    public String getRemoteAddr() {
        return remoteAddr;
    }
    public void setRemoteAddr(String remoteAddr) {
        this.remoteAddr = remoteAddr;
    }


    public String getRequestUri() {
        return requestUri;
    }
    public void setRequestUri(String requestUri) {
        this.requestUri = requestUri;
    }


    public String getMethod() {
        return method;
    }
    public void setMethod(String method) {
        this.method = method;
    }


    public String getParams() {
        return params;
    }
    public void setParams(String params) {
        this.params = params;
    }
    /**
     * 设置请求参数
     * @param paramMap
     */
    public void setMapToParams(Map<String, String[]> paramMap) {
        if (paramMap == null){
            return;
        }
        StringBuilder params = new StringBuilder();
        for (Map.Entry<String, String[]> param : ((Map<String, String[]>)paramMap).entrySet()){
            params.append(("".equals(params.toString()) ? "" : "&") + param.getKey() + "=");
            String paramValue = (param.getValue() != null && param.getValue().length > 0 ? param.getValue()[0] : "");
            params.append(StringUtils.abbr(StringUtils.endsWithIgnoreCase(param.getKey(), "password") ? "" : paramValue, 100));
            //params.append(param.getKey(), paramValue, 100);
        }
        this.params = params.toString();
    }


    public String getException() {
        return exception;
    }
    public void setException(String exception) {
        this.exception = exception;
    }


    public Date getOperateDate() {
        return operateDate;
    }
    public void setOperateDate(Date operateDate) {
        this.operateDate = operateDate;
    }


    public String getTimeout() {
        return timeout;
    }
    public void setTimeout(String timeout) {
        this.timeout = timeout;
    }


    public String getUserId() {
        return userId;
    }
    public void setUserId(String userId) {
        this.userId = userId;
    }
    @Override
    public String toString() {
        return "SystemLog{" +
                "logId='" + logId + '\'' +
                ", type='" + type + '\'' +
                ", title='" + title + '\'' +
                ", remoteAddr='" + remoteAddr + '\'' +
                ", requestUri='" + requestUri + '\'' +
                ", method='" + method + '\'' +
                ", params='" + params + '\'' +
                ", exception='" + exception + '\'' +
                ", operateDate=" + operateDate +
                ", timeout='" + timeout + '\'' +
                ", userId='" + userId + '\'' +
                '}';
    }
}

第三步:

定义一个切面类,用于拦截记录用户的操作,实现日志的收集。

package cn.ac.bcc.ebap.modules.sys.log.aspect;

/**
 * Created by wrr on 2018/3/27.
 */
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import cn.ac.bcc.ebap.common.utils.DateUtils;
import cn.ac.bcc.ebap.common.web.Servlets;
import cn.ac.bcc.ebap.modules.sys.entity.User;
import cn.ac.bcc.ebap.modules.sys.log.annotation.SystemControllerLog;
import cn.ac.bcc.ebap.modules.sys.log.annotation.SystemUserLogAnnotation;
import cn.ac.bcc.ebap.modules.sys.log.entity.SystemUserLog;
import cn.ac.bcc.ebap.modules.sys.log.service.SystemUserLogService;
import cn.ac.bcc.ebap.modules.sys.utils.UserUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.NamedThreadLocal;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class SystemUserLogAspect {
    private static final Logger logger = LoggerFactory.getLogger(SystemUserLogAspect.class);
    private static final ThreadLocal<Date> beginTimeThreadLocal =
            new NamedThreadLocal<Date>("ThreadLocal beginTime");
    private static final ThreadLocal<SystemUserLog> logThreadLocal =
            new NamedThreadLocal<SystemUserLog>("ThreadLocal log");
    private static final ThreadLocal<User> currentUser=new NamedThreadLocal<>("ThreadLocal user");
    private SystemUserLog systemUserLog=new SystemUserLog();
//    @Autowired(required=false)
//    private HttpServletRequest request;
    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    @Autowired
    private SystemUserLogService systemUserLogService;
    /**
     * 切入点 注解拦截
     */
    @Pointcut(
            //"execution(* cn.ac.bcc.ebap.modules.sys.web.*.*(..)) ||"+
            "execution(* cn.ac.bcc.ebap.modules.sys.service.*.*(..))"
            //"execution(* cn.ac.bcc.ebap.common.exception.*.*(..))"
    )
    public void aspectPointCut(){
    }

    /**
     * @description
     * @author wrr on 2018/3/27 9:30
     * 前置通知 用于拦截记录用户的操作的开始时间
     * @param joinPoint 切点
     * @throws InterruptedException
     */
    @Before("aspectPointCut()")
    public void doBefore(JoinPoint joinPoint) throws InterruptedException{
        Date beginTime=new Date();
        beginTimeThreadLocal.set(beginTime);//线程绑定变量(该数据只有当前请求的线程可见)
        if (logger.isDebugEnabled()){      //这里日志级别为debug
            logger.debug("开始计时: {}  URI: {}", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS")
                    .format(beginTime), Servlets.getRequest().getRequestURI());
        }

        //读取session中的用户
        User user = (User) UserUtils.getSession().getAttribute("getUser");
        currentUser.set(user);
    }
    /**
     * @description
     * @author wrr on 2018/3/27 9:30
     * 后置通知 用于记录用户的操作
     * @param joinPoint
     */
    @SuppressWarnings("unchecked")
    @After("aspectPointCut()")
    public void doAfter(JoinPoint joinPoint){
            String type="info";                       //日志类型(info:入库,error:错误)
            systemUserLog.setType(type);
            systemUserLog.setException("");
        }

    /**
     * @description
     * @author wrr on 2018/3/27 10:18
     *  异常通知 记录操作报错日志
     * @param joinPoint
     * @param e
     */
    @SuppressWarnings("unchecked")
    @AfterThrowing(pointcut = "aspectPointCut()", throwing = "e")
    public  void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
            String type="error";                       //日志类型(info:入库,error:错误)
            systemUserLog.setType(type);
            systemUserLog.setException(e.toString());
        }
    /**
     * @description
     * @author wrr on 2018/3/27 18:09
     * 最终通知 用于拦截记录用户的操作
     * @param joinPoint
     */
    @SuppressWarnings("unchecked")
    @AfterReturning("aspectPointCut()")
    public void doAfterReturning(JoinPoint joinPoint) {
        //System.out.println(joinPoint.getSignature().getName());
        User user = currentUser.get();
        if(user !=null && user.getId() != null){
            String title="";
            try {
                title=getAnnotationMthodDescription2(joinPoint);
            } catch (Exception e) {
                e.printStackTrace();
            }
            if(title==null||title.equals("")){
                return;
            }
            if(Servlets.getRequest()!=null&&!Servlets.getRequest().equals("")) {
                HttpServletRequest request = Servlets.getRequest();
                String remoteAddr = request.getRemoteAddr();//请求的IP
                String requestUri = request.getRequestURI();//请求的Uri
                String method = request.getMethod();        //请求的方法类型(post/get)
                Map<String, String[]> params = request.getParameterMap(); //请求提交的参数
                systemUserLog.setRemoteAddr(remoteAddr);
                systemUserLog.setRequestUri(requestUri);
                systemUserLog.setMethod(method);
                params= request.getParameterMap();
                systemUserLog.setMapToParams(params);
            }
            else{
                systemUserLog.setRemoteAddr("");
                systemUserLog.setRequestUri("");
                systemUserLog.setMethod("");
                systemUserLog.setMapToParams(null);
            }
            long beginTime = beginTimeThreadLocal.get().getTime();//得到线程绑定的局部变量(开始时间)
            long endTime = System.currentTimeMillis();  //2、结束时间
            if (logger.isDebugEnabled()) {
                logger.debug("计时结束:{}  URI: {}  耗时: {}   最大内存: {}m  已分配内存: {}m  已分配内存中的剩余空间: {}m  最大可用内存: {}m",
                        new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(endTime),
                        Servlets.getRequest().getRequestURI(),
                        endTime - beginTime,
                        Runtime.getRuntime().maxMemory() / 1024 / 1024,
                        Runtime.getRuntime().totalMemory() / 1024 / 1024,
                        Runtime.getRuntime().freeMemory() / 1024 / 1024,
                        (Runtime.getRuntime().maxMemory() - Runtime.getRuntime().totalMemory() + Runtime.getRuntime().freeMemory()) / 1024 / 1024);
            }
            systemUserLog.setTitle(title+"|"+joinPoint.getSignature().getName());
            //systemLog.setTitle(title);
            systemUserLog.setLogId(UUID.randomUUID().toString());
            systemUserLog.setUserId(user.getUserId());
            Date operateDate = beginTimeThreadLocal.get();
            systemUserLog.setOperateDate(operateDate);
            //每次访问花费的时长
            systemUserLog.setTimeout(DateUtils.formatDateTime(endTime - beginTime));
            //1.直接执行保存操作
            //this.logService.createSystemLog(log);

            //2.优化:异步保存日志
            //new SaveLogThread(systemLog, systemLogService).start();

            //3.再优化:通过线程池来执行日志保存
            threadPoolTaskExecutor.execute(new SaveLogThread(systemUserLog, systemUserLogService));
        }}
    /**
     * 获取注解中对方法的描述信息 用于service层注解
     * @return discription
     */
    public static String getAnnotationMthodDescription2(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        SystemUserLogAnnotation systemLogAnnotation = method
                .getAnnotation(SystemUserLogAnnotation.class);
        if(systemLogAnnotation==null||systemLogAnnotation.equals("")){
            return "";
        }
        String discription = systemLogAnnotation.description();
        if(discription==null||discription.equals("")){
            return "";
        }
        return discription;
    }
    
    /**
     * 保存日志线程
     */
    private static class SaveLogThread extends Thread{
        private SystemUserLog systemUserLog;
        private SystemUserLogService systemUserLogService;

        public SaveLogThread(SystemUserLog systemUserLog, SystemUserLogService systemUserLogService) {
            this.systemUserLog = systemUserLog;
            this.systemUserLogService = systemUserLogService;
        }

        @Override
        public void run() {
            systemUserLogService.insert(systemUserLog);
        }
    }}

第四步:

在想要拦截的方法上,加上注解。一般建议加在serivice层。

@Transactional(readOnly = false)
@SystemUserLogAnnotation(description = "UserService")
public void deleteUser(User user) {
    user.preUpdate();
    user.setDelFlag(CommonEntity.DEL_FLAG_DELETE);
    userDao.updateByPrimaryKeySelective(user);
}

总体步骤如上,界面效果如下

spring aop收集日志的方法