记录日志的目的是多种多样的,这里主要以性能跟踪和用户行为分析为目的讨论如何记录日志。

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)记录日志实现性能跟踪以及用户行为分析相关推荐

  1. 利用AOP进行简单的性能监控及用户行为审计

    做web项目后台开发的时候,比较关心的一个问题程序跑的快不快,这个问题的最好度量就是计时器了.但是在每一个方法里面都写一个计时器,弊端太明显了:代码冗余大,维护起来巨麻烦.切面编程为解决这个问题的不错 ...

  2. paip.性能跟踪profile原理与架构与本质-- python扫带java php

    paip.性能跟踪profile原理与架构与本质-- python扫带java php ##背景 弄个个输入法音标转换atiEnPH工具,老是python性能不的上K,7k记录浏览过k要30分钟了. ...

  3. JProfiler 解决 Java 服务器的性能跟踪

    作者:徐建祥(netpirate@gmail.com) 时间: 2006/01/05 来自:http://www.anymobile.org 1.摘要......................... ...

  4. 十分良心!全网最详细的Java 自动内存管理机制及性能优化教程

    同样的,先来个思维导图预览一下本文结构. 一图带你看完本文 一.运行时数据区域 首先来看看Java虚拟机所管理的内存包括哪些区域,就像我们要了解一个房子,我们得先知道这个房子大体构造.根据<Ja ...

  5. 日志库EasyLogging++学习系列(9)—— 性能跟踪功能

    性能跟踪是 Easylogging++ 其中一个非常显著的功能,而且使用起来也十分简单.如果在Windows平台下使用性能跟踪的话,其原理是基于 Windows API函数 GetSystemTime ...

  6. 【spring】切入点(Pointcut)、方面/切面(Aspect、Advisor)详解

    文章目录 1. Pointcut概念的引入及简介 1.1 Pointcut接口 1.1.1 ClassFilter接口 1.1.2 MethodMatcher接口 1.1.3 TruePointcut ...

  7. 流言粉碎机:JAVA使用 try catch会影响性能

    流言粉碎机:JAVA使用 try catch会影响性能 一.JVM 异常处理逻辑 二.关于JVM的编译优化 1. 分层编译 2. 即时编译器 1. 解释模式 2. 编译模式 3. 提前编译器:jaot ...

  8. 利用电脑自带的性能监视器进行资源监控

    本文来源:http://blog.163.com/jack_test/blog/static/166620663201061594459936 [摘要] 在性能测试中,你要对测试结果进行数据分析,就要 ...

  9. Java Springboot切面+注解实现数据脱敏

    Java Springboot切面+注解实现数据脱敏 1. 业务概述 2. 设计编码 2.1 脱敏类型枚举 2.2 脱敏注解 2.3 脱敏工具类 2.4 统一拦截器 2.5 统一结果集 2.6 用户实 ...

最新文章

  1. WordPress插件扫描工具plecost
  2. virtual box挂载 共享文件夹
  3. 【组原】机器字长、指令字长、存储字长、存储单元、存储字 的区分
  4. HTML编码问题导致的乱码
  5. 如何写_如何写博士论文?博士生如何写期刊论文?
  6. net空间一次购买终身使用_官方解答关于 Internet Download Manager IDM 终身许可证和1年许可证的相关说明!...
  7. C++ 常用算数生成算法
  8. linux nmap命令,nmap命令
  9. 优秀项目经理应具备的素质和能力
  10. Bone Collector——01背包
  11. 基于SSM框架和JSP的房屋租赁、合同签订系统
  12. 百度大脑大升级:各种算法并驾齐驱
  13. 水纹(涟漪)特效壁纸——程序+实现原理
  14. 美女硕士养猪记:如何用大数据激活一头猪
  15. 索骥馆-OFFICE系列之《北风网Excel高端应用培训:多条件约束报表自动统计系统分析与制作》共4章更新完
  16. python基础“猜单词游戏”代码
  17. win7家庭版怎么升级旗舰版
  18. C++版 PPyolo+部署记录
  19. Cause: java.sql.SQLException: 无效的列类型
  20. 一个免费提供Linux相关ebook的站点

热门文章

  1. 一周信创舆情观察(5.24~5.30)
  2. java中的arg是什么意思_java中args是什么意思?
  3. 2011年IT业趋势盘点(谈IT发展,观天下大势)
  4. .NET Framework 框架简介
  5. Kubenetes基础学习
  6. RELY-CPPS应用案例——如何用一台设备解决数字化的挑战
  7. Gatsby中怎么使用emotion?
  8. 华北电力计算机技术考研难度,华北电力大学(保定)计算机技术怎么样
  9. 摘自-万能的林萧说:我来告诉你,一个草根程序员如何进入BAT。
  10. 红米note9和红米k30有什么区别 红米note9和红米k30哪个好