首先创建一个SpringBoot项目,项目目录如下
引入AOP依赖,以及lombok日志依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.22</version>
</dependency>
<!--aop-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
先自定义一个注解LoggerProfile用于记录日志
@Target({ElementType.PARAMETER,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LoggerProfile {
String description() default "";
}
定义一个注解拦截器LoggerProfileInterceptor,此拦截器将@LoggerProfile注解的内容进行拦截,然后获取用户的信息以及通过反射获取方法的信息,将这些信息存储到对应的表中(本次我们只通过日志来进行打印,并不涉及表的操作),本次AOP的操作只执行了两个,一个是方法执行后的操作记录以及方法抛出异常后的处理。
@Slf4j
@Aspect
@Component
public class LoggerProfileInterceptor {
private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
/**
* 定义切入点
*/
@Pointcut("@annotation(cn.qblank.logdemo.annotion.LoggerProfile)")
public void controllerAspect(){
}
/**
* 执行日志
* 在执行操作执行
* 用于拦截Controller层记录用户操作
* @param joinPoint
*/
@After("controllerAspect()")
public void doBefore(JoinPoint joinPoint){
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String ip = request.getHeader("x-forwarded-for");
if (StringUtils.isEmpty(ip)){
ip = request.getRemoteAddr();
}
try {
log.info(ip + "在 {} 时访问了:{},url:{}" ,sdf.format(new Date()),getControllerMethodDescription(joinPoint),request.getRequestURI());
}catch (Exception e){
log.error("错误,{}",e);
}
}
/**
* 记录异常信息
* @param joinPoint
* @param e
*/
@AfterThrowing(pointcut = "controllerAspect()",throwing = "e")
public void doAfterThrowing(JoinPoint joinPoint,Throwable e){
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
//获取请求ip,由于服务器做了集群,使用了nginx反向代理所以request.getRemoteAddr并不能获取客户端真实地址,而是得到127.0.0.1
String ip = request.getHeader("x-forwarded-for");
if(StringUtils.isEmpty(ip)){
ip = request.getRemoteAddr();
}
try {
//目前是打印信息 项目中可以将这些操作存入日志表中
log.error("用户" + ip + "访问:" + request.getRequestURI() + "," + getControllerMethodDescription(joinPoint) + "时出现了异常," +
"异常代码:" + e.getClass() + ",异常信息:" + e.getMessage() + ",异常方法:"
+ joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()" +
"具体请查看日志");
}catch (Exception e2){
log.error("错误,{}",e2);
}
}
/**
* 获取注解中对方法的描述信息 用于Controller注解
* @param joinPoint
* @return
* @throws Exception
*/
public static String getControllerMethodDescription(JoinPoint joinPoint) throws Exception{
String targetName = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
Object[] arguments = joinPoint.getArgs();
Class<?> targetClass = Class.forName(targetName);
Method[] methods = targetClass.getMethods();
String description = "";
for (Method method:methods){
if (method.getName().equals(methodName)){
Class<?>[] clazzs = method.getParameterTypes();
if (clazzs.length == arguments.length){
//获取注解参数中的值
if (method.getAnnotation(LoggerProfile.class) != null){
description = method.getName() + "(" + method.getAnnotation(LoggerProfile.class).description() + ")";
}
break;
}
}
}
return description;
}
}
以上配置完成后,我们编写Controller方法
@Controller
@RequestMapping("/test/")
public class UserController {
@GetMapping("users")
@LoggerProfile(description = "用户列表")
@ResponseBody
public List<String> findUsers(){
List<String> list = new ArrayList<>();
list.add("张三");
list.add("里斯");
list.add("王五");
list.add("王麻子");
list.add("赵六");
return list;
}
@GetMapping("user")
@LoggerProfile(description = "查询单个列表")
@ResponseBody
public String findUserById(int id){
String name = "evan_qb";
return name;
}
@GetMapping("error")
@LoggerProfile(description = "错误测试")
@ResponseBody
public String error(){
int i = 1/0;
return "错误";
}
}
接下来我们进行测试:
首先访问第一个方法,记录如下:
访问第二个方法依然如此
当系统抛出异常时,例如访问第三个方法,则也将错误信息记录下来
怎么样,是不是非常方便呀
项目地址: https://gitee.com/evan_qb/logdemo