java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Springboot使用@Aspect、自定义注解记录日志

Springboot使用@Aspect、自定义注解记录日志方式

作者:月半花开

本文介绍了如何使用AOP技术进行日志记录,包括扫描包和自定义注解两种方式,扫描包方式通过添加依赖和配置切入点表达式实现,自定义注解方式则通过创建自定义注解和AOP切面类来实现方法执行时间的统计和记录

1、前言

日志的作用不言而喻,协助运维故障排查,问题分析,数据统计,记录查询等。

日志各位大佬记录都会,但是规范有效记录日志的很少,本文为工具类文章,开箱即用,直接导入项目即可,CV大法一键搞定

2、切面方法说明

AOP五种通知工作

3、AOP日志记录方式

aop解决的这个办法有很多种,这里介绍两种最简单、最常用的

3.1、扫描包的方式

1)添加依赖

<--AOP的依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<--JSON的依赖-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.79</version>
</dependency>
<--mysql-data-jpa的依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<--lombok的依赖-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
/**   
 * Copyright © 2022 eSunny Info. Tech Ltd. All rights reserved.
 * 功能描述:
 * @Title: WebLogAspect.java  
 * @Package com.police.violation.aop  
 * @Description: TODO  
 * @author Administrator   
 * @date 2022年8月31日 上午9:48:11  
 * @version  
 */

package com.police.violation.aop;

import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
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;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import com.google.gson.Gson;

/**
 * @author www.exception.site (exception 教程网)
 * @date 2019/2/12
 * @time 14:03
 * @discription
 **/
@Aspect
@Component
public class WebLogAspect {

	private final static Logger logger = LoggerFactory.getLogger(WebLogAspect.class);

	/** 以 controller 包下定义的所有请求为切入点 */
	@Pointcut("execution(public * com.police.violation.controller..*.*Ticket(..))")
	public void webLog() {
	}

	/**
	 * 在切点之前织入
	 * 
	 * @param joinPoint
	 * @throws Throwable
	 */
	@Before("webLog()")
	public void doBefore(JoinPoint joinPoint) throws Throwable {
		// 开始打印请求日志
		ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		HttpServletRequest request = attributes.getRequest();

		// 打印请求相关参数
		logger.info("========================================== Start ==========================================");
		// 打印请求 url
		logger.info("URL            : {}", request.getRequestURL().toString());
		// 打印 Http method
		logger.info("HTTP Method    : {}", request.getMethod());
		// 打印调用 controller 的全路径以及执行方法
		logger.info("Class Method   : {}.{}", joinPoint.getSignature().getDeclaringTypeName(),
				joinPoint.getSignature().getName());
		// 打印请求的 IP
		logger.info("IP             : {}", request.getRemoteAddr());
		// 打印请求入参
		logger.info("Request Args   : {}", new Gson().toJson(joinPoint.getArgs()));
	}

	/**
	 * 在切点之后织入
	 * 
	 * @throws Throwable
	 */
	@After("webLog()")
	public void doAfter() throws Throwable {
		logger.info("=========================================== End ===========================================");
		// 每个请求之间空一行
		logger.info("");
	}

	/**
	 * 环绕
	 * 
	 * @param proceedingJoinPoint
	 * @return
	 * @throws Throwable
	 */
    @Around("webLog()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = proceedingJoinPoint.proceed();
        // 打印出参
        logger.info("Response Args  : {}", new Gson().toJson(result));
        // 执行耗时
        logger.info("Time-Consuming : {} ms", System.currentTimeMillis() - startTime);
        return result;
    }


	/**
	 *
	 * @param proceedingJoinPoint 切面
	 * @return
	 * @throws Throwable
	 */
//	@Around("webLog()")
//	public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
//		long start = System.currentTimeMillis();
//		Object result = proceedingJoinPoint.proceed();
//		logger.info("Request Params       : {}", getRequestParams(proceedingJoinPoint));
//		logger.info("Result               : {}", result);
//		logger.info("Time Cost            : {} ms", System.currentTimeMillis() - start);
//
//		return result;
//	}

	/**
	 * 获取入参
	 * 
	 * @param proceedingJoinPoint
	 *
	 * @return
	 */
	private Map<String, Object> getRequestParams(ProceedingJoinPoint proceedingJoinPoint) {
		Map<String, Object> requestParams = new HashMap<>();

		// 参数名
		String[] paramNames = ((MethodSignature) proceedingJoinPoint.getSignature()).getParameterNames();
		// 参数值
		Object[] paramValues = proceedingJoinPoint.getArgs();

		for (int i = 0; i < paramNames.length; i++) {
			Object value = paramValues[i];

			// 如果是文件对象
			if (value instanceof MultipartFile) {
				MultipartFile file = (MultipartFile) value;
				// 获取文件名
				value = file.getOriginalFilename();
			}

			requestParams.put(paramNames[i], value);
		}

		return requestParams;
	}

}

3.2、自定义注解方式

3.2.1.Maven依赖

<!--引入AOP依赖-->
 <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
 </dependency>

3.2.2. 自定义注解

package com.ruoyi.aspect;

import java.lang.annotation.*;
 
/**
 * 统计耗时
 */
