使用aop和注解实现日志记录

问题描述:
    生产上遇到一个问题,就是第三方厂商调用我们服务创建数据库实例,后面创建成功后,因为某些条件不适合,又调用卸载接口进行卸载了。
卸载后再次进行创建,创建成功。
但是过了一周后,第三方厂商人员反馈创建的数据库实例集群映射的DNS域名的ip还是之前旧的删除的哪套的,不是最新的。那么我去定位问题。无非就是去查看日志。
    但是发现日志记录不全。所以我萌生了使用aop切面来记录请求和参数的想法。
于是就干起来了,实现主要有3点
 
1:打印日志的注解实现
@Target({ElementType.TYPE, ElementType.METHOD}) // 注解类型, 级别
@Retention(RetentionPolicy.RUNTIME) // 运行时注解
public @interface PrintLog {
 
    String module() default ""; //方法标识,比如赋值为 新增数据库实例
    boolean printParam() default true; //默认打印请求参数在日志中
 
}
 
2:切面定义
@Aspect // 切面标识
@Component // 交给spring容器管理
@Slf4j
 
public class PrintLogAspect {
 
    @Autowired
 
    /**
     * 选取切入点为自定义注解
     */
    @Pointcut("@annotation(cn.com.huacloud.cdd.annotation.PrintLog)")
    public void PrintLog(){}
 
    @Around(value="PrintLog()")
    public Object printlog(ProceedingJoinPoint joinPoint) throws Throwable {
 
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
 
        PrintLog printLog = methodSignature.getMethod().getDeclaredAnnotation(PrintLog.class);
        String module = printLog.module();
        if(StringUtils.isEmpty(module)){
            String classname = joinPoint.getTarget().getClass().getName();
            String methodName= methodSignature.getMethod().getName();
            module = classname+":"+methodName;
        }
        String paramsString = "";
        // 是否要打印方法的参数数据
        if (printLog.printParam()) {
            // 参数名
            String[] paramNames = methodSignature.getParameterNames();
            if (paramNames != null && paramNames.length > 0) {
                // 参数值
                Object[] args = joinPoint.getArgs();
                Map<String, Object> params = new HashMap<>();
                for (int i = 0; i < paramNames.length; i++) {
                    Object value = args[i];
                    params.put(paramNames[i], value);
                }
                // 以json的形式记录参数
                paramsString = JSONObject.toJSONString(params);
            }
        }
        String username = "";
        try{
            LoginUser loginUser = SecurityUtil.getLoginUser();
            username = loginUser.getNickName();
        }catch(Exception e){
            log.info("切面获取用户失败!");
            e.printStackTrace();
        }
        try {
 
            String startTime = DateUtil.now();
            //统一打印执行前日志
            log.info(StrUtil.format("当前时间{},用户{}请求进入方法模块{}开始,请求参数为{}!", startTime,username, module,paramsString));
            // 执行原方法
            Object object = joinPoint.proceed();
            //统一打印执行后日志
            String endTime = DateUtil.now();
            log.info(StrUtil.format("当前时间{},用户{}请求进入方法模块{}开始,请求参数为{}", endTime,username, module,paramsString));
 
            return object;
        } catch (Exception e) {
            // 备注记录失败原因
            String exceptionTime = DateUtil.now();
            log.info(StrUtil.format("异常时间{},用户{}请求进入方法模块{}异常,请求参数为{},异常原因是{}",exceptionTime,username, module,paramsString, e));
            throw e;
        }
    }
 
 
}
 
 
3:注解使用方式
使用aop和注解实现日志记录
 
这样你在调用queryIMysqlInstance方法前后就会输出日志了。
 
疑难点:
 
获取当前方法所属的类:            String classname = joinPoint.getTarget().getClass().getName();
备注:
在AOP编程中,我们经常会遇到下面的概念:
  • Aspect:切面,即一个横跨多个核心逻辑的功能,或者称之为系统关注点;
  • Joinpoint:连接点,即定义在应用程序流程的何处插入切面的执行;
  • Pointcut:切入点,即一组连接点的集合;
  • Advice:增强,指特定连接点上执行的动作;
  • Introduction:引介,指为一个已有的Java对象动态地增加新的接口;
  • Weaving:织入,指将切面整合到程序的执行流程中;
  • Interceptor:拦截器,是一种实现增强的方式;
  • Target Object:目标对象,即真正执行业务的核心逻辑对象;
  • AOP Proxy:AOP代理,是客户端持有的增强后的对象引用。
 
引申思考:
这里提到了代理模式,后续我会对这里如何使用代理模式进行重点阐述。

使用aop和注解实现日志记录

上一篇:系统不做任何优化,性能提升10%的方法


下一篇:OpenCV开发环境搭建-并测试一个图像灰度处理程序