第一步:
自定义一个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); }
总体步骤如上,界面效果如下