常用的系统操作日志记录实现方式

1. AOP(面向切面编程)

实现原理
通过拦截方法调用(如Controller层或Service层),在方法执行前后插入日志记录逻辑,实现业务代码与日志逻辑的解耦。

技术实现(以Spring AOP为例):

@Aspect
@Component
public class LogAspect {
    @Pointcut("execution(* com.example.service.*.*(..))")
    public void servicePointcut() {}

    @Around("servicePointcut()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long endTime = System.currentTimeMillis();
        
        // 记录操作日志(方法名、参数、耗时等)
        LogUtils.log(
            joinPoint.getSignature().getName(),
            joinPoint.getArgs(),
            result,
            endTime - startTime
        );
        return result;
    }
}

优点

  • 无侵入性:不修改业务代码。
  • 集中管理:日志逻辑统一维护。
  • 灵活性:支持自定义切面(如仅记录特定注解的方法)。

缺点

  • 粒度较粗:难以记录数据库变更细节(如字段级变化)。
  • 性能损耗:频繁拦截可能影响性能。

适用场景
业务逻辑层的操作记录(如接口调用、方法执行耗时)。


2. Canal中间件监听Binlog

实现原理
通过监听数据库的Binlog日志(如MySQL),解析数据变更事件(增删改),触发日志记录。

技术流程

  1. 部署Canal Server:伪装为MySQL Slave,接收Binlog。
  2. 客户端订阅变更:解析Binlog事件(如RowChange)。
  3. 记录变更日志:将变更前后的数据写入日志存储。

示例代码(Canal客户端):

CanalConnector connector = CanalConnectors.newClusterConnector(
    "canal-server:11111", "example", "", "");
connector.connect();
connector.subscribe(".*\\..*"); // 订阅所有表

while (true) {
    Message message = connector.getWithoutAck(100);
    for (CanalEntry.Entry entry : message.getEntries()) {
        CanalEntry.RowChange rowChange = CanalEntry.RowChange.parseFrom(entry.getStoreValue());
        for (CanalEntry.RowData rowData : rowChange.getRowDatasList()) {
            // 记录变更前后的数据
            LogUtils.logDbChange(
                entry.getHeader().getTableName(),
                rowData.getBeforeColumnsList(),
                rowData.getAfterColumnsList()
            );
        }
    }
    connector.ack(message.getId());
}

优点

  • 解耦彻底:完全独立于业务代码。
  • 细粒度追踪:支持字段级变更记录。

缺点

  • 架构复杂:需维护Canal中间件。
  • 仅限数据库操作:无法记录非DB操作(如文件上传)。

适用场景
数据库层面的数据变更审计(如订单状态变更、用户信息修改)。


3. 消息队列异步记录

实现原理
将日志事件发送到消息队列(如Kafka、RabbitMQ),由消费者异步处理,避免阻塞主流程。

实现步骤

  1. 生产日志事件:在业务代码中发送日志消息。
kafkaTemplate.send("operation-log", "用户删除操作: ID=123");
  1. 消费并持久化:消费者批量处理日志,写入数据库或文件。
@KafkaListener(topics = "operation-log")
public void saveLog(String logMessage) {
    logRepository.save(new OperationLog(logMessage));
}

优点

  • 高性能:异步处理避免主流程阻塞。
  • 削峰填谷:应对高并发场景。
  • 扩展性:可接入多个消费者处理日志(如分析、报警)。

缺点

  • 最终一致性:日志可能存在延迟。
  • 依赖中间件:需保障消息队列的可用性。

适用场景
高并发系统或需要异步处理的日志(如用户行为埋点)。


4. 直接记录(硬编码)

实现原理
在业务代码中直接调用日志服务,同步写入日志。

示例代码

public void deleteUser(Long userId) {
    try {
        userRepository.deleteById(userId);
        // 直接记录日志
        logService.log("删除用户", "用户ID=" + userId, "SUCCESS");
    } catch (Exception e) {
        logService.log("删除用户", "用户ID=" + userId, "FAIL: " + e.getMessage());
        throw e;
    }
}

优点

  • 简单直接:快速实现,无需额外组件。
  • 精准控制:可灵活记录上下文信息。

缺点

  • 耦合度高:日志逻辑分散在代码各处。
  • 维护困难:修改日志格式需改动多处。

适用场景
小型项目或需要精准记录特定操作的场景。


5. 数据库触发器

实现原理
通过数据库触发器(如MySQL Trigger)在数据变更时自动记录日志。

示例SQL

CREATE TRIGGER log_user_update 
AFTER UPDATE ON users 
FOR EACH ROW 
BEGIN
    INSERT INTO user_audit_log 
    SET user_id = OLD.id,
        old_name = OLD.name,
        new_name = NEW.name,
        operation_time = NOW();
END;

优点

  • 数据库层面保障:无论通过何种方式修改数据均会触发。
  • 无需修改业务代码

缺点

  • 性能影响:触发器执行增加数据库负载。
  • 维护复杂:难以调试和版本控制。

适用场景
对数据变更的强审计需求,且无法通过应用层实现时。


6. ORM框架事件监听

实现原理
利用ORM框架(如Hibernate、MyBatis)的拦截器监听数据操作事件。

示例(Hibernate Interceptor):

public class AuditLogInterceptor extends EmptyInterceptor {
    @Override
    public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
        logService.log("新增操作", "实体=" + entity.getClass().getName());
        return super.onSave(entity, id, state, propertyNames, types);
    }
}

优点

  • 与ORM深度集成:精准捕获数据操作。
  • 减少代码侵入

缺点

  • 依赖ORM框架:不同框架实现方式差异大。
  • 无法捕获原生SQL操作

适用场景
使用ORM框架且需记录数据变更细节的系统。


方案对比与选型建议

方案

耦合性

性能影响

实现复杂度

适用场景

AOP

业务方法调用追踪

Canal

数据库字段级变更审计

消息队列

高并发异步日志处理

直接记录

简单场景快速实现

数据库触发器

数据库强审计需求

ORM事件监听

ORM框架下的数据操作追踪

选型建议

  • 全链路审计:结合AOP(业务层)+ Canal(数据库层)。
  • 高并发系统:消息队列异步处理 + AOP。
  • 简单需求:直接记录或AOP。
  • 数据库强审计:Canal或数据库触发器。

根据团队技术栈、性能要求、审计粒度等综合选择,必要时可组合多种方案实现全面覆盖。

你可能感兴趣的:(java,网络,mybatis,kafka,spring)