Design and Implementation of a Spring Boot Log Auditing Plugin Using AOP
This article details the design, architecture, and step‑by‑step implementation of a Spring Boot log auditing plugin built with AOP, Maven plugins, auto‑configuration, thread‑pool execution, and optional message‑queue streaming to improve operation logging and exception tracing in backend services.
1. Introduction – The article explains the need for a log auditing plugin in the Qianshuju advertising system, describing scenarios where developers need to trace abnormal requests, data mismatches, and server issues.
2. Overview – The plugin is built on AOP technology, integrated via a Maven dependency, and can optionally push logs to a message queue for real‑time analysis. The basic architecture diagram is shown.
3. Implementation – The core components are introduced:
git-commit-id-maven-plugin to generate git-commit-id.properties containing Git commit hash, branch, and timestamp.
hibernate-validator for configuration validation.
spring-boot-configuration-processor to generate IDE metadata.
maven-source-plugin to attach source jars.
DependencyManagement block to unify Spring Boot versions.
Key modules are described with code snippets (shown in ... blocks), including:
<groupId>io.github.git-commit-id</groupId>
<artifactId>git-commit-id-maven-plugin</artifactId>
<version>${git-commit-id-maven-plugin.version}</version>
<executions>
<execution>
<id>get-the-git-infos</id>
<goals>
<goal>revision</goal>
</goals>
</execution>
</executions>LogPluginProperties class (auto‑configuration entry) defines properties such as dataStreamType, thread‑pool sizes, and ZZMQ settings, and implements InitializingBean to provide defaults and environment checks.
public class LogPluginProperties implements InitializingBean {
private String dataStreamType = DataStreamType.ZZ_MQ;
private Boolean enableStream = false;
private ZZMQProperties zzmqProperties;
private String renter;
@Override
public void afterPropertiesSet() throws Exception {
if (StrUtil.isBlank(renter)) {
renter = SicUtil.getCurrentSicInfo().getAppName();
}
checkEnv();
}
// getters & setters omitted for brevity
}ZZMQDataStreamServiceImpl shows how logs are sent to the internal ZZMQ message queue.
@Service
@ConditionalOnProperty(name = "qianshuju.logplugin.dataStreamType", havingValue = "zzmq")
@ConditionalOnClass(DefaultMQProducer.class)
public class ZZMQDataStreamServiceImpl implements IDataStreamService {
@Autowired
private LogPluginProperties logPluginProperties;
@Autowired
private DefaultMQProducer defaultMqProducer;
@Override
public boolean sendToStream(PluginLogDto pluginLogDto) {
ZZMQProperties p = logPluginProperties.getZzmqProperties();
Message msg = new Message(p.getTopic(), p.getTag(), JsonUtil.silentObject2String(pluginLogDto).getBytes());
try {
SendResult result = defaultMqProducer.send(msg);
return ObjectUtil.equal(result.getSendStatus(), SendStatus.SEND_OK);
} catch (Exception e) {
log.error("sendToStream fail:", e);
return false;
}
}
}The thread‑pool bean definition ensures log processing does not block business threads.
@Bean(name = CommonConstant.LOG_PLUGIN_EXECUTOR, autowireCandidate = false)
public ThreadPoolTaskExecutor logPluginExecutor() {
ThreadPoolTaskExecutor exec = new ThreadPoolTaskExecutor();
exec.setCorePoolSize(logPluginProperties.getCorePoolSize());
exec.setMaxPoolSize(logPluginProperties.getMaxPoolSize());
exec.setQueueCapacity(logPluginProperties.getQueueCapacity());
exec.setWaitForTasksToCompleteOnShutdown(true);
exec.setAwaitTerminationSeconds(CommonConstant.LOGPLUGIN_EXECUTOR_AWAIT_TERMINATION_SECONDS);
return exec;
}The LogApiLogAspect weaves the logging logic, capturing request details, user information, server metadata, and delegating persistence to either the console logger or the data‑stream service.
@Aspect
@Component
public class LogApiLogAspect {
@Autowired private Server server;
@Autowired private ICreatedByService iCreatedByService;
@Autowired private LogPluginProperties logPluginProperties;
@Resource(name = CommonConstant.LOG_PLUGIN_EXECUTOR) private ThreadPoolTaskExecutor executor;
@Autowired private IlogPersistenceService ilogPersistenceService;
@Autowired(required = false) private IDataStreamService dataStreamService;
@Pointcut("@annotation(com.bj58.zhuanzhuan.qianshuju.logPlugin.annotation.ApiLog)")
public void logPointCut() {}
@AfterReturning("@annotation(apiLog)")
public void doAround(JoinPoint point, ApiLog apiLog) { handleUsualLog(point); }
@AfterThrowing(value = "logPointCut()", throwing = "exception")
public void afterThrowing(JoinPoint point, Exception exception) { handleExceptionLog(point, exception); }
// handling methods omitted for brevity
}4. Auto‑configuration – The plugin registers its beans via spring.factories, enabling seamless integration when the Maven coordinate is added to a project.
5. Result – With the plugin enabled, logs containing request parameters, server IP, user, method name, and error details are persisted (or sent to ZZMQ) and can be queried through a search engine, dramatically improving debugging efficiency.
6. Future work – The author proposes dynamic log titles using SpringEL, extending the annotation to service methods, adapting to Spring Boot 3’s AutoConfiguration.imports, and integrating with the internal “Tianwang” tracing system to correlate TraceId across services.
7. Conclusion – The plugin provides a comprehensive solution for operation logging and exception tracing in a Java backend, leveraging Maven plugins, Spring Boot, AOP, thread‑pool execution, and optional message‑queue streaming.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Zhuanzhuan Tech
A platform for Zhuanzhuan R&D and industry peers to learn and exchange technology, regularly sharing frontline experience and cutting‑edge topics. We welcome practical discussions and sharing; contact waterystone with any questions.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
