基于AOP的日志输出自定义注解
程序员文章站
2024-03-26 11:23:53
...
前言
在企业开发中,日志输出是一种习惯,也是开发中强制要求的,这样可以快速定位问题,当然可以收集日志进行数据分析,每个公司都有自己的风格,就我们公司的日志输出做一个简单的展示
- 公司日志风格
@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排查这是我们公司最基本的日志输出格式,开始->结束-> 异常,一目了然
- 问题
以上三行每个方法都要写一遍,有的同学想第一时间想到CV也就几秒的事,说的也是,CV类名、方法名、注解、入参、返回结果,现在想想都有点恶心。
- 解决
为了解决上面问题,观察日志输出规律,你可以很轻松的想到Aop。。。。。是的没错就是Aop,完美解决上述问题
- 上代码
(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时间,同时也可以规范代码开发,效率更高。