网站首页 > 技术文章 正文
springboot中记录操作日志的方式有哪些呢?他们各自优缺点你了解吗?这个问题不仅工作中常常会用到,而且面试中也是高频出现的问题,今天就自己多年工作经验总结下。
1.利用 AOP 切面拦截请求:通过在切面中拦截请求,获取请求相关的信息(如请求 URL、请求参数、用户信息等),并将这些信息记录到操作日志中。这种方式比较常见,也比较灵活,可以记录所有的请求操作。
2.利用 Filter 过滤器拦截请求:与 AOP 类似,通过在过滤器中拦截请求,获取请求相关信息,并将这些信息记录到操作日志中。但相比 AOP,Filter 更为底层,能够获取更多的请求信息。
3.利用注解方式记录操作日志:通过在方法上添加注解,在方法执行前后获取请求相关的信息,并将这些信息记录到操作日志中。这种方式比较简单,但需要手动添加注解,对于已经存在的代码需要修改,比较繁琐。
4.利用拦截器拦截请求:与 AOP、Filter 类似,拦截器也可以拦截请求,获取请求相关信息,并将这些信息记录到操作日志中。不同的是,拦截器是 Spring MVC 中的组件,因此可以很方便地获取请求和响应相关的信息。
5.利用 Spring Boot 的 Actuator 模块记录操作日志:Actuator 是 Spring Boot 提供的一组监控和管理功能,可以方便地查看应用程序的运行状态和指标数据。其中,AuditingEndpoint 和 HttpTraceEndpoint 组件可以记录应用程序的审计和 HTTP 请求相关信息,包括操作日志。
下面逐一介绍下具体实现流程
一、“利用 AOP 切面拦截请求”这种方式具体实现流程和代码
- 定义一个切面类来实现记录操作日志的逻辑:
@Aspect
@Component
public class LogAspect {
private static final Logger logger = LoggerFactory.getLogger(LogAspect.class);
@Autowired
private LogService logService;
@Pointcut("execution(public * com.example.demo.controller..*.*(..))")
public void webLog() {
}
@Before("webLog()")
public void doBefore(JoinPoint joinPoint) {
// 在方法执行前记录日志
// 获取请求的方法名和参数
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
// 将日志写入数据库
Log log = new Log();
log.setOperation(methodName);
log.setParams(Arrays.toString(args));
logService.addLog(log);
logger.info("日志记录成功");
}
@AfterReturning(returning = "result", pointcut = "webLog()")
public void doAfterReturning(Object result) {
// 在方法执行后记录日志
logger.info("返回值: " + result);
}
}
- 定义一个日志实体类:
public class Log {
private Long id;
private String operation;
private String params;
private Date createTime;
// 省略 getter 和 setter 方法
}
- 定义一个记录日志的服务接口和实现类:
public interface LogService {
void addLog(Log log);
}
@Service
public class LogServiceImpl implements LogService {
@Autowired
private LogDao logDao;
@Override
public void addLog(Log log) {
log.setCreateTime(new Date());
logDao.add(log);
}
}
- 配置 AOP 切面:
@Configuration
@EnableAspectJAutoProxy
public class AopConfig {
}
- 在日志配置文件中配置日志输出:
<logger name="com.example.demo.aspect.LogAspect" level="INFO"/>
这样,每当请求经过被 @Controller 或 @RestController 注解的方法时,LogAspect 切面类中的 doBefore() 方法就会被执行,将请求的方法名和参数记录到数据库中。在方法执行后,doAfterReturning() 方法将会记录返回值。通过这种方式,可以方便地记录操作日志。
二、“利用 Filter 过滤器拦截请求”这种方式具体实现流程和代码
使用 Filter 过滤器记录操作日志的方式实现流程如下:
- 编写 Filter 类
编写一个实现 javax.servlet.Filter 接口的 Filter 类,重写其 doFilter 方法,在该方法中实现日志记录逻辑。
@Component
public class LogFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// 从 servletRequest 中获取请求信息
HttpServletRequest request = (HttpServletRequest) servletRequest;
String url = request.getRequestURI(); // 请求的 URL
String method = request.getMethod(); // 请求的方法
String queryString = request.getQueryString(); // 请求的查询字符串
// 执行目标资源,即进入下一个过滤器或 Servlet
filterChain.doFilter(servletRequest, servletResponse);
// 从 servletResponse 中获取响应信息
HttpServletResponse response = (HttpServletResponse) servletResponse;
int status = response.getStatus(); // 响应的状态码
// 记录日志
log.info("请求 {} {} {},响应状态码为 {}", method, url, queryString, status);
//保存数据库的操作省了
}
}
- 在配置类中注册 Filter
在 Spring Boot 应用的配置类中注册 Filter:
javaCopy code@Configuration
public class WebConfig {
@Bean
public FilterRegistrationBean<LogFilter> logFilter() {
FilterRegistrationBean<LogFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new LogFilter());
registrationBean.addUrlPatterns("/*");
registrationBean.setName("LogFilter");
registrationBean.setOrder(1);
return registrationBean;
}
}
在该配置类中创建 FilterRegistrationBean 对象,并将 LogFilter 类型的 Bean 注入到其中。通过设置 addUrlPatterns 方法设置需要拦截的 URL 模式,通过 setName 方法设置 Filter 的名称,通过 setOrder 方法设置 Filter 的执行顺序。
通过以上两步即可实现 Filter 方式记录操作日志的功能。在应用启动后,每次发起请求时,Filter 将会记录该请求的相关信息,并输出到日志中。
三、“利用注解方式记录操作日志”这种方式具体实现流程和代码
利用注解方式记录操作日志的实现步骤如下:
- 定义注解:首先需要定义一个注解,用于标识需要记录操作日志的方法。可以定义在方法上或者类上,具体根据需求而定。
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface LogAnnotation {
String value() default "";
}
- 实现注解处理器:定义一个注解处理器,用于处理被@LogAnnotation注解标记的方法或类,并记录操作日志。
@Component
@Aspect
public class LogAspect {
@Autowired
private LogService logService;
@Pointcut("@annotation(com.example.demo.annotation.LogAnnotation)")
public void pointcut() {}
@Around("pointcut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
// 获取注解内容
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class);
String operation = logAnnotation.value();
// 记录操作日志
Log log = new Log();
log.setOperation(operation);
logService.save(log);
// 执行原方法
Object result = point.proceed();
return result;
}
}
- 在需要记录操作日志的方法上添加注解:在需要记录操作日志的方法上添加@LogAnnotation注解,并设置注解内容。
@LogAnnotation("登录")
public void login(String username, String password) {
// do something
}
通过以上步骤,即可实现利用注解方式记录操作日志。
需要注意的是,使用注解方式记录操作日志时,需要将注解处理器添加到Spring容器中,并且需要在AOP配置文件中添加对该处理器的配置。具体代码可以参考以下示例:
@Configuration
public class AppConfig {
@Bean
public LogAspect logAspect() {
return new LogAspect();
}
}
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AopConfig {
}
以上示例中,AppConfig类用于将注解处理器添加到Spring容器中,AopConfig类用于开启AOP支持并添加对注解处理器的配置。
四、“利用拦截器拦截请求”这种方式具体实现流程和代码
利用拦截器拦截请求记录操作日志的实现流程和代码如下:
- 定义一个实现了 HandlerInterceptor 接口的拦截器类,实现 preHandle() 方法和 afterCompletion() 方法。其中,preHandle() 方法会在请求处理之前执行,afterCompletion() 方法会在请求处理完成之后执行。
public class LogInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 在请求处理之前记录日志
String url = request.getRequestURI();
String method = request.getMethod();
String params = request.getQueryString();
String ip = request.getRemoteAddr();
// ...
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 在请求处理完成之后记录日志
String url = request.getRequestURI();
String method = request.getMethod();
String params = request.getQueryString();
String ip = request.getRemoteAddr();
// ...
}
}
- 在 Spring Boot 应用程序的配置类中注册拦截器。
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LogInterceptor logInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(logInterceptor);
}
}
- 为需要记录操作日志的请求方法添加注解,例如 @LogAnnotation。
@GetMapping("/user/{id}")
@LogAnnotation("查询用户信息")
public User getUser(@PathVariable Long id) {
return userService.getUserById(id);
}
- 定义一个注解解析器,用于解析 @LogAnnotation 注解中的信息并在拦截器中记录日志。
@Aspect
@Component
public class LogAnnotationAspect {
@Autowired
private LogInterceptor logInterceptor;
@Pointcut("@annotation(com.example.demo.annotation.LogAnnotation)")
public void logPointCut() {}
@Before("logPointCut() && @annotation(logAnnotation)")
public void before(JoinPoint joinPoint, LogAnnotation logAnnotation) throws Throwable {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
logInterceptor.logBefore(request, joinPoint, logAnnotation);
}
@AfterReturning(returning = "ret", pointcut = "logPointCut() && @annotation(logAnnotation)")
public void afterReturning(Object ret, LogAnnotation logAnnotation) throws Throwable {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
logInterceptor.logAfter(request, logAnnotation);
}
}
- 在拦截器中记录日志。
@Component
public class LogInterceptor {
public void logBefore(HttpServletRequest request, JoinPoint joinPoint, LogAnnotation logAnnotation) {
// 在请求处理之前记录日志
String url = request.getRequestURI();
String method = request.getMethod();
String params = Arrays.toString(joinPoint.getArgs());
String ip = request.getRemoteAddr();
String operation = logAnnotation.value();
// ...
}
public void logAfter(HttpServletRequest request, LogAnnotation logAnnotation) {
// 在请求处理完成之后记录日志
String url = request.getRequestURI();
String method = request.getMethod();
String ip = request.getRemoteAddr();
String operation = logAnnotation.value();
// ...
}
}
五、“利用 Spring Boot 的 Actuator 模块记录操作日志”这种方式具体实现流程和代码
利用 Spring Boot 的 Actuator 模块记录操作日志可以通过集成 Actuator 模块中的 Audit 功能来实现。
- 集成 Actuator 模块
在 pom.xml 文件中添加 Actuator 依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
- 配置 Audit
在 application.yml 或 application.properties 文件中添加 Audit 相关配置:
management:
audit:
enabled: true
record-data: true
其中 enabled 属性表示是否启用 Audit,record-data 属性表示是否记录请求和响应数据。
- 编写 Controller
在需要记录操作日志的 Controller 方法上添加 @Audit 注解:
@RestController
public class UserController {
@Autowired
private UserService userService;
@Audit("查询用户列表")
@GetMapping("/users")
public List<User> getUsers() {
return userService.getUsers();
}
}
- 测试
启动应用程序,然后访问 http://localhost:8080/actuator/auditevents,可以看到 Audit 记录的事件列表。其中,data.request 和 data.response 字段记录了请求和响应数据。
注意:需要启用 Spring Security 来保护 Actuator 端点,避免未授权访问 Audit 数据。
spring:
security:
user:
name: admin
password: password
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
audit:
enabled: true
sensitive: true
security:
roles: ACTUATOR_ADMIN
以上就是利用 Spring Boot 的 Actuator 模块记录操作日志的具体实现流程和代码。
下面总结下每种方式的优缺点
1、利用 AOP 切面拦截请求
优点:
可以精细地控制需要记录的操作和不需要记录的操作;
可以统一处理异常,避免记录操作日志时出现异常而中断;
可以通过注解方式方便地对不同的操作类型进行区分。
缺点:
会对系统性能造成一定的影响;
需要熟悉 AOP 的使用方法。
2、利用 Filter 过滤器拦截请求
优点:
可以拦截所有请求,无需配置;
可以统一处理异常,避免记录操作日志时出现异常而中断;
可以通过注解方式方便地对不同的操作类型进行区分。
缺点:
不能精细地控制需要记录的操作和不需要记录的操作;
会对系统性能造成一定的影响;
需要熟悉 Filter 的使用方法。
3、利用注解方式记录操作日志
优点:
可以精细地控制需要记录的操作和不需要记录的操作;
可以通过注解方式方便地对不同的操作类型进行区分。
缺点:
需要在每个需要记录操作日志的方法上添加注解,工作量较大;
如果有很多需要记录操作日志的方法,容易造成代码冗余。
4、利用拦截器拦截请求
优点:
可以拦截所有请求,无需配置;
可以统一处理异常,避免记录操作日志时出现异常而中断;
可以通过注解方式方便地对不同的操作类型进行区分。
缺点:
不能精细地控制需要记录的操作和不需要记录的操作;
会对系统性能造成一定的影响;
需要熟悉拦截器的使用方法。
5、利用 Spring Boot 的 Actuator 模块记录操作日志
优点:
可以快速地集成到 Spring Boot 项目中,无需编写额外的代码;
可以获取到系统的运行状态,方便进行监控。
缺点:
不能精细地控制需要记录的操作和不需要记录的操作;
不能记录用户的操作内容;
只能获取到系统的运行状态,不能记录其他自定义的操作信息。
个人推荐:
这可能不是最适合你的方式,因为不同的场景和需求可能会需要不同的方式。我认为比较推荐的两种方式和它们的优点。
1、利用 AOP 切面拦截请求。这种方式具有高度的灵活性,可以拦截任意的请求,并且可以通过切面的方式进行统一的处理,减少了代码的冗余。此外,AOP 还支持对请求的参数、返回值等进行拦截和处理。缺点是配置比较繁琐,需要使用 AOP 技术,对开发人员的技能要求比较高。
2、利用注解方式记录操作日志。这种方式比较简单,只需要在需要记录操作日志的方法上添加注解即可,对代码的侵入性比较小。此外,可以根据注解的不同实现不同的日志记录策略。缺点是需要开发人员手动添加注解,如果有大量的方法需要记录操作日志,会比较繁琐。
总结不易,如果对您有帮助,希望您点赞、收藏、评论。有问题可以留言看到会回复大家,谢谢。
猜你喜欢
- 2024-12-29 基于 SLF4J 的 MDC 实现日志链路追踪详解
- 2024-12-29 使用Flume同步日志到Kafka flume收集日志到hdfs
- 2024-12-29 Python日志模块logging python logger日志级别
- 2024-12-29 在Spring Boot中通过AOP技术实现日志拦截操作
- 2024-12-29 [编程基础] Python日志记录库logging总结
- 2024-12-29 如何将日志记录到 Windows事件日志 中
- 2024-12-29 SpringBoot集成logback异步日志 springboot集成日志log4j2
- 2024-12-29 Spring Boot中的Logback日志配置详解
- 2024-12-29 Linux 系统日志写入记录命令用法(logger)
- 2024-12-29 Python logging模块日志相关功能及应用示例
- 02-21走进git时代, 你该怎么玩?_gits
- 02-21GitHub是什么?它可不仅仅是云中的Git版本控制器
- 02-21Git常用操作总结_git基本用法
- 02-21为什么互联网巨头使用Git而放弃SVN?(含核心命令与原理)
- 02-21Git 高级用法,喜欢就拿去用_git基本用法
- 02-21Git常用命令和Git团队使用规范指南
- 02-21总结几个常用的Git命令的使用方法
- 02-21Git工作原理和常用指令_git原理详解
- 最近发表
- 标签列表
-
- cmd/c (57)
- c++中::是什么意思 (57)
- sqlset (59)
- ps可以打开pdf格式吗 (58)
- phprequire_once (61)
- localstorage.removeitem (74)
- routermode (59)
- vector线程安全吗 (70)
- & (66)
- java (73)
- org.redisson (64)
- log.warn (60)
- cannotinstantiatethetype (62)
- js数组插入 (83)
- resttemplateokhttp (59)
- gormwherein (64)
- linux删除一个文件夹 (65)
- mac安装java (72)
- reader.onload (61)
- outofmemoryerror是什么意思 (64)
- flask文件上传 (63)
- eacces (67)
- 查看mysql是否启动 (70)
- java是值传递还是引用传递 (58)
- 无效的列索引 (74)