最近因为公司业务的原因,需要对用户登录,用户注销登录、用户创建、应用创建、应用编辑、应用删除、应用打开等等地方加入日志,用于记录活动轨迹。最简单的实现方式,在所有的需要日志的接口中加入写日志的代码。日志需要包括:什么模块,干了什么,入参是什么,成功与否,如果失败,失败原因是什么。考虑到上面这些要素,对应不同模块的接口,代码实现起来比较繁琐,而且失败原因,如果是非预期的运行时异常,很难记录下来。下面用一种比较优雅的方式,实现业务需求。
主要用到的技术:AOP
step1: 自定义注解
/**
* @Author: zzh
* @Date: 2020/7/2 14:33
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OperationLog {
String operateType() default "";
String operateContent() default "";
}
step2:将自定义注解加在需要记录日志的方法上
@Override
@OperationLog(operateType = "桌面", operateContent = "创建桌面")
public String addVm(MemberDto dto) {
//此处省略一万字
}
step3:定义切面
package com.xietong.phoenix.aspect;
import com.google.gson.Gson;
import com.xietong.phoenix.common.utils.GennerateUtil;
import com.xietong.phoenix.module.log.entity.SysUserLog;
import com.xietong.phoenix.module.log.service.LogService;
import com.xietong.sns.application.LoginUserService;
import com.xietong.sns.domain.model.system.LoginCredential;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* @Author: zzh
* @Date: 2020/7/2 14:31
*/
@Aspect
@Component
public class LogAspect {
private ThreadLocal<SysUserLog> local = new ThreadLocal<SysUserLog>();
@Autowired
private LoginUserService loginUserService;
@Autowired
private LogService logService;
@Pointcut("@annotation(com.xietong.phoenix.aspect.OperationLog)")
public void logPointCut() {
}
@Around("logPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
Class<?> clazz = point.getTarget().getClass();
method = clazz.getMethod(method.getName(), method.getParameterTypes());
OperationLog operationLog = method.getAnnotation(OperationLog.class);
SysUserLog sysUserLog = new SysUserLog();
sysUserLog.setId(GennerateUtil.generateId(16));
sysUserLog.setOperateContent(operationLog.operateContent());
sysUserLog.setOperateType(operationLog.operateType());
sysUserLog.setParams(Arrays.toString(point.getArgs()));
LoginCredential loginUser = loginUserService.getLoginUser();
if (loginUser != null) {
sysUserLog.setOperatorName(loginUserService.getLoginUser().getRegisterName());
sysUserLog.setOperatorId(loginUserService.getLoginUser().getId());
}
sysUserLog.setMethodName(point.getSignature().getName());
local.set(sysUserLog);
Object result = point.proceed();
sysUserLog.setOperateResult(1);
logService.addLog(sysUserLog);
return result;
}
@AfterThrowing(pointcut = "logPointCut()", throwing = "e")
public void afterThrowing(Throwable e) {
SysUserLog sysUserLog = local.get();
if (sysUserLog != null) {
sysUserLog.setOperateResult(0);
sysUserLog.setOperateFailReason(String.format("traceId : \":%s\"", MDC.get("traceId")));
logService.addLog(sysUserLog);
}
}
}