Evolving Business Operation Logging: From AOP Annotations to Binlog Time Windows

This article examines the need for comprehensive business operation logging in a central system, outlines the benefits of audit, security, monitoring, and analysis, and walks through three progressive solutions—AOP with annotations, AOP with SpEL, and a Binlog‑based time‑window approach—detailing their implementations, trade‑offs, and architectural considerations.

Java Architect Handbook
Java Architect Handbook
Java Architect Handbook
Evolving Business Operation Logging: From AOP Annotations to Binlog Time Windows

0. Background

In a recent project the system sits at the core of a business data pipeline, receiving upstream data and forwarding processed results downstream. To improve issue‑resolution efficiency and provide richer tooling for users, the team decided to record the full lifecycle of core business operations.

0.1 Benefits

Audit & Compliance : Track who performed which operation and when.

Security : Detect intrusion attempts and support post‑incident analysis.

Error Diagnosis & Monitoring : Provide crucial context for troubleshooting and performance analysis.

User Behavior Analysis : Gain business insights and improve customer service.

Data Recovery : Assist in backup and restoration processes.

Process Improvement : Enable workflow optimization based on logged data.

0.2 Goals

Enhance traceability and transparency of data, ensuring smooth and monitorable business processes. The core question to solve is: "Who performed what action on which object at what time?"

1. Solution 1.0 – AOP + Annotation

Using Spring AOP and a custom annotation provides a low‑intrusion way to capture method execution details.

1.1 Define Logging Annotation

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Loggable {
    String value() default ""; // additional attributes can be added
}

1.2 Create AOP Aspect

@Aspect
@Component
public class LoggingAspect {
    @Around("@annotation(loggable)")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint, Loggable loggable) throws Throwable {
        long start = System.currentTimeMillis();
        Object proceed = joinPoint.proceed();
        long executionTime = System.currentTimeMillis() - start;
        logger.info(joinPoint.getSignature() + " executed in " + executionTime + "ms");
        return proceed;
    }
}

1.3 Enable AOP and Apply Annotation

@Configuration
@EnableAspectJAutoProxy
public class AopConfig { }

public class SomeService {
    @Loggable
    public void someBusinessMethod(Object param) {
        // business logic
    }
}

1.4 Logging Logic

The aspect can be extended to record method parameters, return values, exceptions, etc., by injecting a logger (e.g., SLF4J) and adding custom logic before and after method execution.

1.5 Summary of Solution 1.0

While simple and minimally invasive, this approach suffers from limited log granularity, inability to capture complex business scenarios, and difficulty handling cascaded operations across multiple services.

2. Solution 2.0 – AOP + SpEL

To enrich log content, the annotation is extended with SpEL (Spring Expression Language) fields, allowing dynamic extraction of method arguments and business context.

2.1 SpEL Overview

SpEL evaluates expressions at runtime and can be used in annotations, configuration files, and XML. It enables arithmetic, logical, and method‑call expressions.

2.2 Define Extended Annotation

@Repeatable(LogRecords.class)
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface LogRecord {
    String success();
    String fail() default "";
    String operator() default ""; // who performed the action
    String type(); // business module
    String subType() default ""; // sub‑module
    String bizNo(); // business identifier
    String extra() default ""; // additional info
    String actionType(); // e.g., CREATE, UPDATE, DELETE
}

2.3 Parse SpEL Expressions

import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;

public class LogRecordParser {
    public static Map<String, Object> parseLogRecord(Annotation logRecordAnnotation) {
        Map<String, Object> result = new HashMap<>();
        ExpressionParser parser = new SpelExpressionParser();
        for (String attribute : logRecordAnnotation.getAttributeNames()) {
            Object value = logRecordAnnotation.getAttribute(attribute);
            Expression expression = parser.parseExpression(attribute);
            Object parsedValue = expression.getValue();
            result.put(attribute, parsedValue);
        }
        return result;
    }
}

2.4 Usage Example

Developers annotate methods with @LogRecord and embed SpEL placeholders (e.g., #{#param.id}) to capture specific business data such as the affected entity ID.

2.5 Summary of Solution 2.0

Pros: Reduces boilerplate, captures richer business context.

Cons: Increases annotation complexity and still requires agreement on log content across product and development teams.

3. Solution 3.0 – Binlog + Time Window

To move logging to the data layer, the team explores using MySQL Binlog combined with a sliding time‑window mechanism (inspired by Flink) to aggregate and correlate changes across tables.

3.1 Binlog Basics

Binlog records every SQL statement that modifies data. It can be consumed to reconstruct data changes.

3.2 Problem & Approach

Problem 1 : Binlog events are unordered and may span multiple tables without a transaction boundary.

Solution : Apply a fixed‑size (e.g., 1‑minute) sliding window to group events; within each window, correlate rows using foreign‑key references.

Problem 2 : Missing operator information when updates are performed automatically.

Solution : Enforce operator stamping at the ORM layer or via database triggers.

3.3 Data Model

@Data
public static class RowChange {
    private int tableId;
    private List<RowDatas> rowDatas;
    private String eventType;
    private boolean isDdl;
}

@Data
public static class RowDatas {
    private List<DataColumn> afterColumns;
    private List<DataColumn> beforeColumns;
}

@Data
public static class DataColumn {
    private int sqlType;
    private boolean isNull;
    private String mysqlType;
    private String name;
    private boolean isKey;
    private int index;
    private boolean updated;
    private String value;
}

3.4 Architecture Overview

The pipeline consumes Binlog, parses RowChange objects, groups them by a 1‑minute window, and then performs join operations based on reference fields to reconstruct a complete business operation log.

3.5 Summary of Solution 3.0

Advantages: Captures low‑level data changes without application‑level instrumentation; can be reused for other systems.

Limitations: Requires handling of large, noisy Binlog streams and may lose fine‑grained business semantics.

4. Final Thoughts

The evolution from simple AOP annotations to a Binlog‑driven, windowed architecture illustrates how logging requirements grow with system complexity. While each solution addresses specific shortcomings of its predecessor, the ideal approach often combines multiple techniques and adapts to the concrete business and technical constraints.

JavaAOPSpringSpELloggingbinlog
Java Architect Handbook
Written by

Java Architect Handbook

Focused on Java interview questions and practical article sharing, covering algorithms, databases, Spring Boot, microservices, high concurrency, JVM, Docker containers, and ELK-related knowledge. Looking forward to progressing together with you.

0 followers
Reader feedback

How this landed with the community

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.