自定义注解监控接口并记录操作日志到数据库

package io.common.ascept;

import io.annotation.LogOperation;
import io.common.utils.HttpContextUtils;
import io.common.utils.IpUtils;
import io.common.utils.JsonUtils;
import io.entity.TokenEntity;
import io.entity.mianchong.ApiLogOperationEntity;
import io.enums.OperationStatusEnum;
import io.service.TokenService;
import io.service.mianchong.ApiLogOperationService;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;

/**
 * @describe 记录操作日志
 */
@Aspect
@Component
public class LogOperationAspect {
    @Resource
    private ApiLogOperationService apiLogOperationService;
    @Resource
    private TokenService tokenService;

    @Pointcut("@annotation(io.annotation.LogOperation)")
    public void logPointCut() {
    }

    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        long beginTime = System.currentTimeMillis();
        try {
            //执行方法
            Object result = point.proceed();
            //执行时长(毫秒)
            long time = System.currentTimeMillis() - beginTime;
            //保存日志
            saveLog(point, time, OperationStatusEnum.SUCCESS.value());
            return result;
        } catch (Exception e) {
            //执行时长(毫秒)
            long time = System.currentTimeMillis() - beginTime;
            //保存日志
            saveLog(point, time, OperationStatusEnum.FAIL.value());
            throw e;
        }
    }

    private void saveLog(ProceedingJoinPoint joinPoint, long time, Integer status) throws Exception {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = joinPoint.getTarget().getClass().getDeclaredMethod(signature.getName(), signature.getParameterTypes());
        LogOperation annotation = method.getAnnotation(LogOperation.class);

        ApiLogOperationEntity log = new ApiLogOperationEntity();
        if (annotation != null) {
            //注解上的描述
            log.setOperation(annotation.value());
        }

        log.setStatus(status);
        log.setRequestTime((int) time);

        //请求相关信息
        HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
        assert request != null;

        //客户端ip
        log.setIp(IpUtils.getClientIpAddress(request));

        //从header中获取token
        String token = request.getHeader("token");
        //token为空
        if (StringUtils.isBlank(token)) {
            log.setUserAgent("未登录操作");
        } else {
            //查询token信息
            TokenEntity tokenEntity = tokenService.getByToken(token);
            //登录用户信息
            log.setUserAgent(tokenEntity.getUserId().toString());
        }
        log.setRequestUri(request.getRequestURI());
        log.setRequestMethod(request.getMethod());

        //请求参数
        Object[] args = joinPoint.getArgs();
        try {
            String params = JsonUtils.toJsonString(args[0]);
            log.setRequestParams(params);
        } catch (Exception ignored) {
        }

        log.setCreateDate(new Date());
        //保存到DB
        apiLogOperationService.save(log);
    }
}

package io.annotation;

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

