How to Build Scalable Business Operation Logging with AOP, SpEL, and Binlog
This article explores a step‑by‑step evolution of business operation logging—from a simple AOP‑annotation approach, through enriched SpEL expressions, to a robust Binlog‑based solution with time‑window aggregation—highlighting benefits, challenges, code examples, and architectural diagrams for backend developers.
Background
In recent projects the system needs a business operation log statistics feature. Because the system sits at the core of the business chain, it receives upstream data and forwards user‑generated data downstream. To improve issue‑resolution efficiency and provide more tooling for users, we plan to record the full lifecycle of core business data.
Benefits of Business Operation Logs
Audit and compliance : Track data‑change history and identify who performed which operation and when.
Security
Intrusion detection: Analyze logs to spot abnormal or unauthorized access.
Post‑incident analysis: Use logs to determine attack scope and impact.
Error diagnosis and system monitoring
Troubleshooting: Provide key information for rapid fault location and repair.
Performance monitoring: Analyze response time and resource consumption.
User behavior analysis
Business insight: Derive valuable insights from user operation patterns to guide product improvement and market strategy.
Customer service: Help support staff understand user issues for targeted assistance.
Data recovery : Assist backup and restoration when data loss or corruption occurs.
Business rule and process improvement : Analyze and optimize workflows to increase efficiency.
Goal
Enhance data traceability and transparency, ensuring smooth and monitorable business processes. The expected system effects are:
Solution 1.0: AOP + Annotation
Using Spring AOP and a custom annotation provides a low‑intrusion way to capture core business operations before and after execution.
Define Log Annotation
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Loggable {
String value() default "";
// additional attributes such as operation type, level, etc.
}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(); // execute target method
long executionTime = System.currentTimeMillis() - start;
logger.info(joinPoint.getSignature() + " executed in " + executionTime + "ms");
return proceed;
}
}Configure Spring AOP + Mark Annotation
@Configuration
@EnableAspectJAutoProxy
public class AopConfig {
// other beans if needed
}
public class SomeService {
@Loggable
public void someBusinessMethod(Object someParam) {
// business logic
}
}Log Recording Logic
@Autowired
private Logger logger; // e.g., SLF4J logger
@Around("@annotation(loggable)")
public Object logBusinessOperation(ProceedingJoinPoint joinPoint, Loggable loggable) throws Throwable {
long start = System.currentTimeMillis();
try {
Object result = joinPoint.proceed();
return result;
} catch (Exception e) {
throw e;
} finally {
long executionTime = System.currentTimeMillis() - start;
logger.info("{} executed in {} ms", joinPoint.getSignature(), executionTime);
}
}Solution Summary
The basic framework is now coded; after deployment the system will start persisting user operation logs.
Solution 2.0: AOP + SpEL
Solution 1.0 records fixed‑template logs. To embed richer business context (e.g., "User X modified project ID=001"), we extend the annotation and evaluate SpEL expressions at runtime.
SpEL Overview
Spring Expression Language (SpEL) evaluates expressions against runtime objects, allowing access to properties, methods, arithmetic, logical operations, and more. It is used in @Value, XML, and annotation configurations.
Expression Definition
@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 operation
String type(); // business module
String subType() default ""; // sub‑module
String bizNo(); // business identifier
String extra() default ""; // extra info
String actionType(); // e.g., edit, add, delete
}Expression Usage
By parsing the annotation fields with SpEL, we can capture full method parameters, return values, and contextual data, greatly expanding the log’s informational breadth.
Solution Summary
Pros: Reduces repetitive code, simplifies integration, and allows unlimited extension of business data.
Cons: Still introduces annotation overhead and requires product‑R&D alignment on log content.
Solution 3.0: Binlog + Time Window
Binlog records every change to MySQL tables in binary format. It can be leveraged to sense data‑level events directly from the database.
Problems and Idea
Problem 1 : Binlog entries are unordered and may span multiple tables, making it hard to handle cascaded updates without a transaction.
Problem 2 : The updater (operator) is often missing because upstream services don’t set it.
To address Problem 1 we borrow Flink’s sliding time‑window concept: group binlog events into fixed‑size windows (e.g., 1 minute) and then correlate rows within each window, using transaction keys when available.
By also capturing reference fields between tables, the windowed processing can resolve cascaded updates.
Binlog Data Structures
@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;
}Architecture Diagram
Solution Summary
Binlog gives fine‑grained data change awareness, but also captures non‑application changes (e.g., batch jobs), which may be noisy.
Solution 3 weakens the rich business‑scene fields (action type, sub‑type), reducing granularity.
The design can be reused for other systems to centralize log collection.
Conclusion
Architecture evolves through trade‑offs; there is no perfect solution. The key is to adapt the logging strategy to the specific tech stack and business needs, balancing elegance, invasiveness, and completeness.
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.
Architect
Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.
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.
