1、方法执行日志方案设计
1.1 注解的方式
通过定义日志注解+解析注解的方式来实现记录方法执行日志,这种方式在需要监控的接口实现方法上打上定义的注解,再通过解析注解的方式将日志记录到文件。
注解方式的优点是粒度细,在需要监控的服务接口实现方法打上注解就可以实现记录日志。缺点是服务接口太多的话,每一个接口都需要打上注解,比较繁琐。
定义注解
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
String value() default "";
}
注解解析
import com.alibaba.fastjson.JSON;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Aspect
@Order(5)
@Component
public class LogAspect {
/**
* 记录开始时间
*/
private ThreadLocal<Long> startTime = new ThreadLocal<>();
/**
* LOGGER
*/
private static final Logger LOGGER = LoggerFactory.getLogger(LogAspect.class);
@Pointcut("@annotation(xx.Log)")
public void log(){}
@Before("log()")
public void doBefore(JoinPoint joinPoint) {
long current = System.currentTimeMillis();
startTime.set(current);
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
LogUtil.info(LOGGER, "方法名称:", method.getName(), "method parameter:", method.getParameterTypes(), ",start time:", current);
}
@AfterReturning(returning="returnValue", pointcut = "log()")
public void doAfterReturning(Object returnValue) {
long gap = System.currentTimeMillis() - startTime.get();
LogUtil.info(LOGGER, "returnValue:" + JSON.toJSONString(returnValue), ",gap:", gap);
}
}
2.2 AOP的方式
通过AOP的方式来实现,对集成层服务接口的包定义切点切面,当调用服务时实际会调用的代理对象的方法,可以在前置和后置方法中实现日志的记录。AOP的方式的优点是粒度较粗,可以直接对包下面的所有服务全部记录日志。缺点是不能实现细粒度的日志记录。
切面方式
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
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.junit.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Aspect
@Order(5)
@Component
public class LogAspect {
private ThreadLocal<Long> startTime = new ThreadLocal<>();
/**
* LOGGER
*/
private static final Logger LOGGER = LoggerFactory.getLogger(LogAspect.class);
//包及其子包执行
@Pointcut("execution(* com.package..*.*(..))")
public void log(){}
@Before("log()")
public void doBefore(JoinPoint joinPoint) {
startTime.set(System.currentTimeMillis());
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
Log mylog = method.getAnnotation(Log.class);
}
@Around("log()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
}
@AfterReturning(returning="returnValue", pointcut = "log()")
public void doAfterReturning(Object returnValue) {
long gap = System.currentTimeMillis() - startTime.get();
System.out.println("returnValue:" + returnValue);
}
}
2.3 xml配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"
default-autowire="byName">
<!--开启注解扫描-->
<context:component-scan base-package="com.package.xx"/>
<!--开启AOP自动代理,默认为false-->
<aop:aspectj-autoproxy />
</beans>