/**
 * 操作日志注解
 *
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogOperation {

    String value() default "";
}

使用方法

@PostMapping("xxxxx")
    @LogOperation("接口描述")
    public ResponseEntity<?> xxxxxxxx(@RequestBody DTO dto) throws IOException {
    ..............
    }

操作日志表:

CREATE TABLE `db_name`.`log_operation`  (
  `id` bigint(0) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'id',
  `operation` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '用户操作',
  `request_uri` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '请求URI',
  `request_method` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '请求方式',
  `request_params` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL COMMENT '请求参数',
  `request_time` int(0) UNSIGNED NOT NULL COMMENT '请求时长(毫秒)',
  `user_agent` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '用户代理',
  `ip` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '操作IP',
  `status` tinyint(0) UNSIGNED NOT NULL COMMENT '状态  0:失败   1:成功',
  `creator_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '用户名',
  `creator` bigint(0) NULL DEFAULT NULL COMMENT '创建者',
  `create_date` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `idx_create_date`(`create_date`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '操作日志' ROW_FORMAT = Dynamic;
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是一个简单的示例代码,帮助你实现所需的功能: 1. 引入 Actuator 相关依赖 在 pom.xml 文件添加以下依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> ``` 2. 实现 AOP 切面 首先定义一个自定义注解 `@Monitor`,用于标记需要进行监控接口方法。接着,定义一个切面 `ApiMonitorAspect`,在该切面实现监控逻辑,包括记录调用次数、记录调用来源等。具体代码如下: ```java @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Monitor { } @Aspect @Component public class ApiMonitorAspect { // 定义一个 Map,用于记录接口方法的调用次数 private Map<String, Integer> callCountMap = new ConcurrentHashMap<>(); @Around("@annotation(com.example.demo.annotation.Monitor)") public Object monitorApi(ProceedingJoinPoint joinPoint) throws Throwable { // 获取接口方法的参数和返回值 Object[] args = joinPoint.getArgs(); Object result = joinPoint.proceed(); // 记录接口方法的调用次数 String methodName = joinPoint.getSignature().toShortString(); Integer callCount = callCountMap.get(methodName); if (callCount == null) { callCount = 0; } callCountMap.put(methodName, callCount + 1); // 记录接口方法的调用来源(这里假设调用来源是从 HTTP 头获取的) HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); String callSource = request.getHeader("callSource"); // 将调用次数和调用来源写入日志(这里使用 log4j2) Logger logger = LogManager.getLogger(joinPoint.getTarget().getClass()); logger.info("method: {}, callCount: {}, callSource: {}", methodName, callCount + 1, callSource); return result; } // 定义一个定时任务,定时将接口方法的调用次数写入 MQ @Scheduled(cron = "0 0 * * * ?") public void sendCallCountToMQ() { // 将调用次数转换成 JSON 格式的字符串 ObjectMapper objectMapper = new ObjectMapper(); String callCountJson = null; try { callCountJson = objectMapper.writeValueAsString(callCountMap); } catch (JsonProcessingException e) { e.printStackTrace(); } // 将调用次数发送到 MQ // ... } } ``` 3. 标记需要监控接口方法 在接口方法上添加 `@Monitor` 注解即可,例如: ```java @RestController public class ApiController { @Autowired private ApiService apiService; @GetMapping("/api") @Monitor public String api(@RequestParam("param") String param) { return apiService.process(param); } } ``` 4. 发送消息到 MQ 使用 Spring Boot 集成的 RabbitMQ 或者 Kafka 组件发送消息即可。以 RabbitMQ 为例,需要在 application.yml 文件添加 RabbitMQ 的配置信息,然后在代码使用 RabbitTemplate 发送消息。具体代码如下: ```java @Component public class CallCountSender { @Autowired private RabbitTemplate rabbitTemplate; public void send(String message) { rabbitTemplate.convertAndSend("call_count_exchange", "call_count_key", message); } } ``` 5. 消费 MQ 的消息 首先定义一个消息消费者 `CallCountConsumer`,在该消费者实现消息的消费逻辑,将消费到的消息插入到数据库。具体代码如下: ```java @Component public class CallCountConsumer { @Autowired private CallCountRepository callCountRepository; @RabbitListener(queues = "call_count_queue") public void receive(String message) { ObjectMapper objectMapper = new ObjectMapper(); try { // 将 JSON 格式的字符串转换成 Map Map<String, Integer> callCountMap = objectMapper.readValue(message, new TypeReference<Map<String, Integer>>() {}); // 将调用次数插入到数据库 for (Map.Entry<String, Integer> entry : callCountMap.entrySet()) { CallCount callCount = new CallCount(); callCount.setMethodName(entry.getKey()); callCount.setCallCount(entry.getValue()); callCountRepository.save(callCount); } } catch (IOException e) { e.printStackTrace(); } } } ``` 最后,需要在 application.yml 文件添加 RabbitMQ 的配置信息和数据库的配置信息。例如: ```yaml spring: rabbitmq: host: localhost port: 5672 username: guest password: guest datasource: url: jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=Asia/Shanghai username: root password: root ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值