java利用切面(aspect)记录日志实现性能跟踪以及用户行为分析
记录日志的目的是多种多样的,这里主要以性能跟踪和用户行为分析为目的讨论如何记录日志。
1. 概述
一个有一定规模的应用系统,都会存在大量的功能,这些功能通过菜单,链接,按钮和页面进行展示,在系统建设初期,为了尽早将系统投入生产,对于系统性能优化方面可能考虑不够。当然,系统初期的用户量和数据量都相对较小,系统性能也不会是有明显的问题。但是,随着系统的持续运行,用户量和数据量的不断增加,性能性能的优化就变得越来越重要了。
一个请求的响应速度直接影响到用户的使用体验,只有用户系统的响应时间在正常的接受范围内时,用户才会感觉到系统是正常的,一旦超过这个时间,就会感觉系统很慢。但是系统慢,只是一个大概的描述,是个别的特定操作响应慢,还是所有的操作都慢,是个别用户觉得慢,还是所有用户都觉得慢,这就不容易描述清楚。所以,系统最好有自身处理时间的一个记录和统计,包括各种操作,各种条件下的操作,包括不同的用户,不同的时间,不同的输入条件等。
另一方面,系统在不断的增加功能和完善过程中,增加了各种各样的功能,这些功能有哪些使用得多,哪些使用得少,都是什么用户在使用,下一步的发展方向在什么地方。最好的方法就是系统能够提供针对性的统计,这是最直接的,也是最客观的,不受主观判断的影响。通过日志记录,可以分析出系统提供的功能中,哪些功能使用最频繁,哪些功能使用最少,这样有利于在后续的版本升级中,把重心放在用户关心的功能上。用户使用少的功能,可以进一步分析用户使用少的原因,比如是用户不了解,还是用户的确不需要等。
2. 主要代码
总体技术思路,采用切面方式拦截请求,记录每个请求的信息,包括请求用户,请求时间,请求参数以及执行结果等,有了这些请求数据,就可以进行统计分析,得出系统是否存在性能瓶颈。也可以统计出什么用户对什么功能的使用最频繁,什么功能几乎没有人使用。
2.1 注解代码
切面采用根据注解判断是否需要拦截方法的调用,所以首先需要创建一个自定义注解。
package com.ruoyi.common.annotation;import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.enums.OperatorType;
import org.springframework.web.bind.annotation.ResponseBody;import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;// 事件埋点
@Target({ElementType.PARAMETER, ElementType.METHOD})//作用在参数和方法上
@Retention(RetentionPolicy.RUNTIME)//运行时注解
@Documented//表明这个注解应该被 javadoc工具记录
// @ResponseBody//响应时转JSON格式
public @interface EventTrack {/*** 模块*/public String title() default "";/*** 功能*/public BusinessType businessType() default BusinessType.OTHER;/*** 操作人类别*/public OperatorType operatorType() default OperatorType.MANAGE;/*** 是否保存请求的参数*/public boolean isSaveRequestData() default true;/*** 是否保存响应的参数*/public boolean isSaveResponseData() default true;/*** 业务描述* @return*/String description() default "";/*** 是否打日志 默认打*/boolean isLog() default true;}
在需要记录日志的方法上,添加该注解,切面会根据该注解进行拦截并日志记录。 在注解中,可以添加相关的参数,这些参数可以记录到日志中,用于区分不同的方法。
2.2 日志类代码
package com.ruoyi.system.domain;import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity;/*** 事件跟踪对象 event_track_log* * @author * @date 2022-06-08*/
public class EventTrackLog extends BaseEntity
{private static final long serialVersionUID = 1L;/** 跟踪编号 */private Long traceId;/** 应用名称 */@Excel(name = "应用名称")private String systemName;/** 模块名称 */@Excel(name = "模块名称")private String moduleTitle;/** 业务类型 */@Excel(name = "业务类型")private String businessType;/** 请求方式 */@Excel(name = "请求方式")private String requestMethod;/** 操作人员 */@Excel(name = "操作人员")private String userName;/** 部门名称 */@Excel(name = "部门名称")private String deptName;/** 用户类型 */@Excel(name = "用户类型")private String userType;/** 商户编号 */@Excel(name = "商户编号")private String merchantNo;/** 操作状态 */@Excel(name = "操作状态")private Long status;/** 错误消息 */@Excel(name = "错误消息")private String errorMsg;/** 开始时间 */@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")@Excel(name = "开始时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")private Date startTime;/** 结束时间 */@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")@Excel(name = "结束时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")private Date endTime;/** 方法名 */@Excel(name = "方法名")private String method;/** 描述信息 */@Excel(name = "描述信息")private String description;/** 客户端ip地址 */@Excel(name = "客户端ip地址")private String ipAddress;/** 户端主机名 */@Excel(name = "户端主机名")private String hostName;/** 请求参数 */@Excel(name = "请求参数")private String reqParams;/** 访问url */@Excel(name = "访问url")private String url;/** 异常信息 */@Excel(name = "异常信息")private String exceptMsg;/** 返回结果 */@Excel(name = "返回结果")private String result;/** 时间长度 */@Excel(name = "时间长度")private Long timeSpend;/** 调用次数 */@Excel(name = "调用次数")private Long number;/** 访问渠道 */@Excel(name = "访问渠道")private String channel;public void setTraceId(Long traceId) {this.traceId = traceId;}public Long getTraceId() {return traceId;}public void setSystemName(String systemName) {this.systemName = systemName;}public String getSystemName() {return systemName;}public void setModuleTitle(String moduleTitle) {this.moduleTitle = moduleTitle;}public String getModuleTitle() {return moduleTitle;}public void setBusinessType(String businessType) {this.businessType = businessType;}public String getBusinessType() {return businessType;}public void setRequestMethod(String requestMethod) {this.requestMethod = requestMethod;}public String getRequestMethod() {return requestMethod;}public void setUserName(String userName) {this.userName = userName;}public String getUserName() {return userName;}public void setDeptName(String deptName) {this.deptName = deptName;}public String getDeptName() {return deptName;}public void setUserType(String userType) {this.userType = userType;}public String getUserType() {return userType;}public void setMerchantNo(String merchantNo) {this.merchantNo = merchantNo;}public String getMerchantNo() {return merchantNo;}public void setStatus(Long status) {this.status = status;}public Long getStatus() {return status;}public void setErrorMsg(String errorMsg) {this.errorMsg = errorMsg;}public String getErrorMsg() {return errorMsg;}public void setStartTime(Date startTime) {this.startTime = startTime;}public Date getStartTime() {return startTime;}public void setEndTime(Date endTime) {this.endTime = endTime;}public Date getEndTime() {return endTime;}public void setMethod(String method) {this.method = method;}public String getMethod() {return method;}public void setDescription(String description) {this.description = description;}public String getDescription() {return description;}public void setIpAddress(String ipAddress) {this.ipAddress = ipAddress;}public String getIpAddress() {return ipAddress;}public void setHostName(String hostName) {this.hostName = hostName;}public String getHostName() {return hostName;}public String getReqParams() {return reqParams;}public void setReqParams(String reqParams) {this.reqParams = reqParams;}public void setUrl(String url){this.url = url;}public String getUrl() {return url;}public void setExceptMsg(String exceptMsg) {this.exceptMsg = exceptMsg;}public String getExceptMsg() {return exceptMsg;}public void setResult(String result) {this.result = result;}public String getResult() {return result;}public void setTimeSpend(Long timeSpend) {this.timeSpend = timeSpend;}public Long getTimeSpend() {return timeSpend;}public void setNumber(Long number) {this.number = number;}public Long getNumber() {return number;}public void setChannel(String channel) {this.channel = channel;}public String getChannel() {return channel;}@Overridepublic String toString() {return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE).append("traceId", getTraceId()).append("systemName", getSystemName()).append("moduleTitle", getModuleTitle()).append("businessType", getBusinessType()).append("requestMethod", getRequestMethod()).append("userName", getUserName()).append("deptName", getDeptName()).append("userType", getUserType()).append("merchantNo", getMerchantNo()).append("status", getStatus()).append("errorMsg", getErrorMsg()).append("startTime", getStartTime()).append("endTime", getEndTime()).append("method", getMethod()).append("description", getDescription()).append("ipAddress", getIpAddress()).append("hostName", getHostName()).append("reqParams", getReqParams()).append("url", getUrl()).append("exceptMsg", getExceptMsg()).append("result", getResult()).append("timeSpend", getTimeSpend()).append("number", getNumber()).append("channel", getChannel()).toString();}
}
事件日志信息记录类,与对应的数据库表脚本一一对应。事件日志记录的信息,可以根据业务需要进行扩展,比如有的业务场景中需要用户编号,客户编号或者用户手机号等进行关联标识。
CREATE TABLE `event_track_log` (`trace_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '跟踪编号',`system_name` varchar(32) DEFAULT NULL COMMENT '应用名称',`module_title` varchar(32) DEFAULT NULL COMMENT '模块名称',`business_type` varchar(20) DEFAULT NULL COMMENT '业务类型',`request_method` varchar(10) DEFAULT NULL COMMENT '请求方式',`user_name` varchar(64) DEFAULT NULL COMMENT '操作人员',`dept_name` varchar(64) DEFAULT NULL COMMENT '部门名称',`user_type` varchar(20) DEFAULT NULL COMMENT '用户类型',`merchant_no` varchar(255) DEFAULT NULL COMMENT '商户编号',`status` int(11) DEFAULT NULL COMMENT '操作状态',`error_msg` varchar(255) DEFAULT NULL COMMENT '错误消息',`start_time` datetime DEFAULT NULL COMMENT '开始时间',`end_time` datetime DEFAULT NULL COMMENT '结束时间',`method` varchar(64) DEFAULT NULL COMMENT '方法名',`description` varchar(255) DEFAULT NULL COMMENT '描述信息',`ip_address` varchar(32) DEFAULT NULL COMMENT '客户端ip地址',`host_name` varchar(32) DEFAULT NULL COMMENT '户端主机名',`req_params` varchar(255) DEFAULT NULL COMMENT '请求参数',`url` varchar(255) DEFAULT NULL COMMENT '访问url',`except_msg` varchar(255) DEFAULT NULL COMMENT '异常信息',`result` varchar(255) DEFAULT NULL COMMENT '返回结果',`time_spend` bigint(20) DEFAULT NULL COMMENT '时间长度',`number` int(11) DEFAULT NULL COMMENT '调用次数',`channel` varchar(20) DEFAULT NULL COMMENT '访问渠道',`create_by` varchar(32) DEFAULT NULL COMMENT '创建者',`create_time` datetime DEFAULT NULL COMMENT '创建时间',PRIMARY KEY (`trace_id`)
) ENGINE=InnoDB AUTO_INCREMENT=481 DEFAULT CHARSET=utf8 COMMENT='事件跟踪';
2.3 切面类代码
package com.ruoyi.framework.aspectj;import com.alibaba.fastjson.JSON;
import com.ruoyi.common.annotation.EventTrack;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.ShiroUtils;
import com.ruoyi.system.domain.EventTrackLog;
import com.ruoyi.system.service.IEventTrackLogService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
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.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.util.Arrays;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;/*** author:* 埋点切面*/
@Aspect
@Component
public class EventTrackAspect {//日志工厂获取日志对象static Logger logger = LoggerFactory.getLogger(EventTrackAspect.class);/** 排除敏感属性字段 */public static final String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" };@Autowiredprivate IEventTrackLogService eventTrackLogService;//startTime存放开始时间private ThreadLocal<Map<String, Long >> startTime = new ThreadLocal<>();//eventTrackLog日志访问对象private ThreadLocal<EventTrackLog> eventTrackLog = new ThreadLocal<>();//Controller层切点@Pointcut("@annotation(com.ruoyi.common.annotation.EventTrack)")public void controllerAspectse() {}//前置通知 用于拦截Controller层记录用户的操作@Before("controllerAspectse()")public void before(JoinPoint pjp) {//方法调用之前初始化EventTrackLog eventTrackLog = this.eventTrackLog.get();eventTrackLog = new EventTrackLog();Map<String, Long> map = new HashMap<>();map.put("startTime",System.currentTimeMillis());this.startTime.set(map);logger.info("==============前置通知开始:记录用户的操作==============");String currentTime = DateUtils.getTime();logger.info("请求开始时间:" + currentTime);eventTrackLog.setStartTime(new Date());String resultString = "";// 是否打日志 默认打boolean isLog = true;try {MethodSignature signature = (MethodSignature) pjp.getSignature();EventTrack eventTrack = signature.getMethod().getAnnotation(EventTrack.class);//是否开启日志打印isLog = eventTrack.isLog();if(isLog){//开始打印日志HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();HttpSession session = request.getSession();String api = pjp.getTarget().getClass().getName() + "." + pjp.getSignature().getName();logger.info("请求API:" + api);eventTrackLog.setMethod(api);String methodDescription = getControllerMethodDescription(pjp);logger.info("方法描述:" + methodDescription);eventTrackLog.setDescription(methodDescription);String ipAddress = InetAddress.getLocalHost().toString().substring(InetAddress.getLocalHost().toString().lastIndexOf("/") + 1);logger.info("请求ip:"+ ipAddress);eventTrackLog.setIpAddress(ipAddress);String hostName = InetAddress.getLocalHost().getHostName();logger.info("机器名:" + hostName);eventTrackLog.setHostName(hostName);Enumeration<?> enu = request.getParameterNames();String params = "{";while (enu.hasMoreElements()) {String paraName = (String) enu.nextElement();List<String> list = Arrays.asList(EXCLUDE_PROPERTIES);if(list.contains(paraName)) {continue;}params += "\"" + paraName + "\":\"" + request.getParameter(paraName) + "\",";}String methodParams = params + "}";String substring = methodParams.substring(0, methodParams.length() - 2);substring = substring + "}";logger.info("方法参数:" + substring);eventTrackLog.setReqParams(substring);StringBuffer url = request.getRequestURL();logger.info("URL:" + url);eventTrackLog.setUrl(String.valueOf(url));}} catch (Exception e) {StackTraceElement stackTraceElement2 = e.getStackTrace()[2];String reason = "异常:【"+"类名:"+stackTraceElement2.getClassName()+";"+"文件:"+stackTraceElement2.getFileName()+";"+"行:"+stackTraceElement2.getLineNumber()+";"+"方法:"+stackTraceElement2.getMethodName() + "】";//记录本地异常日志logger.error("==============前置通知异常:记录访问异常信息==============");String message = e.getMessage() + "|" + reason;logger.error("异常信息:",message);eventTrackLog.setErrorMsg(message);eventTrackLog.setResult("请求发生异常,异常信息:" + message);}finally {this.eventTrackLog.set(eventTrackLog);}}@Around("controllerAspectse()")public Object around(ProceedingJoinPoint pjp) throws Throwable {// 获取方法签名MethodSignature signature = (MethodSignature) pjp.getSignature();EventTrack eventTrack = signature.getMethod().getAnnotation(EventTrack.class);//是否开启日志打印Boolean isLog = eventTrack.isLog();EventTrackLog eventTrackLog = new EventTrackLog();Long startTime = System.currentTimeMillis();eventTrackLog.setStartTime(new Date());try {//开始打印日志HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();HttpSession session = request.getSession();String api = pjp.getTarget().getClass().getName() + "." + pjp.getSignature().getName();logger.info("请求API:" + api);eventTrackLog.setMethod(api);String methodDescription = getControllerMethodDescription(pjp);logger.info("方法描述:" + methodDescription);eventTrackLog.setDescription(methodDescription);String ipAddress = InetAddress.getLocalHost().toString().substring(InetAddress.getLocalHost().toString().lastIndexOf("/") + 1);logger.info("请求ip:"+ ipAddress);eventTrackLog.setIpAddress(ipAddress);String hostName = InetAddress.getLocalHost().getHostName();logger.info("机器名:" + hostName);eventTrackLog.setHostName(hostName);Enumeration<?> enu = request.getParameterNames();String params = "{";while (enu.hasMoreElements()) {String paraName = (String) enu.nextElement();List<String> list = Arrays.asList(EXCLUDE_PROPERTIES);if(list.contains(paraName)) {continue;}params += "\"" + paraName + "\":\"" + request.getParameter(paraName) + "\",";}String methodParams = params + "}";String substring = methodParams.substring(0, methodParams.length() - 2);substring = substring + "}";logger.info("方法参数:" + substring);eventTrackLog.setReqParams(substring);StringBuffer url = request.getRequestURL();logger.info("URL:" + url);eventTrackLog.setUrl(String.valueOf(url));}catch (Exception e) {}Object proceed = pjp.proceed();String result = JSON.toJSONString(proceed);logger.info("==============环切方法执行完成==============");logger.info("请求结果:" + result);try {Long endTime = System.currentTimeMillis();Long timeSpan = endTime - startTime;logger.info("timeSpan = " + timeSpan);eventTrackLog.setTimeSpend(timeSpan);eventTrackLog.setEndTime(new Date());// 获取当前的用户SysUser currentUser = ShiroUtils.getSysUser();if(currentUser != null){eventTrackLog.setUserName(currentUser.getLoginName());eventTrackLog.setDeptName(currentUser.getDept().getDeptName());}// eventTrackLog.setSystemName("平台管理系统");eventTrackLog.setSystemName(RuoYiConfig.getName());// eventTrackLog.setModuleTitle("后台管理");eventTrackLog.setStatus(0l);eventTrackLog.setNumber(1L);eventTrackLog.setModuleTitle(eventTrack.title());eventTrackLog.setBusinessType(eventTrack.businessType().toString());if(ServletUtils.checkAgentIsMobile(ServletUtils.getRequest().getHeader("User-Agent"))){eventTrackLog.setChannel("Mobile");}else {eventTrackLog.setChannel("PC");}eventTrackLog.setCreateBy("around");eventTrackLog.setCreateTime(new Date());} catch (Exception e) {} finally {// 添加日志信息入库eventTrackLogService.insertEventTrackLog(eventTrackLog);}return proceed;}/*** 拦截异常操作** @param joinPoint 切点* @param ex 异常*/@AfterThrowing(value = "@annotation(eventTrack)", throwing = "ex")public void doAfterThrowing(JoinPoint joinPoint, EventTrack eventTrack, Exception ex){logger.info("==============异常方法执行==============");EventTrackLog eventTrackLog = this.eventTrackLog.get();try {//获取方法名String methodName = joinPoint.getSignature().getName();Long end = System.currentTimeMillis();Long total = end - startTime.get().get("startTime");logger.info("执行总耗时为:" +total);eventTrackLog = this.eventTrackLog.get();eventTrackLog.setTimeSpend(total);String endTime = DateUtils.getTime();logger.info("请求结束时间:" + endTime);eventTrackLog.setEndTime(new Date());// 获取当前的用户SysUser currentUser = ShiroUtils.getSysUser();if(currentUser != null){eventTrackLog.setUserName(currentUser.getLoginName());eventTrackLog.setDeptName(currentUser.getDept().getDeptName());}// eventTrackLog.setSystemName("平台管理系统");eventTrackLog.setSystemName(RuoYiConfig.getName());// eventTrackLog.setModuleTitle("后台管理");eventTrackLog.setStatus(2l);eventTrackLog.setNumber(1L);eventTrackLog.setExceptMsg(ex.getMessage());eventTrackLog.setModuleTitle(eventTrack.title());eventTrackLog.setBusinessType(eventTrack.businessType().toString());if(ServletUtils.checkAgentIsMobile(ServletUtils.getRequest().getHeader("User-Agent"))){eventTrackLog.setChannel("Mobile");}else {eventTrackLog.setChannel("PC");}eventTrackLog.setCreateBy("except");eventTrackLog.setCreateTime(new Date());} catch (Exception e) {StackTraceElement stackTraceElement2 = e.getStackTrace()[2];String reason = "异常:【"+"类名:"+stackTraceElement2.getClassName()+";"+"文件:"+stackTraceElement2.getFileName()+";"+"行:"+stackTraceElement2.getLineNumber()+";"+"方法:"+stackTraceElement2.getMethodName() + "】";//记录本地异常日志logger.error("==============通知异常:记录访问异常信息==============");String message = e.getMessage() + "|" + reason;logger.error("异常信息:",message);eventTrackLog.setExceptMsg(message);eventTrackLog.setResult("请求发生异常!!!");} finally {// 添加日志信息入库eventTrackLogService.insertEventTrackLog(eventTrackLog);// 处理使用结束,清理变量this.eventTrackLog.remove();}}/**** @param jp*/@AfterReturning(pointcut = "@annotation(eventTrack)", returning = "jsonResult")public void afterMethod(JoinPoint jp, EventTrack eventTrack, Object jsonResult) {logger.info("==============方法执行完成==============");EventTrackLog eventTrackLog = this.eventTrackLog.get();try {//获取方法名String methodName = jp.getSignature().getName();Long end = System.currentTimeMillis();Long total = end - startTime.get().get("startTime");logger.info("执行总耗时为:" +total);eventTrackLog = this.eventTrackLog.get();eventTrackLog.setTimeSpend(total);String endTime = DateUtils.getTime();logger.info("请求结束时间:" + endTime);eventTrackLog.setEndTime(new Date());// 获取当前的用户SysUser currentUser = ShiroUtils.getSysUser();if(currentUser != null){eventTrackLog.setUserName(currentUser.getLoginName());eventTrackLog.setDeptName(currentUser.getDept().getDeptName());}// eventTrackLog.setSystemName("平台管理系统");eventTrackLog.setSystemName(RuoYiConfig.getName());// eventTrackLog.setModuleTitle("后台管理");eventTrackLog.setStatus(0l);eventTrackLog.setNumber(1L);eventTrackLog.setModuleTitle(eventTrack.title());eventTrackLog.setBusinessType(eventTrack.businessType().toString());if(ServletUtils.checkAgentIsMobile(ServletUtils.getRequest().getHeader("User-Agent"))){eventTrackLog.setChannel("Mobile");}else {eventTrackLog.setChannel("PC");}eventTrackLog.setCreateBy("return");eventTrackLog.setCreateTime(new Date());} catch (Exception e) {StackTraceElement stackTraceElement2 = e.getStackTrace()[2];String reason = "异常:【"+"类名:"+stackTraceElement2.getClassName()+";"+"文件:"+stackTraceElement2.getFileName()+";"+"行:"+stackTraceElement2.getLineNumber()+";"+"方法:"+stackTraceElement2.getMethodName() + "】";//记录本地异常日志logger.error("==============通知异常:记录访问异常信息==============");String message = e.getMessage() + "|" + reason;logger.error("异常信息:",message);eventTrackLog.setExceptMsg(message);eventTrackLog.setResult("请求发生异常!!!");} finally {// this.eventTrackLog.set(eventTrackLog);// 添加日志信息入库eventTrackLogService.insertEventTrackLog(eventTrackLog);// 处理使用结束,清理变量this.eventTrackLog.remove();}}/*** 获取注解中对方法的描述信息 用于Controller层注解*/public static String getControllerMethodDescription(JoinPoint joinPoint) throws Exception {String targetName = joinPoint.getTarget().getClass().getName();String methodName = joinPoint.getSignature().getName();//目标方法名Object[] arguments = joinPoint.getArgs();Class targetClass = Class.forName(targetName);Method[] methods = targetClass.getMethods();String description = "";for (Method method:methods) {if (method.getName().equals(methodName)){Class[] clazzs = method.getParameterTypes();if (clazzs.length==arguments.length){description = method.getAnnotation(EventTrack.class).description();break;}}}return description;}}
在切面类中,定义了前置通知和后置通知,前置通知记录方法调用的开始时间,后置通知包括正常返回和抛出异常两种情况,后置通知记录方法的结束时间,通过两个时间,就可以计算出方法的执行时间。另外一种方法是采用环切通知,执行前记录一次时间,执行后记录一次时间,因为在同一个方法内,不需要定义线程本地变量了。
根据需要,两种方式选择一种就可以了。
2.4 添加切面注解
@RequiresPermissions("system:user:list")
@PostMapping("/list")
@EventTrack(title = "用户管理", businessType = BusinessType.QUERY, description="查询用户")
@ResponseBody
public TableDataInfo list(SysUser user)
{startPage();List<SysUser> list = userService.selectUserList(user);return getDataTable(list);
}
在方法上添加注释:
@EventTrack(title = "用户管理", businessType = BusinessType.QUERY, description="查询用户")
相关参数根据场景进行设置。
2.5 日志信息
通过日志信息,可以看出每个操作所花费的时间,单位是毫秒。通过日志时间,也可以看出是什么时间进行的操作,通过操作人员,可以知道是谁操作的。还可以关联其它的业务信息,比如用户类型,用户所属机构等。
java利用切面(aspect)记录日志实现性能跟踪以及用户行为分析相关推荐
- 利用AOP进行简单的性能监控及用户行为审计
做web项目后台开发的时候,比较关心的一个问题程序跑的快不快,这个问题的最好度量就是计时器了.但是在每一个方法里面都写一个计时器,弊端太明显了:代码冗余大,维护起来巨麻烦.切面编程为解决这个问题的不错 ...
- paip.性能跟踪profile原理与架构与本质-- python扫带java php
paip.性能跟踪profile原理与架构与本质-- python扫带java php ##背景 弄个个输入法音标转换atiEnPH工具,老是python性能不的上K,7k记录浏览过k要30分钟了. ...
- JProfiler 解决 Java 服务器的性能跟踪
作者:徐建祥(netpirate@gmail.com) 时间: 2006/01/05 来自:http://www.anymobile.org 1.摘要......................... ...
- 十分良心!全网最详细的Java 自动内存管理机制及性能优化教程
同样的,先来个思维导图预览一下本文结构. 一图带你看完本文 一.运行时数据区域 首先来看看Java虚拟机所管理的内存包括哪些区域,就像我们要了解一个房子,我们得先知道这个房子大体构造.根据<Ja ...
- 日志库EasyLogging++学习系列(9)—— 性能跟踪功能
性能跟踪是 Easylogging++ 其中一个非常显著的功能,而且使用起来也十分简单.如果在Windows平台下使用性能跟踪的话,其原理是基于 Windows API函数 GetSystemTime ...
- 【spring】切入点(Pointcut)、方面/切面(Aspect、Advisor)详解
文章目录 1. Pointcut概念的引入及简介 1.1 Pointcut接口 1.1.1 ClassFilter接口 1.1.2 MethodMatcher接口 1.1.3 TruePointcut ...
- 流言粉碎机:JAVA使用 try catch会影响性能
流言粉碎机:JAVA使用 try catch会影响性能 一.JVM 异常处理逻辑 二.关于JVM的编译优化 1. 分层编译 2. 即时编译器 1. 解释模式 2. 编译模式 3. 提前编译器:jaot ...
- 利用电脑自带的性能监视器进行资源监控
本文来源:http://blog.163.com/jack_test/blog/static/166620663201061594459936 [摘要] 在性能测试中,你要对测试结果进行数据分析,就要 ...
- Java Springboot切面+注解实现数据脱敏
Java Springboot切面+注解实现数据脱敏 1. 业务概述 2. 设计编码 2.1 脱敏类型枚举 2.2 脱敏注解 2.3 脱敏工具类 2.4 统一拦截器 2.5 统一结果集 2.6 用户实 ...
最新文章
- WordPress插件扫描工具plecost
- virtual box挂载 共享文件夹
- 【组原】机器字长、指令字长、存储字长、存储单元、存储字 的区分
- HTML编码问题导致的乱码
- 如何写_如何写博士论文?博士生如何写期刊论文?
- net空间一次购买终身使用_官方解答关于 Internet Download Manager IDM 终身许可证和1年许可证的相关说明!...
- C++ 常用算数生成算法
- linux nmap命令,nmap命令
- 优秀项目经理应具备的素质和能力
- Bone Collector——01背包
- 基于SSM框架和JSP的房屋租赁、合同签订系统
- 百度大脑大升级:各种算法并驾齐驱
- 水纹(涟漪)特效壁纸——程序+实现原理
- 美女硕士养猪记:如何用大数据激活一头猪
- 索骥馆-OFFICE系列之《北风网Excel高端应用培训:多条件约束报表自动统计系统分析与制作》共4章更新完
- python基础“猜单词游戏”代码
- win7家庭版怎么升级旗舰版
- C++版 PPyolo+部署记录
- Cause: java.sql.SQLException: 无效的列类型
- 一个免费提供Linux相关ebook的站点
热门文章
- 一周信创舆情观察(5.24~5.30)
- java中的arg是什么意思_java中args是什么意思?
- 2011年IT业趋势盘点(谈IT发展,观天下大势)
- .NET Framework 框架简介
- Kubenetes基础学习
- RELY-CPPS应用案例——如何用一台设备解决数字化的挑战
- Gatsby中怎么使用emotion?
- 华北电力计算机技术考研难度,华北电力大学(保定)计算机技术怎么样
- 摘自-万能的林萧说:我来告诉你,一个草根程序员如何进入BAT。
- 红米note9和红米k30有什么区别 红米note9和红米k30哪个好