Backend Development 10 min read

Implementing a Simple Log Management System with Annotations, AOP, and RabbitMQ in Spring Cloud

This tutorial explains how to build a lightweight log management module by defining a custom @SystemLog annotation, using Spring AOP to capture method execution details, and sending the log data through RabbitMQ via Spring Cloud Stream for asynchronous persistence in a database.

Top Architect
Top Architect
Top Architect
Implementing a Simple Log Management System with Annotations, AOP, and RabbitMQ in Spring Cloud

Introduction: In any system, log management is crucial; this article demonstrates building a simple log management module using annotations, AOP, and a message queue.

Design

Three components work together: a custom annotation marks methods, an AOP aspect intercepts those methods to create log objects, and RabbitMQ (integrated through Spring Cloud Stream) transports the logs to a dedicated service.

Database schema

CREATE TABLE `sys_log` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '唯一ID',
  `opt_id` int(11) DEFAULT NULL COMMENT '操作用户id',
  `opt_name` varchar(50) DEFAULT NULL COMMENT '操作用户名',
  `log_type` varchar(20) DEFAULT NULL COMMENT '日志类型',
  `log_message` varchar(255) DEFAULT NULL COMMENT '日志信息(具体方法名)',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8 COMMENT='系统日志表';

Entity class

@Data
public class SysLog {
    private static final long serialVersionUID = 1L;
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
    private Integer optId; // 操作用户id
    private String optName; // 操作用户名
    private String logType; // 日志类型
    private String logMessage; // 日志信息(具体方法名)
    private Date createTime; // 创建时间
}

Custom annotation

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SystemLog {
    SystemLogEnum type();
}

Enum for log types

public enum SystemLogEnum {
    SAVE_LOG("保存"),
    DELETE_LOG("删除"),
    REGISTER_LOG("注册"),
    LOGIN_LOG("登录"),
    LAUD_LOG("点赞"),
    COLLECT_LOG("收藏"),
    THROW_LOG("异常");
    private String type;
    SystemLogEnum(String type) { this.type = type; }
    public String getType() { return type; }
}

AOP aspect

@Component
@Aspect
@Slf4j
public class SysLogAspect {
    @Autowired
    MqStream stream;

    @Pointcut("@annotation(cn.zdxh.commons.utils.SystemLog)")
    public void logPointcut() {}

    @After("logPointcut()")
    public void afterLog(JoinPoint joinPoint) {
        SysLog sysLog = wrapSysLog(joinPoint);
        log.info("Log值:" + sysLog);
        stream.logOutput().send(MessageBuilder.withPayload(sysLog).build());
    }

    @AfterThrowing(value = "logPointcut()", throwing = "e")
    public void throwingLog(JoinPoint joinPoint, Exception e) {
        SysLog sysLog = wrapSysLog(joinPoint);
        sysLog.setLogType(SystemLogEnum.THROW_LOG.getType());
        sysLog.setLogMessage(sysLog.getLogMessage() + "===" + e);
        log.info("异常Log值:" + sysLog);
        stream.logOutput().send(MessageBuilder.withPayload(sysLog).build());
    }

    public SysLog wrapSysLog(JoinPoint joinPoint) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        SysLog sysLog = new SysLog();
        String methodName = signature.getDeclaringTypeName() + "." + signature.getName();
        SystemLog systemLog = signature.getMethod().getAnnotation(SystemLog.class);
        String token = request.getHeader("token");
        if (!StringUtils.isEmpty(token)) {
            Integer userId = JwtUtils.getUserId(token);
            String username = JwtUtils.getUsername(token);
            sysLog.setOptId(userId);
            sysLog.setOptName(username);
        }
        if (!StringUtils.isEmpty(systemLog.type())) {
            sysLog.setLogType(systemLog.type().getType());
        }
        sysLog.setLogMessage(methodName);
        sysLog.setCreateTime(new Date());
        return sysLog;
    }
}

RabbitMQ integration

Define an Input/Output interface (MqStream) for Spring Cloud Stream, configure bindings in application.yml , and enable the bindings with @EnableBinding(MqStream.class) . The producer sends logs via logOutput() , while the consumer listens on logInput() and persists the logs.

@Component
public interface MqStream {
    String LOG_INPUT = "log_input";
    String LOG_OUTPUT = "log_output";
    @Input(LOG_INPUT)
    SubscribableChannel logInput();
    @Output(LOG_OUTPUT)
    MessageChannel logOutput();
}

Producer configuration (excerpt):

spring:
  cloud:
    stream:
      bindings:
        log_output:
          destination: log.exchange
          content-type: application/json
          group: log.queue
          binder: youqu_rabbit
      binders:
        youqu_rabbit:
          type: rabbit
          environment:
            spring:
              rabbitmq:
                host: localhost
                port: 5672
                username: guest
                password: 25802580

Consumer configuration mirrors the producer but binds to log_input and saves the received SysLog objects.

Application usage

Simply annotate a method with @SystemLog(type = SystemLogEnum.REGISTER_LOG) . When the method is invoked, the AOP aspect automatically creates a log entry, sends it to RabbitMQ, and the consumer persists it to the sys_log table.

Conclusion

The overall flow is: annotation → AOP interception → log object creation → asynchronous transmission via MQ → consumer service inserts the log into the database.

BackendaopSpringRabbitMQAnnotationsLog ManagementSpring Cloud Stream
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.