掘金 后端 ( ) • 2024-07-02 17:37

前言

在开发企业级应用时,记录用户操作日志是非常重要的。这不仅能帮助开发者监控系统的行为,还能在出现问题时进行追踪。在这篇文章中,我们将介绍如何在Spring Boot中开发一个完整的日志系统,记录每一步操作,如登录、创建订单、删除、查询等。

添加依赖

首先,确保在你的Spring Boot项目中包含必要的依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-logging</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
</dependency>

配置日志存储

在数据库中创建一个表来存储日志信息。以下是一个示例表的SQL脚本:

CREATE TABLE operation_logs (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50),
    operation VARCHAR(100),
    method VARCHAR(100),
    params TEXT,
    result TEXT,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

创建实体类

接下来,创建一个实体类来映射日志表:

import javax.persistence.*;
import java.time.LocalDateTime;

@Entity
@Table(name = "operation_logs")
public class OperationLog {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String username;
    private String operation;
    private String method;
    
    @Lob
    private String params;
    
    @Lob
    private String result;
    
    private LocalDateTime timestamp;

    // Getters and setters
}

创建仓库接口

创建一个仓库接口来管理日志记录:

import org.springframework.data.jpa.repository.JpaRepository;

public interface OperationLogRepository extends JpaRepository<OperationLog, Long> {
}

创建AOP切面

使用Spring AOP来拦截方法并记录日志:

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.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.Arrays;

@Aspect
@Component
public class LoggingAspect {

    @Autowired
    private OperationLogRepository logRepository;

    @Autowired
    private HttpServletRequest request;

    @Before("execution(* com.example.yourpackage..*(..))")
    public void logBefore(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String methodName = signature.getMethod().getName();
        String className = joinPoint.getTarget().getClass().getName();
        String operation = "Executing method: " + className + "." + methodName;
        
        String username = request.getUserPrincipal().getName(); // 获取当前用户名
        String params = Arrays.toString(joinPoint.getArgs());

        OperationLog log = new OperationLog();
        log.setUsername(username);
        log.setOperation(operation);
        log.setMethod(className + "." + methodName);
        log.setParams(params);
        log.setTimestamp(LocalDateTime.now());
        
        logRepository.save(log);
    }

    @AfterReturning(pointcut = "execution(* com.example.yourpackage..*(..))", returning = "result")
    public void logAfter(JoinPoint joinPoint, Object result) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String methodName = signature.getMethod().getName();
        String className = joinPoint.getTarget().getClass().getName();
        String operation = "Completed method: " + className + "." + methodName;

        String username = request.getUserPrincipal().getName(); // 获取当前用户名
        String params = Arrays.toString(joinPoint.getArgs());
        
        OperationLog log = new OperationLog();
        log.setUsername(username);
        log.setOperation(operation);
        log.setMethod(className + "." + methodName);
        log.setParams(params);
        log.setResult(result != null ? result.toString() : "null");
        log.setTimestamp(LocalDateTime.now());
        
        logRepository.save(log);
    }
}

配置日志级别

application.propertiesapplication.yml中配置日志级别:

logging.level.root=INFO
logging.level.com.example.yourpackage=DEBUG

测试日志系统

编写单元测试或通过实际操作测试日志系统是否按预期工作。

通过上述步骤,您可以在Spring Boot项目中实现一个功能完善的日志系统,记录用户的每一步操作,包括登录、创建订单、删除、查询等。这个日志系统将帮助您更好地监控和维护应用程序,同时提供有价值的审计追踪。