preface

In real projects, especially in management systems, we often log important operations. For example, for CRUD operations on a database, we log every important operation, usually by inserting a record into the database specified log table. This raises the question, do we need to manually insert logging every time we are in CRUD? This is certainly not appropriate, such operations will undoubtedly increase the amount of development, and is not easy to maintain, so the actual project always use AOP(Aspect Oriented Programming), that is, the Aspect Oriented Programming technology to record the operation log in the system.

This article appeared on my personal blog: [www.xiongfrblog.cn]

Classification of log

Here I divide logs into two types according to their object orientation:

  • User-oriented logs: Users are the people who use the system. This type of log is usually recorded in the database, and usually records some information about the databaseCRUDOperation.
  • Developer-oriented logs: This type of log is usually viewed by developers, and is usually stored in a file or printed on the console (at the console during development and in a file after the project goes live). This type of log is used by developers to locate errors during development and maintenance.

We adopt different logging strategies for different objects. It is easy to see that there is a lot of flexibility with user-facing logging and that the developer needs to control which user actions need to be logged to the database, so we use custom annotations to match these types of logs stored in the database when using AOP records. For developer logs, we use expressions to match. (This may be a little vague, but the following examples will make it clear.)

Implement AOP for user-oriented logging

The next step is how to implement logging operations using AOP in Spring Boot.

Add the dependent

Add the following dependencies to the pom.xml file:

<! -- AOP dependencies -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
Copy the code

Modifying a Configuration File

Add the following configuration sentence to the project’s application.properties file:

spring.aop.auto=true
Copy the code

The default value is true, as long as we add a dependency to the pom.xml, and we mention it to let you know that it has this configuration.

Custom annotations

As we saw above, because this type of logging is flexible, we need to customize an Annotation, so when we use it, we need to add this Annotation to the method that needs to be logged. First, create a new config package under the launch class’s peer package, and then create a new Annotation file called Log under the launch class, The contents of the file are as follows:

package com.web.springbootaoplog.config;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/ * * *@author Promise
* @createTimeDecember 18, 2018 at 9:26:25 PM *@descriptionDefine a method level@logNote * /
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
	String value(a) default "";
}
Copy the code

Here is the use of Java meta-annotations related knowledge, not clear about the concept of friends can go to this blog get [portal].

Prepare database log tables as well as entity classes, SQL interfaces, and XML files

Since it is to insert records into the database, so the prerequisite is to create a log table, the following table SQL, because is to write a sample, I here this table design is very simple, you can design.

