Custom annotation + AOP, elegant print method, accept and return parameter content

Keywords: Java log4j AOP



Write in front

After nearly two months, I finally completed the development of more than ten interfaces of a project site to the site SIT. In this process, I deeply realized the importance of adding logs (I never wanted to add logs before, but this time it really taught me a lesson).

Take an example of the credit occupation interface. In our system, when the front desk submits a transaction for approval, it will eventually send the corresponding data to the corresponding credit system through an interface (the interaction between the two systems actually involves the transfer platform). Before calling the credit interface, we will process the corresponding credit data of that business (the business process is also very complex), and finally add the parameters required by the interface (the parameters required by the opposite party's credit system) to the specified object, and then assemble the interface into message data in the specified format and send it to the opposite party.

There is a problem in this process. How can I ensure that the parameters I receive are correct? That is, when I get the value in the object according to the parameters required by the opposite credit system and get null, how can I locate whether the value in the object passed to me after the foreground credit processing is null (not transmitted after credit), or whether it becomes null after I get the value due to relevant logical operations?



Credit developers often print the objects sent to the log at the place where the relevant interface is called, so as to locate whether it is their value transfer problem or the logic problem on the interface.

It can only be said that the solution is to locate the developers of the credit function, but in case of production problems on site, the first person to find is often the developer of the interface (I have been found several times). At this time, because I don't understand the business logic related to credit, I can't know where they will print the objects passed to the interface, So I need to print the received parameter object myself.

But this person always forgets that every time there is a problem, I will think of why I didn't print the results at this position at the beginning?



To sum up: I want to print the contents of the parameters passed in by the method



There are three printing methods I can think of. There are other better methods. Please comment

First printing method

At the beginning of the method, directly call the relevant log.info(xxx) method

1. Define a logger variable

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

2. Specify location print parameters

logger.info("xxxx");




Second printing method

Direct extraction as a public method

1. Put the step of passing in the current class into the tool class

public static void logInfo(Class<?> clazz,Throwable e){
    LoggerFactory.getLogger(clazz).info(e.getMessage(),e);
}

2. Called directly within each method

LogUtils.logInfo(this.getClass(),"xxxx");




The third printing method

Because the previous way is too troublesome and not good-looking, I think of a solution. You only need to add a @ Log annotation on the class when using it, but this annotation can only print the accepted and returned objects. There is no way to print the Log in the business. The parsing object is Gson (I even thought about using a template method to give an extension interface to the corresponding parsing process. If I want to modify it, I can rewrite the corresponding method, but think about it. I need to talk about it later.)

At present, the functions of this annotation are:

  1. The value of the object received by the print method. When using, you only need to add the corresponding prefix string to locate the log record
  2. You can choose whether to print the returned object

I used a try catch to wrap my code, and the catch did not throw an exception, but just added an error log record. The reason is that I am afraid that the received parameter resolution will cause exceptions, which will affect the original normal business process

1. Customize a Log annotation

@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
	// Mark before log
	String[] logPreArr();

	// Do you want to print the returned object
	boolean isNeedReturn() default false;
}

2. Using Spring's AOP, write relevant cut in methods

@Aspect
@Component
public class LogAspect {

	// Configure weaving points
	@Pointcut("@annotation(pers.mobian.logannotation.annotation.Log)")
	public void logPointCut() {
	}

	@Before("logPointCut() ")
	public void beforeLog(JoinPoint point) throws Exception {
		// 1. Target class
		Class<?> aClass = point.getTarget().getClass();
		Logger log = LoggerFactory.getLogger(aClass);
		try {
			// 2. Target method
			Log logAnno = getAnnotationLogByPoint(point);
			if (logAnno != null) {
				// 3. Print target parameters
				Object[] args = point.getArgs();
				Gson gson = new Gson();
				String[] value = logAnno.logPreArr();
				if (value.length == 1) {
					for (Object arg : args) {
						log.info(point.getSignature().getName() + "method:" + value[0] + ": " + gson.toJson(arg));
					}
				} else if (value.length == args.length) {
					for (int i = 0; i < args.length; i++) {
						log.info(point.getSignature().getName() + "method:" + value[i] + ": " + gson.toJson(args[i]));
					}
				} else {
					log.info(point.getSignature().getName() + "method:" + "Parameter type and Log The number of annotations specified does not match" + gson.toJson(args));
				}
			}
		} catch (Exception e) {
			log.error(point.getSignature().getName() + "method: Log Exception in annotation parsing:" + e.getMessage());
		}
	}

	@AfterReturning(pointcut = "logPointCut() ", returning = "jsonResult")
	public void doAfterReturning(JoinPoint joinPoint, Object jsonResult) throws Exception {
		Class<?> aClass = joinPoint.getTarget().getClass();
		Logger log = LoggerFactory.getLogger(aClass);
		try {
			Log logAnno = getAnnotationLogByPoint(joinPoint);
			if (logAnno != null) {
				boolean needReturn = logAnno.isNeedReturn();
				if (needReturn) {
					log.info(joinPoint.getSignature().getName() + "method:" + "Return result:" + jsonResult);
				}
			}
		} catch (Exception e) {
			log.error(joinPoint.getSignature().getName() + "method: Log Exception in annotation parsing:" + e.getMessage());
		}
	}
    
	private Log getAnnotationLogByPoint(JoinPoint joinPoint) throws Exception {
		Signature signature = joinPoint.getSignature();
		MethodSignature methodSignature = (MethodSignature) signature;
		Method method = methodSignature.getMethod();
		if (method != null) {
			return method.getAnnotation(Log.class);
		}
		return null;
	}
}

3. Written test method

@Log(logPreArr = {"Test method 4"},isNeedReturn = true)
public Map<String, Object> show4(Teacher student) {
    Map<String, Object>  dataMap = new HashMap<>();
    dataMap.put("first",1);
    dataMap.put("the second","2");
    dataMap.put("Third",true);
    dataMap.put("Fourth",student);

    Map<String, Object>  returnMap = new HashMap<>();
    returnMap.put("first",1);
    returnMap.put("the second","2");
    returnMap.put("Third",true);
    returnMap.put("Fourth",student);
    returnMap.put("Fifth",dataMap);
    return returnMap;
}

@Log(logPreArr = {"Received teacher entity class", "Received student entity class"})
public String show5(Teacher teacher, Student student) {
    return student.toString();
}

Print results:

Posted by tomfra on Sun, 21 Nov 2021 13:05:16 -0800