Most projects now log or save logs, and in this era of big data, data has become a very, very important resource.

Journaling is also very useful, so don’t underestimate it. 😁

Like a sentence: “eight hours for life, eight hours for development”.

If you like it, let's stick to it!!

‘😁

We: by the time we meet on another day, we have achieved something

One, foreword

The SpringBoot version used in this article is 2.5.2

1) Overview:

Log: Network devices, systems, and service programs generate an event record called log during operation. Each line of the log contains a description of the date, time, user, and action.

2) Introduction:

Windows network operating systems are designed to have various log files, such as application logs, security logs, system logs, Scheduler service logs, FTP logs, WWW logs, DNS server logs, and so on, depending on which services are enabled on your system.

This article focuses more on behavior logging than system logging.

When we do something on the system, these log files usually record something about the operation that may not be useful to us, but is very useful to the system security staff.

For example, when someone probes the system for IPC, the system will quickly record the IP address, time, and user name used in the probe in the security log. After FTP probe, the system will record the IP address, time, and user name used in the probe in the FTP log.

3) Usage Scenarios:

Simply introduce a few ~~ (I still dish a lot of don’t know, dog head protect life πŸ˜‚) ~~

  1. Check for bugs and see where the error occurred in the log
  2. Remote login. (The login log will record your Ip address)

By the way, this article is more about providing a method, ideas, and a complete case study to give you a sense of springboot-annotation Aop logging

Two, preliminary preparation

Case study:

The visitor’s information is written to the database using SpringBoot’s Aop approach.

Project Structure:

Note: Because used to use MybatisPlus, took the previous complete configuration, so it looks like Java files, but about the log is not complicated, the code also with annotations, please rest assured to eat.

Those interested in MybatisPlus can go to πŸ‘‰SpringBoot to integrate MybatisPlus

2.1 database

Tb_user table

