Ghi nhật ký là kỹ thuật quan trọng giúp nâng cao độ tin cậy, bảo mật và hiệu năng hệ thống, hỗ trợ xử lý sự cố, phân tích nghiệp vụ và tuân thủ quy định. Thay vì lưu log dạng văn bản dễ bị xóa, giải pháp lưu trữ nhật ký vào cơ sở dữ liệu mang lại các lợi ích:
- Truy xuật nhật ký lịch sử
- Phân tích và thống kê dữ liệu
- Liên kết thông tin giao dịch
- Đảm bảo an toàn dữ liệu
1. Tạo chú thích tùy chỉnh
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface AuditLog {
String module() default "";
ActionType action() default ActionType.OTHER;
boolean captureParams() default true;
boolean captureResult() default true;
}
public enum ActionType {
CREATE, UPDATE, DELETE, QUERY, OTHER
}
2. Triển khai Aspect xử lý nhật ký
@Aspect
@Component
public class AuditAspect {
@Value("${audit.enabled}")
private boolean auditEnabled;
@Autowired
private LogStorageService logService;
@AfterReturning(pointcut = "@annotation(auditConfig)", returning = "response")
public void logSuccess(JoinPoint joinPoint, AuditLog auditConfig, Object response) {
if(auditEnabled) processLog(joinPoint, auditConfig, response, null);
}
@AfterThrowing(pointcut = "@annotation(auditConfig)", throwing = "ex")
public void logError(JoinPoint joinPoint, AuditLog auditConfig, Exception ex) {
if(auditEnabled) processLog(joinPoint, auditConfig, null, ex);
}
private void processLog(JoinPoint joinPoint, AuditLog config, Object result, Exception error) {
HttpServletRequest request =
((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
LogEntry log = new LogEntry();
log.setModule(config.module());
log.setActionType(config.action().name());
log.setPath(request.getRequestURI());
log.setIpAddress(request.getRemoteAddr());
if(config.captureParams()) {
log.setParameters(serialize(joinPoint.getArgs()));
}
if(error != null) {
log.setErrorMsg(error.getMessage());
} else if(config.captureResult() && result != null) {
log.setResult(serialize(result));
}
logService.save(log);
}
private String serialize(Object obj) {
return new ObjectMapper().writeValueAsString(obj);
}
}
3. Ứng dụng thực tế
@AuditLog(module = "Product", action = ActionType.CREATE)
@PostMapping("/products")
public ResponseEntity<?> createProduct(@RequestBody Product product) {
productService.save(product);
return ResponseEntity.ok().build();
}
Giải pháp này sử dụng cơ chế AOP để tự động ghi lại thông tin hoạt động khi phương thức được chú thích thực thi. Dữ liệu nhật ký bao gồm tham số, kết quả, lỗi và metadata được lưu trữ định dạng cấu trúc trong database.