本文目录导读:

我来提供一个完整的Java操作日志案例,包含多种实现方式:
基于Spring AOP + 自定义注解的方式(最常用)
自定义操作日志注解
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OperationLog {
// 操作模块
String module() default "";
// 操作类型(新增、修改、删除、查询等)
String operation() default "";
// 操作描述
String description() default "";
// 是否保存请求参数
boolean saveRequest() default true;
// 是否保存响应结果
boolean saveResponse() default false;
}
操作日志实体类
import javax.persistence.*;
import java.time.LocalDateTime;
@Entity
@Table(name = "operation_log")
public class OperationLog {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "module")
private String module; // 操作模块
@Column(name = "operation")
private String operation; // 操作类型
@Column(name = "description")
private String description; // 操作描述
@Column(name = "request_params")
private String requestParams; // 请求参数
@Column(name = "response_result")
private String responseResult; // 响应结果
@Column(name = "operator_id")
private Long operatorId; // 操作人ID
@Column(name = "operator_name")
private String operatorName; // 操作人姓名
@Column(name = "ip_address")
private String ipAddress; // 操作IP
@Column(name = "operation_time")
private LocalDateTime operationTime; // 操作时间
@Column(name = "execution_time")
private Long executionTime; // 执行耗时(毫秒)
@Column(name = "status")
private Integer status; // 操作状态:1-成功,0-失败
@Column(name = "error_message")
private String errorMessage; // 错误信息
// getters and setters 省略
}
Service层
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
@Service
public class OperationLogService {
public void saveLog(OperationLog log) {
// 这里可以保存到数据库
// 或者异步发送到消息队列
System.out.println("保存操作日志: " + log);
// logRepository.save(log);
}
}
AOP切面实现
import com.fasterxml.jackson.databind.ObjectMapper;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.Arrays;
@Aspect
@Component
public class OperationLogAspect {
private static final Logger logger = LoggerFactory.getLogger(OperationLogAspect.class);
@Autowired
private OperationLogService operationLogService;
@Autowired
private ObjectMapper objectMapper;
// 定义切点:使用自定义注解的方法
@Pointcut("@annotation(com.example.log.OperationLog)")
public void operationLogPointcut() {}
// 环绕通知
@Around("operationLogPointcut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
OperationLog operationLog = getOperationLogAnnotation(joinPoint);
// 构建日志对象
OperationLog log = new OperationLog();
log.setModule(operationLog.module());
log.setOperation(operationLog.operation());
log.setDescription(operationLog.description());
log.setOperationTime(LocalDateTime.now());
log.setOperatorId(getCurrentUserId());
log.setOperatorName(getCurrentUsername());
log.setIpAddress(getIpAddress());
// 保存请求参数
if (operationLog.saveRequest()) {
try {
log.setRequestParams(objectMapper.writeValueAsString(joinPoint.getArgs()));
} catch (Exception e) {
log.setRequestParams(Arrays.toString(joinPoint.getArgs()));
}
}
// 执行目标方法
Object result = null;
try {
result = joinPoint.proceed();
log.setStatus(1); // 成功
log.setResponseResult(objectMapper.writeValueAsString(result));
} catch (Exception e) {
log.setStatus(0); // 失败
log.setErrorMessage(e.getMessage());
throw e;
} finally {
// 计算耗时
log.setExecutionTime(System.currentTimeMillis() - startTime);
// 异步保存日志
OperationLogService.logService.saveLog(log);
}
return result;
}
// 获取注解信息
private OperationLog getOperationLogAnnotation(ProceedingJoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
return method.getAnnotation(OperationLog.class);
}
// 获取IP地址
private String getIpAddress() {
try {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attributes != null) {
HttpServletRequest request = attributes.getRequest();
String ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Real-IP");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
} catch (Exception e) {
logger.error("获取IP地址失败", e);
}
return "unknown";
}
// 获取当前用户ID
private Long getCurrentUserId() {
// 从SecurityContext或Session中获取
// 示例代码
return 1L;
}
// 获取当前用户名
private String getCurrentUsername() {
// 从SecurityContext或Session中获取
return "admin";
}
}
使用示例
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/users")
public class UserController {
@PostMapping
@OperationLog(module = "用户管理", operation = "新增", description = "创建新用户")
public User createUser(@RequestBody User user) {
// 业务逻辑
return userService.createUser(user);
}
@PutMapping("/{id}")
@OperationLog(module = "用户管理", operation = "修改", description = "修改用户信息")
public User updateUser(@PathVariable Long id, @RequestBody User user) {
// 业务逻辑
user.setId(id);
return userService.updateUser(user);
}
@DeleteMapping("/{id}")
@OperationLog(module = "用户管理", operation = "删除", description = "删除用户")
public void deleteUser(@PathVariable Long id) {
userService.deleteUser(id);
}
}
基于Spring的事件机制实现
操作日志事件
import org.springframework.context.ApplicationEvent;
public class OperationLogEvent extends ApplicationEvent {
private final OperationLogData logData;
public OperationLogEvent(Object source, OperationLogData logData) {
super(source);
this.logData = logData;
}
public OperationLogData getLogData() {
return logData;
}
}
操作日志数据
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class OperationLogData {
private String module;
private String operation;
private String description;
private String requestParams;
private String responseResult;
private Long operatorId;
private String operatorName;
private String ipAddress;
private LocalDateTime operationTime;
private Long executionTime;
private Integer status;
private String errorMessage;
}
事件监听器
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class OperationLogListener {
@EventListener
@Async // 异步处理
public void onOperationLogEvent(OperationLogEvent event) {
OperationLogData logData = event.getLogData();
// 保存日志到数据库
saveLog(logData);
}
private void saveLog(OperationLogData logData) {
// 实现日志保存逻辑
}
}
日志工具类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
@Component
public class OperationLogUtils {
@Autowired
private ApplicationEventPublisher eventPublisher;
public void recordLog(OperationLogData logData) {
eventPublisher.publishEvent(new OperationLogEvent(this, logData));
}
}
使用日志框架记录(简易版)
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SimpleLoggerExample {
private static final Logger logger = LoggerFactory.getLogger(SimpleLoggerExample.class);
public void businessMethod() {
// 记录操作日志
logger.info("用户操作日志: userId={}, operation={}, timestamp={}",
"12345", "查询订单", System.currentTimeMillis());
// 记录错误日志
try {
// 业务逻辑
} catch (Exception e) {
logger.error("操作失败: user={}, operation={}, error={}",
"12345", "创建订单", e.getMessage(), e);
}
}
}
异步批量处理方案
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
@Component
public class AsyncLogProcessor {
private final BlockingQueue<LogEntry> logQueue = new LinkedBlockingQueue<>(10000);
@Async
public void asyncProcessLog(LogEntry logEntry) {
// 异步处理日志
boolean offer = logQueue.offer(logEntry);
if (!offer) {
logger.warn("日志队列已满,日志丢失: {}", logEntry);
}
}
// 批量消费者
@Scheduled(fixedRate = 5000) // 每5秒批量处理一次
public void batchProcess() {
List<LogEntry> batchLogs = new ArrayList<>();
logQueue.drainTo(batchLogs, 100); // 每次最多处理100条
if (!batchLogs.isEmpty()) {
// 批量插入数据库
logRepository.batchInsert(batchLogs);
}
}
}
配置类示例
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
@Configuration
@EnableAsync
@EnableScheduling
public class LogConfig {
// 线程池配置
@Bean(name = "logExecutor")
public Executor logExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(4);
executor.setQueueCapacity(1000);
executor.setThreadNamePrefix("log-thread-");
executor.initialize();
return executor;
}
}
查询操作日志
import org.springframework.web.bind.annotation.*;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
@RestController
@RequestMapping("/api/logs")
public class OperationLogController {
@Autowired
private OperationLogService operationLogService;
@GetMapping
@OperationLog(module = "日志管理", operation = "查询", description = "查询操作日志", saveResult = true)
public Page<OperationLog> queryLogs(
@RequestParam(required = false) String module,
@RequestParam(required = false) String operation,
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate,
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
return operationLogService.queryLogs(module, operation, startDate, endDate,
PageRequest.of(page, size));
}
@GetMapping("/{id}")
public OperationLog getLogDetail(@PathVariable Long id) {
return operationLogService.getLogById(id);
}
}
使用要点
- 切面优先级:如果有多个切面,需要设置合适的优先级顺序
- 异步处理:建议使用异步方式保存日志,避免影响业务性能
- 敏感信息处理:记录参数时需要脱敏处理,如密码等
- 日志存储:根据业务量选择合适的存储方式(数据库、ES等)
- 日志清理:定期清理过期日志,避免数据膨胀
这个案例提供了完整的操作日志实现方案,可以根据实际需求选择合适的方案。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播