美文网首页
基于AOP的日志输出自定义注解

基于AOP的日志输出自定义注解

作者: RalapHao | 来源:发表于2018-05-04 17:41 被阅读0次

    前言

    在企业开发中,日志输出是一种习惯,也是开发中强制要求的,这样可以快速定位问题,当然可以收集日志进行数据分析,每个公司都有自己的风格,就我们公司的日志输出做一个简单的展示

    1. 公司日志风格
    @Override
      public classA<classB>methodName(String logId,ByxRepaymentScheduleModel model) {
          LoggerUtils.infoForStart(Class clazz, String businessName,String businessMethod, String businessId, Object inputParamObj)
    try{
          ..............................(省略)
    
          LoggerUtils.infoForEnd(Class clazz, String businessName,String businessMethod, String businessId, Object outputParamObj);
    
    }catch{
    LoggerUtils.error(Class clazz, String businessName, String businessMethod, String businessId, Exception e)
    }
          return ***;
      }
    

    便于日志分析,bug排查这是我们公司最基本的日志输出格式,开始->结束-> 异常,一目了然

    1. 问题

    以上三行每个方法都要写一遍,有的同学想第一时间想到CV也就几秒的事,说的也是,CV类名、方法名、注解、入参、返回结果,现在想想都有点恶心。

    1. 解决

    为了解决上面问题,观察日志输出规律,你可以很轻松的想到Aop。。。。。是的没错就是Aop,完美解决上述问题

    1. 上代码
      (1).配置类
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.EnableAspectJAutoProxy;
    
    /**
     * @author: ralap
     * @date: created at 2018/5/4 15:26
     */
    @Configuration
    @ComponentScan("com.itfan.kernel.service")
    @EnableAspectJAutoProxy
    public class LogConfig {
    
    }
    
    

    添加配置信息,扫描指定路径,开启Aop的代理自动配置。

    (2).自定义注解

    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;
    
    /**
     * @author: ralap
     * @date: created at 2018/5/4 15:28
     */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface ItfanLogMessage {
    
        String value() default "";
    }
    

    自定义注解,添加属性value

    (4).定义切面

    import java.lang.reflect.Method;
    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.aspectj.lang.reflect.MethodSignature;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;
    
    /**
     * @author: ralap
     * @date: created at 2018/5/4 15:31
     */
    @Aspect
    @Component
    public class LogAspact {
    
        private static final Logger logger = LoggerFactory.getLogger(LogAspact.class);
    
        @Pointcut("@annotation(com.itfan.kernel.config.log.ItfanLogMessage)")
        public void annotationPointCut() {
    
        }
    
        @Around("annotationPointCut()")
        public Object around(ProceedingJoinPoint joinPoint) {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            String className = signature.getDeclaringTypeName();
            String methodName = signature.getName();
            Method method = signature.getMethod();
            Object[] args = joinPoint.getArgs();
            String logNo = null;
            Object proceed = null;
            ItfanLogMessage action = method.getAnnotation(ItfanLogMessage.class);
            String agrsStr = "";
            for (int i = 0; i < args.length - 1; i++) {
                if (i < args.length - 2) {
                    agrsStr = agrsStr + args[i] + ",";
                } else {
                    agrsStr = agrsStr + args[i];
                }
    
            }
            logNo = (String) args[args.length - 1];
            try {
                logger.info("【开始】--->类名:【" + className + "】, 方法名【"
                        + methodName + "】,方法描述【" + action.value() + "】,入参【" + agrsStr + "】,日志流水号【"
                        + logNo + "】");
                Long startTime = System.currentTimeMillis();
                proceed = joinPoint.proceed();
                logger.info("【结束】--->类名:【" + className + "】, 方法名【"
                        + methodName + "】,方法描述【" + action.value() + "】,返回参数【" + proceed.toString()
                        + "】,日志流水号【" + logNo + "】,耗时【"+(System.currentTimeMillis()-startTime) +"毫秒】");
            } catch (Throwable throwable) {
    //            throwable.printStackTrace();
                logger.error("【异常】--->类名:【" + className + "】, 方法名【"
                        + methodName + "】,方法描述【" + action.value() + "】,日志流水号【"
                        + logNo + "】,异常信息【" + throwable.getMessage() + "】");
            }
    
            return proceed;
        }
    }
    
    

    定义切面,配置切点为自定义的注解,根据实际需求,选择Around环绕通知,要求入参的最后一个参数为日志流水号,具体实现细节请看上面代码。

    (5)用法

        @Override
        @ItfanLogMessage("获取轮播视频")
        public List<Video> getCarousel(String logNo) {
            List<Video> viewList = redisManager
             .getViewList(RedisKey.VIDEO_PREFIX_HOME_CAROUSEL_KEY, VideTag.LETV_TAG);
            return viewList;
        }
    
    

    总结
    可以对比二者日志输出,很显然,使用AOP大大节省了CV时间,同时也可以规范代码开发,效率更高。

    相关文章

      网友评论

          本文标题:基于AOP的日志输出自定义注解

          本文链接:https://www.haomeiwen.com/subject/ghodrftx.html