CREATE TABLE `sys_log` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'primary key',
  `user_id` int(11) NOT NULL COMMENT 'Operator ID',
  `user_action` varchar(255) NOT NULL COMMENT 'User Action',
  `create_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT 'Creation time',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8 COMMENT='Log Form';
Copy the code

Generate entity classes, SQL interface files, and XML files using MBG, which was introduced in the last blog.

We need to create the service interface file and the interface implementation class.

ISysLogServcie.java

package com.web.springbootaoplog.service;

import com.web.springbootaoplog.entity.SysLog;

/ * * *@author Promise
* @createTimeDecember 18, 2018 at 9:29:48 PM *@descriptionLog interface */
public interface ISysLogService {

	/** * Insert log *@param entity
	 * @return* /
	int insertLog(SysLog entity);
}

Copy the code

SysLogServiceImpl.java

package com.web.springbootaoplog.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.web.springbootaoplog.config.Log;
import com.web.springbootaoplog.dao.SysLogMapper;
import com.web.springbootaoplog.entity.SysLog;
import com.web.springbootaoplog.service.ISysLogService;


/ * * *@author Promise
* @createTimeDecember 18, 2018 9:30:57 PM *@description* /
@Service("sysLogService")
public class SysLogServiceImpl implements ISysLogService{

	@Autowired
	private SysLogMapper sysLogMapper;
	
	@Override
	public int insertLog(SysLog entity) {
		// TODO Auto-generated method stub
		returnsysLogMapper.insert(entity); }}Copy the code

Aspects and pointcuts of AOP

With the above documentation in place, let’s get to the point of creating an AOP aspect implementation class. Again, we’ll put this class under the config package, named Logaspect.java, and it will read as follows:

package com.web.springbootaoplog.config;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
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.hibernate.validator.internal.util.logging.LoggerFactory;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.stereotype.Component;

import com.web.springbootaoplog.entity.SysLog;
import com.web.springbootaoplog.service.ISysLogService;


/ * * *@author Promise
* @createTimeDecember 18, 2018 at 9:33:28 *@descriptionAspect log configuration */
@Aspect
@Component
public class LogAsPect {
	
	private final static Logger log = org.slf4j.LoggerFactory.getLogger(LogAsPect.class);

	@Autowired
	private ISysLogService sysLogService;
	
	// matches methods with custom annotations
	@Pointcut("@annotation(com.web.springbootaoplog.config.Log)")
	public void pointcut(a) {}
	
	@Around("pointcut()")
	public Object around(ProceedingJoinPoint point) {
		Object result =null;
		long beginTime = System.currentTimeMillis();
		
		try {
		    log.info("I execute before the target method!");
			result = point.proceed();
			long endTime = System.currentTimeMillis();
			insertLog(point,endTime-beginTime);
		} catch (Throwable e) {
			// TODO Auto-generated catch block
		}
		return result;
	}
	
	private void insertLog(ProceedingJoinPoint point ,long time) {
		MethodSignature signature = (MethodSignature)point.getSignature();
		Method method = signature.getMethod();
		SysLog sys_log = new SysLog();
		
		Log userAction = method.getAnnotation(Log.class);
		if(userAction ! =null) {
			// The description on the annotation
			sys_log.setUserAction(userAction.value());
		}
		
		// The requested class name
		String className = point.getTarget().getClass().getName();
		// The requested method name
		String methodName = signature.getName();
		// Request method parameter values
		String args = Arrays.toString(point.getArgs());
		
		// Get the id of the current user from session
// Long useride = (Long)SecurityUtils.getSubject().getSession().getAttribute("userid");
		
		Long userid = 1L;// We should get the id of the current logon from the session
		
		sys_log.setUserId(userid);
		
		sys_log.setCreateTime(new java.sql.Timestamp(new Date().getTime()));
		
		log.info("Current login person :{}, class name :{}, method name :{}, argument :{}, execution time :{}",userid, className, methodName, args, time); sysLogService.insertLog(sys_log); }}Copy the code

Here are a few important comments about AOP:

  • @AspectThis annotation indicates that the current class is treated as an aspect class
  • @Component: indicates that the current class is submitted toSpringManagement.
  • @Pointcut: the pointcut expression that defines our matching rule, which we used above@Pointcut("@annotation(com.web.springbootaoplog.config.Log)")Means to match the method with our custom annotations.
  • @Around: Wraps around notifications that can be performed before and after the target method executes, as well as when the target method throws an exception.

These are just a few of the annotations we use, but there are many others that I won’t go over here. For those who want to learn more about AOP, you can visit the official documentation portal.

Here’s a key piece of code:

log.info("I execute before the target method!");
result = point.proceed();
long endTime = System.currentTimeMillis();
insertLog(point,endTime-beginTime);
Copy the code

Where result = point.proceed(); This means that the target method is executed, so you can see that we printed a log before this code executes, and after that we called insertLog() to insert the log method, and in that method we can get important information about the class name, method name, parameters, and so on.

Test controller

Create a new homecontroller.java (whatever name you want) in the Controller package that reads:

package com.web.springbootaoplog.controller;

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

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.web.springbootaoplog.config.Log;
import com.web.springbootaoplog.entity.SysLog;
import com.web.springbootaoplog.service.ISysLogService;

/ * * *@author Promise
* @createTimeJanuary 2, 2019 10:35:30 PM *@descriptionTest controller * /
@Controller
public class HomeController {

    private final static Logger log = org.slf4j.LoggerFactory.getLogger(HomeController.class);
	
	@Autowired
	private ISysLogService logService;

	@RequestMapping("/aop")
	@ResponseBody
	@Log("Test aoplog")
	public Object aop(String name, String nick) {
		Map<String, Object> map =new HashMap<>();
		log.info("I've been executed!);
		map.put("res"."ok");
		returnmap; }}Copy the code

Define a test method with two parameters, and add our custom @log annotation for that method, start the project, and the browser accesses localhost:8080/ AOP? Name =xfr&nick=eran;

The 22:02:17 2019-01-24. 3832-682 the INFO [nio - 8080 - exec - 1] c.w eb. Springbootaoplog. Config. LogAsPect: I performed before the target method! The 2019-01-24 22:02:17. 3832-688 the INFO [nio - 8080 - exec - 1] C.W.S.C ontroller. HomeController: I was carried out. The 2019-01-24 22:02:17. 3832-689 the INFO [nio - 8080 - exec - 1] c.w eb. Springbootaoplog. Config. LogAsPect: Current login: 1, the name of the class: com. Web. Springbootaoplog. Controller. The HomeController, method name: aop, parameters: [XFR, eran], the execution time: 6Copy the code

You can see that we have successfully inserted some logic before and after the target method executes. Now look at the data in the database:

A data entry was successfully logged.

Implement AOP for developer – oriented logging

First of all, HERE I list an application scenario using this method. There is a bug in the project, and we want to know whether the foreground request has entered our controller, and how to obtain the parameters. Now let’s introduce the implementation steps.

In fact, the principle is the same as above, but the matching rules of the pointcut have changed, and the log is not recorded to the database, it can be printed out.

First define a new pointcut expression in logaspect.java, as follows:

@Pointcut("execution(public * com.web.springbootaoplog.controller.. *. * (..) )")
public void pointcutController(a) {}

Copy the code

@Pointcut(“execution(public * com.web.springbootaoplog.controller.. *. * (..) ) “) means to match com. Web. Springbootaoplog. Under the controller bags and bags of all public methods.

The detailed use of this expression can be seen here, portal.

To add a match to a method:

@Before("pointcutController()")
public void around2(JoinPoint point) {
	// Get the target method
	String methodNam = point.getSignature().getDeclaringTypeName() + "." + point.getSignature().getName();
	
	// Get the method parameters
	String params = Arrays.toString(point.getArgs());
	
	log.info("get in {} params :{}",methodNam,params);
}
Copy the code

@before: represents the contents of the following method body Before the target method executes.

Add another test method to the controller:

@RequestMapping("/testaop3")
@ResponseBody
public Object testAop3(String name, String nick) {
	Map<String, Object> map = new HashMap<>();
	
	map.put("res"."ok");
	return map;
}
Copy the code

You can see that this method is not annotated with @log, restart the project, the browser goes to localhost:8080/testaop3? Name =xfr&nick=eran;

The 2019-01-24 23:19:49. 884-108 the INFO [nio - 8080 - exec - 1] c.w eb. Springbootaoplog. Config. LogAsPect: a getin com.web.springbootaoplog.controller.HomeController.testAop3 params :[xfr, eran]
Copy the code

The key log is printed out, so we can know whether the method is entered, whether the parameter is obtained correctly, and other key information.

Some of you may be wondering if this repeats the @log method, and it does, so I usually use @log annotation on Service layer methods in projects, which makes more sense.

conclusion

Well, that’s enough about Aop logging. I’ll see you in the next blog. bye~