有次接口响应时间太长,想知道具体接口执行的时间是多少,于是决定通过注解来实现这个想法,刚好ruoyi本身就提供了完善的日志注解,虽然是采用后置通知,但是完全不影响我们改造它。
想要实现接口耗时的功能,那就必须要获取到接口开始前和接口结束后的时间,后置通知肯定是不行了,但是环绕通知可以呀。
上代码,除了实现新的记录耗时功能外,原本的功能也保留了
package com.ruoyi.common.log.aspect; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; import com.ruoyi.common.core.constant.Constants; import com.ruoyi.common.core.exception.ServiceException; import com.ruoyi.common.core.utils.ServletUtils; import com.ruoyi.common.core.utils.StringUtils; import com.ruoyi.common.core.utils.ip.IpUtils; import com.ruoyi.common.log.annotation.Log; import com.ruoyi.common.log.enums.BusinessStatus; import com.ruoyi.common.log.filter.PropertyPreExcludeFilter; import com.ruoyi.common.log.service.AsyncLogService; import com.ruoyi.common.security.service.utils.SecurityUtils; import com.ruoyi.system.api.domain.SysOperLog; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpMethod; import org.springframework.stereotype.Component; import org.springframework.util.ObjectUtils; import org.springframework.validation.BindingResult; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Collection; import java.util.HashMap; import java.util.Map; /** * 操作日志记录处理 * * @author ruoyi */ @Aspect @Component public class LogAspect { private static final Logger log = LoggerFactory.getLogger(LogAspect.class); /** * 排除敏感属性字段 */ public static final String[] EXCLUDE_PROPERTIES = {"password", "oldPassword", "newPassword", "confirmPassword"}; @Autowired private AsyncLogService asyncLogService; @Pointcut("@annotation(com.ruoyi.common.log.annotation.Log)") public void pointCut() { } /** * 处理完请求后执行 * * @param joinPoint 切点 */ @Around("pointCut() && @annotation(log)") public Object around(ProceedingJoinPoint joinPoint, Log log) { return handleLog(joinPoint, log); } protected Object handleLog(ProceedingJoinPoint joinPoint, Log log) { // *========数据库日志=========*// SysOperLog operLog = new SysOperLog(); operLog.setStatus(BusinessStatus.SUCCESS.ordinal()); // 请求的地址 String ip = IpUtils.getIpAddr(ServletUtils.getRequest()); operLog.setOperIp(ip); operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255)); String username = SecurityUtils.getUsername(); if (StringUtils.isNotBlank(username)) { operLog.setOperName(username); } // 设置方法名称 String className = joinPoint.getTarget().getClass().getName(); String methodName = joinPoint.getSignature().getName(); operLog.setMethod(className + "." + methodName + "()"); // 设置请求方式 operLog.setRequestMethod(ServletUtils.getRequest().getMethod()); // 处理设置注解上的参数 getControllerMethodDescription(joinPoint, log, operLog); try { long start = System.currentTimeMillis(); Object proceed = joinPoint.proceed(); //设置接口耗时 operLog.setCost(System.currentTimeMillis() - start); ////设置接口响应结果,如果响应结果为空或者是查询接口则赋予默认值 if (ObjectUtils.isEmpty(proceed) || HttpMethod.GET.toString().equals(operLog.getRequestMethod())) { operLog.setJsonResult(Constants.OK_RESPONSE_JSON); } else { operLog.setJsonResult(JSONObject.toJSONString(proceed)); } // 保存数据库 asyncLogService.saveSysLog(operLog); return proceed; } catch (Throwable e) { Map<String, Object> errMap = new HashMap<>(1); errMap.put("err_msg", e.getMessage()); errMap.put("stack_trace", e.getStackTrace()[0]); operLog.setErrorMsg(JSONObject.toJSONString(errMap)); asyncLogService.saveSysLog(operLog); throw new ServiceException(e.getMessage()); } } /** * 获取注解中对方法的描述信息 用于Controller层注解 * * @param log 日志 * @param operLog 操作日志 */ public void getControllerMethodDescription(ProceedingJoinPoint joinPoint, Log log, SysOperLog operLog) { // 设置action动作 operLog.setBusinessType(log.businessType().ordinal()); // 设置标题 operLog.setTitle(log.title()); // 设置操作人类别 operLog.setOperatorType(log.operatorType().ordinal()); // 是否需要保存request,参数和值 if (log.isSaveRequestData()) { // 获取参数的信息,传入到数据库中。 setRequestValue(joinPoint, operLog); } } /** * 获取请求的参数,放到log中 * * @param operLog 操作日志 */ private void setRequestValue(ProceedingJoinPoint joinPoint, SysOperLog operLog) { String requestMethod = operLog.getRequestMethod(); if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) { String params = argsArrayToString(joinPoint.getArgs()); operLog.setOperParam(StringUtils.substring(params, 0, 2000)); } } /** * 参数拼装 */ private String argsArrayToString(Object[] paramsArray) { StringBuilder params = new StringBuilder(); if (paramsArray != null && paramsArray.length > 0) { for (Object o : paramsArray) { if (StringUtils.isNotNull(o) && !isFilterObject(o)) { try { String jsonObj = JSON.toJSONString(o, excludePropertyPreFilter()); params.append(jsonObj).append(" "); } catch (Exception ignored) { } } } } return params.toString().trim(); } /** * 忽略敏感属性 */ public PropertyPreExcludeFilter excludePropertyPreFilter() { return new PropertyPreExcludeFilter().addExcludes(EXCLUDE_PROPERTIES); } /** * 判断是否需要过滤的对象。 * * @param o 对象信息。 * @return 如果是需要过滤的对象,则返回true;否则返回false。 */ @SuppressWarnings("rawtypes") public boolean isFilterObject(final Object o) { Class<?> clazz = o.getClass(); if (clazz.isArray()) { return clazz.getComponentType().isAssignableFrom(MultipartFile.class); } else if (Collection.class.isAssignableFrom(clazz)) { Collection collection = (Collection) o; for (Object value : collection) { return value instanceof MultipartFile; } } else if (Map.class.isAssignableFrom(clazz)) { Map map = (Map) o; for (Object value : map.entrySet()) { Map.Entry entry = (Map.Entry) value; return entry.getValue() instanceof MultipartFile; } } return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse || o instanceof BindingResult; } }
代码也挺容易看懂的,毕竟日志功能不管在哪都很重要,下面是日志的实体类
@Data @EqualsAndHashCode(callSuper = true) @Accessors(chain = true) public class SysOperLog extends BaseEntity { private static final long serialVersionUID = 1L; /** 日志主键 */ @Excel(name = "操作序号", cellType = ColumnType.NUMERIC) private Long operId; /** 操作模块 */ @Excel(name = "操作模块") private String title; /** 业务类型(0其它 1新增 2修改 3删除) */ @Excel(name = "业务类型", readConverterExp = "0=其它,1=新增,2=修改,3=删除,4=授权,5=导出,6=导入,7=强退,8=生成代码,9=清空数据") private Integer businessType; /** 业务类型数组 */ private Integer[] businessTypes; /** 请求方法 */ @Excel(name = "请求方法") private String method; /** 请求方式 */ @Excel(name = "请求方式") private String requestMethod; /** 操作类别(0其它 1后台用户 2手机端用户) */ @Excel(name = "操作类别", readConverterExp = "0=其它,1=后台用户,2=手机端用户") private Integer operatorType; /** 操作人员 */ @Excel(name = "操作人员") private String operName; /** 部门名称 */ @Excel(name = "部门名称") private String deptName; /** 请求url */ @Excel(name = "请求地址") private String operUrl; /** 操作地址 */ @Excel(name = "操作地址") private String operIp; /** 请求参数 */ @Excel(name = "请求参数") private String operParam; /** 返回参数 */ @Excel(name = "返回参数") private String jsonResult; /** 操作状态(0正常 1异常) */ @Excel(name = "状态", readConverterExp = "0=正常,1=异常") private Integer status; /** 错误消息 */ @Excel(name = "错误消息") private String errorMsg; @Excel(name = "接口耗时") private long cost; /** 操作时间 */ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") @Excel(name = "操作时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") private Date operTime; }
标签:String,name,operLog,private,ruoyi,切面,import,日志,log From: https://www.cnblogs.com/wzkris/p/16925118.html