@Documented //用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化.Documented是一个标记注解,没有成员.
@Target(ElementType.METHOD) //指定被修饰的Annotation可以放置的位置(被修饰的目标)类,方法,属性
@Retention(RetentionPolicy.RUNTIME) //定义注解的保留策略, RetentionPolicy.RUNTIME:注解会在class字节码文件中存在,在运行时可以通过反射获取到
public @interface TakeTime {
 
    String methodName() default "";
}

3.2.3. TakeTimeAspect(使用AOP技术统计方法执行前后消耗时间)

package com.ruoyi.aspect;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
 
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
 
/**
 * 耗时统计
 */
@Slf4j
@Aspect
@Component
public class TakeTimeAspect {
    //统计请求的处理时间
    ThreadLocal<Long> startTime = new ThreadLocal<>();
    ThreadLocal<Long> endTime = new ThreadLocal<>();
 
    /**
     * 带有@TakeTime注解的方法
     */
//    @Pointcut("within(com.lwx.backend.user.controller.*)")
//    @Pointcut("execution(* com.lwx.backend.user.controller.*.*(..))")
    @Pointcut("@annotation(com.ruoyi.aspect.TakeTime)")
    public void TakeTime() {
 
    }
 
    //    @Before("within(com.lwx.backend.user.controller.*)")
    @Before("TakeTime()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        // 获取方法的名称
        String methodName = joinPoint.getSignature().getName();
        // 获取方法入参
        Object[] param = joinPoint.getArgs();
        StringBuilder sb = new StringBuilder();
        for (Object o : param) {
            sb.append(o + ";");
        }
        log.info("进入《{}》 方法,参数为: {}", methodName,sb.toString());
 
        System.out.println("System.currentTimeMillis(): "+System.currentTimeMillis());
        System.out.println("new Date(): "+new Date());
        startTime.set(System.currentTimeMillis());
        log.info("方法开始时间:" +startTime.get());
        //接收到请求,记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
 
        //记录请求的内容
        log.info("请求URL:" + request.getRequestURL().toString());
        log.info("请求METHOD:" + request.getMethod());
    }
 
    //    @AfterReturning(returning = "ret", pointcut = "within(com.lwx.backend.user.controller.*)")
    @AfterReturning(returning = "ret", pointcut = "TakeTime()")
    public void doAfterReturning(Object ret) {
        //处理完请求后,返回内容
        log.info("方法返回值:" + JSON.toJSONString(ret));
        endTime.set(System.currentTimeMillis());
        log.info("方法结束时间" +endTime.get());
        log.info("方法结束时间" +new Date());
        log.info("方法执行时间:" + (endTime.get() - startTime.get()));
    }
}

3.2.4. 在接口方法上加上注解

@RequestMapping("/loadForTestVariableCategories")
@TakeTime(methodName = "loadForTestVariableCategories")
public void loadForTestVariableCategories(HttpServletRequest req, HttpServletResponse resp) throws Exception {
   KnowledgeBase knowledgeBase = buildKnowledgeBase(req);
   List<VariableCategory> vcs=knowledgeBase.getResourceLibrary().getVariableCategories();
   httpSessionKnowledgeCache.put(req, RuleConstant.VCS_KEY, vcs);
   writeObjectToJson(resp, vcs);
}

3.2.5. 打印查看接口耗时

2022-07-30 22:40:47.057 INFO 16276 --- [nio-8080-exec-2] com.lwx.common.aspect.TakeTimeAspect : 进入《queryUserList》 方法,参数为: {pageNum=1, pageSize=10, userName=张三};
System.currentTimeMillis(): 1659192047058
new Date(): Sat Jul 30 22:40:47 CST 2022
2022-07-30 22:40:47.058 INFO 16276 --- [nio-8080-exec-2] com.lwx.common.aspect.TakeTimeAspect : 方法开始时间:1659192047058
2022-07-30 22:40:47.059 INFO 16276 --- [nio-8080-exec-2] com.lwx.common.aspect.TakeTimeAspect : 请求URL:http://localhost:8080/user/queryUserList
2022-07-30 22:40:47.059 INFO 16276 --- [nio-8080-exec-2] com.lwx.common.aspect.TakeTimeAspect : 请求METHOD:POST
com.lwx.common.aspect.TakeTimeAspect : 方法返回值:{"data":{"endRow":1,"hasNextPage":false,"hasPreviousPage":false,"isFirstPage":true,"isLastPage":true,"list":[{"comment":"男","userAddress":"广东","userAge":20,"userBirth":1640966400000,"userId":1,"userName":"张三","userSex":"男"}],"navigateFirstPage":1,"navigateLastPage":1,"navigatePages":8,"navigatepageNums":[1],"nextPage":0,"pageNum":1,"pageSize":10,"pages":1,"prePage":0,"size":1,"startRow":1,"total":1},"message":"success","status":100,"timestamp":1659192047819}
2022-07-30 22:40:47.846 INFO 16276 --- [nio-8080-exec-2] com.lwx.common.aspect.TakeTimeAspect : 方法结束时间1659192047846
2022-07-30 22:40:47.846 INFO 16276 --- [nio-8080-exec-2] com.lwx.common.aspect.TakeTimeAspect : 方法结束时间Sat Jul 30 22:40:47 CST 2022
2022-07-30 22:40:47.846 INFO 16276 --- [nio-8080-exec-2] com.lwx.common.aspect.TakeTimeAspect : 方法执行时间:788

注:

@SpringBootApplication(scanBasePackages {"com.Lwx.backend","com.Lwx.common"})

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文