CREATE TABLE `tb_user`  (
  `id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `passwrod` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `deleted` int(1) NOT NULL DEFAULT 0,
  `create_time` datetime(0) NOT NULL COMMENT 'Creation time',
  `update_time` datetime(0) NOT NULL COMMENT 'Modification time'.PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

INSERT INTO `tb_user` VALUES ('1'.'Prefer spring'.'123456'.0.'the 2021-07-23 14:32:46'.'the 2021-07-29 23:56:10');
INSERT INTO `tb_user` VALUES ('2'.'Green chestnut'.'qwerasd'.0.'the 2021-07-23 15:02:02'.'the 2021-07-23 15:49:55');
Copy the code

Tb_log table

DROP TABLE IF EXISTS `tb_log`;
CREATE TABLE `tb_log`  (
  `id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `user_id` int(10) NOT NULL,
  `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `login_ip` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `type` int(10) NOT NULL,
  `url` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `operation` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `create_time` datetime(0) NULL DEFAULT NULL,
  `remark` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT ' ',
  `update_time` datetime(0) NULL DEFAULT NULL.PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

INSERT INTO `tb_log` VALUES ('e5b49465-b20a-453f-b15c-b284733f2f8e'.1.'Prefer spring'.'0:0:0:0:0:0:0:1'.1.'127.0.0.1'.'Query user Information'.'the 2021-08-15 01:04:31'.' '.'the 2021-08-15 01:04:31');
Copy the code

2.2. Import dependencies

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.5.2</version>
    <relativePath/> <! -- lookup parent from repository -->
</parent>
<dependencies>
    <! Spring aop dependencies -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.4.1 track</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>1.2.6</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.72</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.23</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>5.6.5</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>
Copy the code

Dependencies are commonly used ha, nothing to say ha. πŸ˜€

2.3. Yml Configuration file

server:
  port: 8091
spring:
  application:
    name: springboot-log
  # data source configuration
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    # Ali database connection pool
    druid:
      username: root
      password: 123456
      url: jdbc:mysql://localhost:3306/commons_utils? serverTimezone=UTC&useSSL=false&characterEncoding=utf8&serverTimezone=GMT
      Select * from database where id = 5;
      initial-size: 5
      # Minimum number of connections (permanent 10 connections)
      min-idle: 10
      # Maximum number of connections (up to 10 connections, more than 10 database will enter a blocking state, waiting for other connections to be released)
      max-active: 20
      Get the maximum connection wait time in milliseconds
      max-wait: 10000
      Configure how often to detect idle connections that need to be closed, in milliseconds
      timeBetweenEvictionRunsMillis: 60000
      Set the minimum time for a connection to live in the pool in milliseconds
      minEvictableIdleTimeMillis: 300000
      Set the maximum number of milliseconds for a connection to live in the pool
      maxEvictableIdleTimeMillis: 900000
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8
mybatis-plus:
  configuration:
    cache-enabled: true # enable cache
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # enable SQL logging
  mapper-locations: classpath:/mapper/*Mapper.xml
  global-config:
    db-config:
      logic-delete-field: flag  # delete entity field name (since 3.3.0)
      logic-delete-value: 1 # Logical deleted value (default: 1)
      logic-not-delete-value: 0 # Logical undeleted value (default: 0)
Copy the code

2.3. Configure the custom log annotation class

If you need to collect more than one log, you can do the extension, add annotations, code can also be used, of course, if there are many items, then it is most appropriate to extract.

(experience inadequacy, if have wrong, please put forward in time, crab crab each big guy 😁)

/** * Configure the custom log annotation class *@author crush
 */
@Target(ElementType.METHOD) METHOD is annotable at the METHOD level
@Retention(RetentionPolicy.RUNTIME) // At which stage annotations are executed
@Documented // Generate a document
public @interface MyLog {
    /** Operation event */
    String operation (a) default "";

    /** Log type */
    int type (a);
}
Copy the code

2.4, SysLogAspect: section processing class

import cn.hutool.core.lang.UUID;
import com.crush.log.annotation.MyLog;
import com.crush.log.entity.LogOperation;
import com.crush.log.entity.LogUser;
import com.crush.log.mapper.LogOperationMapper;
import com.crush.log.utils.IpUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.aspectj.lang.JoinPoint;
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.springframework.beans.factory.annotation.Autowired;
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.lang.reflect.Method;

/** System log: section processing class */
@Aspect
@Component
public class SysLogAspect {
	/** I use log4j2 to print some information on the console, so I don't need to write */
    private static final Logger log = LogManager.getLogger(SysLogAspect.class);

	/** Operate database */
    @Autowired
    private LogOperationMapper logOperationMapper;

    /** * define the pointcut@PointcutThe annotation is written on that method and that method is cut in. * /
    @Pointcut("@annotation(com.crush.log.annotation.MyLog)")
    public void logPoinCut(a) {}// Configure notification in the aspect
    @Before("logPoinCut()")         //AfterReturning
    public void saveOperation(JoinPoint joinPoint) {
        log.info("--------------- Interface logging ---------------");
        // Used to save logs
        LogOperation logOperation = new LogOperation();

        // Here is the request to get the current request
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = servletRequestAttributes.getRequest();

        String requestURL = request.getRequestURL().toString();
        logOperation.setUrl(requestURL);

        // The client IP address can also be compared with the previous, if different, push messages to him, such as remote login and so on.
        String ip = IpUtils.getIpAddr(request);
        logOperation.setLoginIp(ip);

        // The method of getting the texture point from the section by reflection mechanism
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();

        // Get the method where the pointcut is located
        Method method = signature.getMethod();

        // Get the value of Log on the method
        MyLog myLog = method.getAnnotation(MyLog.class);
        if(myLog ! =null) {
            // Save the action event
            String operation = myLog.operation();
            logOperation.setOperation(operation);

            // Save log type you can also make extensions here depending on the type, you can do different things
            int type = myLog.type();
            logOperation.setType(type);

            log.info("operation="+operation+",type="+type);
        }

        // Operator account and name (the user information needs to be saved in session in advance)
        // Use a session because this is a simulation
        // Security is actually used to retrieve information about the current authorization object, not from the session
        // Also from redis, this is just a way of thinking, please forgive me
        LogUser user = (LogUser) request.getSession().getAttribute("user");
        if(user ! =null) {
            String userId = user.getId();
            String userName = user.getUsername();
            logOperation.setUserId(userId);
            logOperation.setUsername(userName);
            System.out.println(user);
        }
        log.info("url="+requestURL,"ip="+ip);
       
        // Call service to save the Operation entity class to the database
        // my id uses the UUID. If I don't need it, I can comment it out
        String id = UUID.randomUUID().toString().replace("-".""); logOperation.setId(id); logOperationMapper.insert(logOperation); }}Copy the code

2.5. MybatisPlus configuration classes

MybatisPlusConfig

/ * * *@EnableTransactionManagement: Start transaction *@Author: crush
 * @Date: 2021-07-23 14:14
 * version 1.0
 */
@Configuration
@EnableTransactionManagement
@MapperScan("com.crush.log.mapper")
public class MybatisPlusConfig {

    Paging / * * * * /
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(a) {
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        // Register optimistic lock plug-in
        return mybatisPlusInterceptor;
    }

    Druid */
    @Bean
    @Primary
    @ConfigurationProperties("spring.datasource.druid")
    public DruidDataSource druidDataSource(a) {
        returnDruidDataSourceBuilder.create().build(); }}Copy the code

MyMetaObjectHandler: Automatically populate

/** * Fill in the create and modify time *@Author: crush
 * @Date: when the 2021-07-23 * /
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("start insert fill ....");
        this.setFieldValByName("createTime", LocalDateTime.now(),metaObject);
        this.setFieldValByName("updateTime",LocalDateTime.now(),metaObject);
    }
    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("start update fill ....");
        this.setFieldValByName("updateTime",LocalDateTime.now(),metaObject); }}Copy the code

LocalDateTime LocalDateTimeSerializerConfig: configure global formats

@Configuration
public class LocalDateTimeSerializerConfig {
    @Value("${spring.jackson.date-format}")
    private String DATE_TIME_PATTERN;
    
    @Value("${spring.jackson.date-format}")
    private  String DATE_PATTERN ;
    / * * * string localdate * /
    @Bean
    public Converter<String, LocalDate> localDateConverter(a) {
        return new Converter<String, LocalDate>() {
            @Override
            public LocalDate convert(String source) {
                if (source.trim().length() == 0) {
                    return null;
                }
                try {
                    return LocalDate.parse(source);
                } catch (Exception e) {
                    returnLocalDate.parse(source, DateTimeFormatter.ofPattern(DATE_PATTERN)); }}}; }/** * string to localDateTime */
    @Bean
    public Converter<String, LocalDateTime> localDateTimeConverter(a) {
        return new Converter<String, LocalDateTime>() {
            @Override
            public LocalDateTime convert(String source) {
                if (source.trim().length() == 0) {
                    return null;
                }
                // Try the ISO format: 2019-07-15T16:00:00
                try {
                    return LocalDateTime.parse(source);
                } catch (Exception e) {
                    returnLocalDateTime.parse(source, DateTimeFormatter.ofPattern(DATE_TIME_PATTERN)); }}}; }/** * set the LocalDateTime format */
    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer(a) {
        JavaTimeModule module = new JavaTimeModule();
        LocalDateTimeDeserializer localDateTimeDeserializer = new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        module.addDeserializer(LocalDateTime.class, localDateTimeDeserializer);
        return builder -> {
            builder.simpleDateFormat(DATE_TIME_PATTERN);
            builder.serializers(new LocalDateSerializer(DateTimeFormatter.ofPattern(DATE_PATTERN)));
            builder.serializers(new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DATE_TIME_PATTERN)));
            builder.modules(module); }; }}Copy the code

2.6, IpUtils

import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;

/** * Obtain the IP address */
public class IpUtils
{
    public static String getIpAddr(HttpServletRequest request)
    {
        if (request == null)
        {
            return "unknown";
        }
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
        {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
        {
            ip = request.getHeader("X-Forwarded-For");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
        {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
        {
            ip = request.getHeader("X-Real-IP");
        }

        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
        {
            ip = request.getRemoteAddr();
        }

        return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
    }

    public static boolean internalIp(String ip)
    {
        byte[] addr = textToNumericFormatV4(ip);
        return internalIp(addr) || "127.0.0.1".equals(ip);
    }

    private static boolean internalIp(byte[] addr)
    {
        if (addr == null || addr.length < 2)
        {
            return true;
        }
        final byte b0 = addr[0];
        final byte b1 = addr[1];
        // 10.x.x.x/8
        final byte SECTION_1 = 0x0A;
        / / 172.16 7.0.x.x / 12
        final byte SECTION_2 = (byte) 0xAC;
        final byte SECTION_3 = (byte) 0x10;
        final byte SECTION_4 = (byte) 0x1F;
        / / 192.168 7.0.x.x / 16
        final byte SECTION_5 = (byte) 0xC0;
        final byte SECTION_6 = (byte) 0xA8;
        switch (b0)
        {
            case SECTION_1:
                return true;
            case SECTION_2:
                if (b1 >= SECTION_3 && b1 <= SECTION_4)
                {
                    return true;
                }
            case SECTION_5:
                if (b1 == SECTION_6) {
                    return true;
                }
            default:
                return false; }}/** * Converts the IPv4 address to bytes **@paramText IPv4 address *@returnByte bytes * /
    public static byte[] textToNumericFormatV4(String text)
    {
        if (text.length() == 0)
        {
            return null;
        }

        byte[] bytes = new byte[4];
        String[] elements = text.split("\ \.", -1);
        try
        {
            long l;
            int i;
            switch (elements.length)
            {
                case 1:
                    l = Long.parseLong(elements[0]);
                    if ((l < 0L) || (l > 4294967295L))
                        return null;
                    bytes[0] = (byte) (int) (l >> 24 & 0xFF);
                    bytes[1] = (byte) (int) ((l & 0xFFFFFF) > >16 & 0xFF);
                    bytes[2] = (byte) (int) ((l & 0xFFFF) > >8 & 0xFF);
                    bytes[3] = (byte) (int) (l & 0xFF);
                    break;
                case 2:
                    l = Integer.parseInt(elements[0]);
                    if ((l < 0L) || (l > 255L))
                        return null;
                    bytes[0] = (byte) (int) (l & 0xFF);
                    l = Integer.parseInt(elements[1]);
                    if ((l < 0L) || (l > 16777215L))
                        return null;
                    bytes[1] = (byte) (int) (l >> 16 & 0xFF);
                    bytes[2] = (byte) (int) ((l & 0xFFFF) > >8 & 0xFF);
                    bytes[3] = (byte) (int) (l & 0xFF);
                    break;
                case 3:
                    for (i = 0; i < 2; ++i)
                    {
                        l = Integer.parseInt(elements[i]);
                        if ((l < 0L) || (l > 255L))
                            return null;
                        bytes[i] = (byte) (int) (l & 0xFF);
                    }
                    l = Integer.parseInt(elements[2]);
                    if ((l < 0L) || (l > 65535L))
                        return null;
                    bytes[2] = (byte) (int) (l >> 8 & 0xFF);
                    bytes[3] = (byte) (int) (l & 0xFF);
                    break;
                case 4:
                    for (i = 0; i < 4; ++i)
                    {
                        l = Integer.parseInt(elements[i]);
                        if ((l < 0L) || (l > 255L))
                            return null;
                        bytes[i] = (byte) (int) (l & 0xFF);
                    }
                    break;
                default:
                    return null; }}catch (NumberFormatException e)
        {
            return null;
        }
        return bytes;
    }

    public static String getHostIp(a)
    {
        try
        {
            return InetAddress.getLocalHost().getHostAddress();
        }
        catch (UnknownHostException ignored)
        {

        }
        return "127.0.0.1";
    }

    public static String getHostName(a)
    {
        try{
            return InetAddress.getLocalHost().getHostName();
        }
        catch (UnknownHostException ignored) {
        }
        return "Unknown"; }}Copy the code

Business code

I do not have the interface to write and view the log here, save the database, the administrator can view the information at any time, can also use the Web page, will be much more convenient.

1, the entity

LogUser

/ * * *@Author: crush
 * @Date: 2021-08-148:43 * Version 1.0 */
@Data
@Accessors(chain = true)
@TableName("tb_user")
public class LogUser implements Serializable {

    private static final long serialVersionUID = 1L;

    private String id;

    private String username;

    private String passwrod;

    /*** drop the field */
    @TableLogic
    private Integer deleted;

    /*** create time */
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;

    /*** Change the time */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
}
Copy the code
package com.crush.log.entity;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.Accessors;

import java.io.Serializable;
import java.time.LocalDateTime;

/** * log table *@author crush
 */
@Data
@Accessors(chain = true)
@TableName("tb_log")
public class LogOperation implements Serializable {

    private static final long serialVersionUID = 7925874058046995566L;

    private String id;
    /*** user ID Operator ID */
    private String userId;
    /** * Associate the user name with admin_user */
    private String username;
    /** * Login IP address */
    private String loginIp;
    /** * Operation type (0 login, 1 query, 2 modify) this can be defined according to their own needs, there are many other methods, this is not perfect, just enough of the kind of */
    private int type;

    /** * the url of the operation */
    private String url;
    /** * Operation content */
    private String operation;
    /** * operation time */
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;

    / * * * comment * /
    private String remark;
    /*** Change the time */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
}
Copy the code

2, mapper

@Repository
@Mapper
public interface LogOperationMapper extends BaseMapper<LogOperation> {}Copy the code
@Repository
public interface LogUserMapper extends BaseMapper<LogUser> {}Copy the code

3, the Service

public interface ILogUserService extends IService<LogUser> {}Copy the code
@Service
public class LogUserServiceImpl extends ServiceImpl<LogUserMapper.LogUser> implements ILogUserService {}Copy the code

4, the Controller

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.crush.log.annotation.MyLog;
import com.crush.log.entity.LogUser;
import com.crush.log.service.ILogUserService;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.util.List;

@RestController
@RequestMapping("user")
public class UserController {

    private static final Logger log = LogManager.getLogger(UserController.class);

    @Autowired
    private ILogUserService userService;


    /** * pretend to log in, save user information to session (method I wrote before) ** /
    @RequestMapping("/login")
    public String login(@RequestBody LogUser logUser,HttpServletRequest request){
        QueryWrapper<LogUser> wrapper = new QueryWrapper<>();
        wrapper.eq("username",logUser.getUsername()).eq("passwrod",logUser.getPasswrod());
        LogUser user = userService.getOne(wrapper);
        if(user! =null){
            request.getSession().setAttribute("user",user);
            return "Login successful";
        }
        return "Login failed";
    }

    /** Log */
    @mylog (operation = "query user information ",type = 1)
    @RequestMapping("/log")
    public List<LogUser> insertLog(HttpServletRequest request){
        List<LogUser> users = userService.list();
        returnusers; }}Copy the code

Remember to put a main boot class, so I’m not going to do that.

5, test,

Start the test directly by logging in first and then accessing /log.

To access/log

Let’s look at the background output again:

4. Talk to yourself

This article is just to provide you with a small idea, the code is relatively rough, please forgive me. 😁

There are many areas that can be expanded and improved, so if you are interested, you can try more, so that learning will be fun. πŸ˜‚

Log words he will be divided into many classes, we can expand according to their own needs.

I know we dig gold big guy, speech and good to hear, long and handsome, girlfriend literally new, give little younger brother a praise πŸ‘, this affirmation. 😁