Custom Annotation for Logging
Define an annotation to mark methods requiring logging:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AuditLog {
String category() default "";
String action() default "";
}
Log Entity Structure
Create a data model for log records:
@Data
public class SystemLog {
private Long id;
private String userId;
private LocalDateTime timestamp;
private String category;
private String action;
private String methodSignature;
private String parameters;
private String outcome;
private String error;
private Long duration;
private String clientAddress;
}
AOP Aspect Implementation
Implement the logging aspect:
@Aspect
@Component
@RequiredArgsConstructor
public class LoggingAspect {
private final LogPersister logPersister;
@Pointcut("@annotation(com.example.AuditLog)")
public void auditedMethods() {}
@Around("auditedMethods()")
public Object logOperation(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
SystemLog logEntry = new SystemLog();
logEntry.setTimestamp(LocalDateTime.now());
HttpServletRequest request = ((ServletRequestAttributes)
RequestContextHolder.currentRequestAttributes()).getRequest();
logEntry.setClientAddress(request.getRemoteAddr());
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
AuditLog auditAnnotation = method.getAnnotation(AuditLog.class);
logEntry.setCategory(auditAnnotation.category());
logEntry.setAction(auditAnnotation.action());
logEntry.setMethodSignature(method.getDeclaringClass().getName() + "." + method.getName());
logEntry.setParameters(Arrays.toString(joinPoint.getArgs()));
try {
Object result = joinPoint.proceed();
logEntry.setOutcome("SUCCESS");
return result;
} catch (Exception e) {
logEntry.setOutcome("FAILURE");
logEntry.setError(e.getMessage());
throw e;
} finally {
logEntry.setDuration(System.currentTimeMillis() - start);
persistLog(logEntry);
}
}
private void persistLog(SystemLog logEntry) {
try {
logPersister.save(logEntry);
} catch (Exception e) {
// Handle persistence failure
}
}
}
Database Persistence
Define the repository interface:
public interface SystemLogRepository {
@Insert("INSERT INTO system_logs (user_id, timestamp, category, action, " +
"method, parameters, outcome, error, duration, client_ip) " +
"VALUES (#{userId}, #{timestamp}, #{category}, #{action}, " +
"#{methodSignature}, #{parameters}, #{outcome}, #{error}, " +
"#{duration}, #{clientAddress})")
@Options(useGeneratedKeys = true, keyProperty = "id")
void insert(SystemLog log);
}
Usage Example
Apply the annotation to methods:
@RestController
public class UserController {
@PostMapping("/users")
@AuditLog(category = "USER_MANAGEMENT", action = "CREATE_USER")
public User createUser(@RequestBody User user) {
return userService.save(user);
}
}
Database Schema
CREATE TABLE system_logs (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id VARCHAR(50) NOT NULL,
timestamp DATETIME NOT NULL,
category VARCHAR(100) NOT NULL,
action VARCHAR(255) NOT NULL,
method VARCHAR(255) NOT NULL,
parameters TEXT,
outcome VARCHAR(50),
error TEXT,
duration BIGINT,
client_ip VARCHAR(50)
);