From 0d334523e9d18b548740af6583521b47b8171446 Mon Sep 17 00:00:00 2001 From: Charles7c Date: Mon, 23 Dec 2024 20:51:42 +0800 Subject: [PATCH] =?UTF-8?q?refactor(log):=20=E6=96=B0=E5=A2=9E=20LogHandle?= =?UTF-8?q?r=20=E6=8F=90=E5=8D=87=E6=97=A5=E5=BF=97=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E7=9A=84=E5=A4=8D=E7=94=A8=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../starter/log/aspect/AccessLogAspect.java | 45 +++++- .../starter/log/aspect/LogAspect.java | 141 +++-------------- .../autoconfigure/LogAutoConfiguration.java | 17 ++- .../starter/log/handler/AopLogHandler.java | 47 ++++++ .../log/handler/AbstractLogHandler.java | 143 ++++++++++++++++++ .../starter/log/handler/LogHandler.java | 100 ++++++++++++ .../autoconfigure/LogAutoConfiguration.java | 17 ++- .../log/handler/InterceptorLogHandler.java | 61 ++++++++ .../LogInterceptor.java | 127 +++------------- 9 files changed, 460 insertions(+), 238 deletions(-) create mode 100644 continew-starter-log/continew-starter-log-aop/src/main/java/top/continew/starter/log/handler/AopLogHandler.java create mode 100644 continew-starter-log/continew-starter-log-core/src/main/java/top/continew/starter/log/handler/AbstractLogHandler.java create mode 100644 continew-starter-log/continew-starter-log-core/src/main/java/top/continew/starter/log/handler/LogHandler.java create mode 100644 continew-starter-log/continew-starter-log-interceptor/src/main/java/top/continew/starter/log/handler/InterceptorLogHandler.java rename continew-starter-log/continew-starter-log-interceptor/src/main/java/top/continew/starter/log/{handler => interceptor}/LogInterceptor.java (52%) diff --git a/continew-starter-log/continew-starter-log-aop/src/main/java/top/continew/starter/log/aspect/AccessLogAspect.java b/continew-starter-log/continew-starter-log-aop/src/main/java/top/continew/starter/log/aspect/AccessLogAspect.java index 1d683824..5b54b694 100644 --- a/continew-starter-log/continew-starter-log-aop/src/main/java/top/continew/starter/log/aspect/AccessLogAspect.java +++ b/continew-starter-log/continew-starter-log-aop/src/main/java/top/continew/starter/log/aspect/AccessLogAspect.java @@ -19,7 +19,9 @@ package top.continew.starter.log.aspect; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.*; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.context.request.RequestContextHolder; @@ -47,12 +49,47 @@ public class AccessLogAspect { } /** - * 切点 - 匹配所有控制器层的方法 + * 切点 - 匹配所有控制器层的 GET 请求方法 */ - @Pointcut("execution(* *..controller.*.*(..)) || execution(* *..*Controller.*(..))") + @Pointcut("within(@org.springframework.web.bind.annotation.RequestMapping *)") public void pointcut() { } + /** + * 切点 - 匹配所有控制器层的 GET 请求方法 + */ + @Pointcut("within(@org.springframework.web.bind.annotation.GetMapping *)") + public void pointcutGet() { + } + + /** + * 切点 - 匹配所有控制器层的 POST 请求方法 + */ + @Pointcut("within(@org.springframework.web.bind.annotation.PostMapping *)") + public void pointcutPost() { + } + + /** + * 切点 - 匹配所有控制器层的 PUT 请求方法 + */ + @Pointcut("within(@org.springframework.web.bind.annotation.PutMapping *)") + public void pointcutPut() { + } + + /** + * 切点 - 匹配所有控制器层的 DELETE 请求方法 + */ + @Pointcut("within(@org.springframework.web.bind.annotation.DeleteMapping *)") + public void pointcutDelete() { + } + + /** + * 切点 - 匹配所有控制器层的 PATCH 请求方法 + */ + @Pointcut("within(@org.springframework.web.bind.annotation.PatchMapping *)") + public void pointcutPatch() { + } + /** * 打印访问日志 * @@ -60,7 +97,7 @@ public class AccessLogAspect { * @return 返回结果 * @throws Throwable 异常 */ - @Around("pointcut()") + @Around("pointcut() || pointcutGet() || pointcutPost() || pointcutPut() || pointcutDelete() || pointcutPatch()") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { Instant startTime = Instant.now(); // 非 Web 环境不记录 diff --git a/continew-starter-log/continew-starter-log-aop/src/main/java/top/continew/starter/log/aspect/LogAspect.java b/continew-starter-log/continew-starter-log-aop/src/main/java/top/continew/starter/log/aspect/LogAspect.java index 931a1ffe..4cd4cbd7 100644 --- a/continew-starter-log/continew-starter-log-aop/src/main/java/top/continew/starter/log/aspect/LogAspect.java +++ b/continew-starter-log/continew-starter-log-aop/src/main/java/top/continew/starter/log/aspect/LogAspect.java @@ -32,15 +32,11 @@ import org.springframework.web.context.request.ServletRequestAttributes; import top.continew.starter.log.annotation.Log; import top.continew.starter.log.autoconfigure.LogProperties; import top.continew.starter.log.dao.LogDao; -import top.continew.starter.log.enums.Include; -import top.continew.starter.log.http.recordable.impl.RecordableServletHttpRequest; -import top.continew.starter.log.http.recordable.impl.RecordableServletHttpResponse; +import top.continew.starter.log.handler.LogHandler; import top.continew.starter.log.model.LogRecord; import java.lang.reflect.Method; import java.time.Instant; -import java.util.HashSet; -import java.util.Set; /** * 日志切面 @@ -53,12 +49,14 @@ import java.util.Set; public class LogAspect { private static final Logger log = LoggerFactory.getLogger(LogAspect.class); - private final LogDao logDao; private final LogProperties logProperties; + private final LogHandler logHandler; + private final LogDao logDao; - public LogAspect(LogDao logDao, LogProperties logProperties) { - this.logDao = logDao; + public LogAspect(LogProperties logProperties, LogHandler logHandler, LogDao logDao) { this.logProperties = logProperties; + this.logHandler = logHandler; + this.logDao = logDao; } /** @@ -87,7 +85,7 @@ public class LogAspect { HttpServletResponse response = attributes.getResponse(); String errorMsg = null; // 开始记录 - LogRecord.Started startedLogRecord = LogRecord.start(startTime, new RecordableServletHttpRequest(request)); + LogRecord.Started startedLogRecord = logHandler.start(startTime, request); try { // 执行目标方法 return joinPoint.proceed(); @@ -95,48 +93,21 @@ public class LogAspect { errorMsg = CharSequenceUtil.sub(e.getMessage(), 0, 2000); throw e; } finally { - // 结束记录 - this.logFinish(startedLogRecord, errorMsg, response, joinPoint); - } - } - - /** - * 结束记录日志 - * - * @param startedLogRecord 日志记录器 - * @param errorMsg 异常信息 - * @param response 响应对象 - * @param joinPoint 切点 - */ - private void logFinish(LogRecord.Started startedLogRecord, - String errorMsg, - HttpServletResponse response, - ProceedingJoinPoint joinPoint) { - try { - Instant endTime = Instant.now(); - Method method = this.getMethod(joinPoint); - Class targetClass = joinPoint.getTarget().getClass(); - Log methodLog = method.getAnnotation(Log.class); - Log classLog = targetClass.getAnnotation(Log.class); - Set includeSet = this.getIncludes(methodLog, classLog); - LogRecord finishedLogRecord = startedLogRecord - .finish(endTime, new RecordableServletHttpResponse(response, response.getStatus()), includeSet); - // 记录异常信息 - if (errorMsg != null) { - finishedLogRecord.setErrorMsg(errorMsg); + try { + Instant endTime = Instant.now(); + Method targetMethod = this.getMethod(joinPoint); + Class targetClass = joinPoint.getTarget().getClass(); + LogRecord logRecord = logHandler.finish(startedLogRecord, endTime, response, logProperties + .getIncludes(), targetMethod, targetClass); + // 记录异常信息 + if (errorMsg != null) { + logRecord.setErrorMsg(errorMsg); + } + logDao.add(logRecord); + } catch (Exception e) { + log.error("Logging http log occurred an error: {}.", e.getMessage(), e); + throw e; } - // 记录日志描述 - if (includeSet.contains(Include.DESCRIPTION)) { - this.logDescription(finishedLogRecord, methodLog); - } - // 记录所属模块 - if (includeSet.contains(Include.MODULE)) { - this.logModule(finishedLogRecord, methodLog, classLog); - } - logDao.add(finishedLogRecord); - } catch (Exception e) { - log.error("Logging http log occurred an error: {}.", e.getMessage(), e); - throw e; } } @@ -150,74 +121,4 @@ public class LogAspect { MethodSignature signature = (MethodSignature)joinPoint.getSignature(); return signature.getMethod(); } - - /** - * 获取日志包含信息 - * - * @param methodLog 方法级 Log 注解 - * @param classLog 类级 Log 注解 - * @return 日志包含信息 - */ - private Set getIncludes(Log methodLog, Log classLog) { - Set includeSet = new HashSet<>(logProperties.getIncludes()); - if (null != classLog) { - processInclude(includeSet, classLog); - } - if (null != methodLog) { - processInclude(includeSet, methodLog); - } - return includeSet; - } - - /** - * 处理日志包含信息 - * - * @param includes 日志包含信息 - * @param logAnnotation Log 注解 - */ - private void processInclude(Set includes, Log logAnnotation) { - Include[] includeArr = logAnnotation.includes(); - if (includeArr.length > 0) { - includes.addAll(Set.of(includeArr)); - } - Include[] excludeArr = logAnnotation.excludes(); - if (excludeArr.length > 0) { - includes.removeAll(Set.of(excludeArr)); - } - } - - /** - * 记录描述 - * - * @param logRecord 日志信息 - * @param methodLog 方法级 Log 注解 - */ - private void logDescription(LogRecord logRecord, Log methodLog) { - // 例如:@Log("新增部门") -> 新增部门 - if (null != methodLog && CharSequenceUtil.isNotBlank(methodLog.value())) { - logRecord.setDescription(methodLog.value()); - } else { - logRecord.setDescription("请在该接口方法上指定日志描述"); - } - } - - /** - * 记录模块 - * - * @param logRecord 日志信息 - * @param methodLog 方法级 Log 注解 - * @param classLog 类级 Log 注解 - */ - private void logModule(LogRecord logRecord, Log methodLog, Log classLog) { - // 例如:@Log(module = "部门管理") -> 部门管理 - // 优先使用方法注解的模块 - if (null != methodLog && CharSequenceUtil.isNotBlank(methodLog.module())) { - logRecord.setModule(methodLog.module()); - return; - } - // 其次使用类注解的模块 - if (null != classLog) { - logRecord.setModule(CharSequenceUtil.blankToDefault(classLog.module(), "请在该接口类上指定所属模块")); - } - } } diff --git a/continew-starter-log/continew-starter-log-aop/src/main/java/top/continew/starter/log/autoconfigure/LogAutoConfiguration.java b/continew-starter-log/continew-starter-log-aop/src/main/java/top/continew/starter/log/autoconfigure/LogAutoConfiguration.java index 89c9a1fc..947d2e66 100644 --- a/continew-starter-log/continew-starter-log-aop/src/main/java/top/continew/starter/log/autoconfigure/LogAutoConfiguration.java +++ b/continew-starter-log/continew-starter-log-aop/src/main/java/top/continew/starter/log/autoconfigure/LogAutoConfiguration.java @@ -29,6 +29,8 @@ import top.continew.starter.log.aspect.AccessLogAspect; import top.continew.starter.log.aspect.LogAspect; import top.continew.starter.log.dao.LogDao; import top.continew.starter.log.dao.impl.DefaultLogDaoImpl; +import top.continew.starter.log.handler.AopLogHandler; +import top.continew.starter.log.handler.LogHandler; /** * 日志自动配置 @@ -53,12 +55,14 @@ public class LogAutoConfiguration { /** * 日志切面 * + * @param logHandler 日志处理器 + * @param logDao 日志持久层接口 * @return {@link LogAspect } */ @Bean @ConditionalOnMissingBean - public LogAspect logAspect(LogDao logDao) { - return new LogAspect(logDao, logProperties); + public LogAspect logAspect(LogHandler logHandler, LogDao logDao) { + return new LogAspect(logProperties, logHandler, logDao); } /** @@ -72,6 +76,15 @@ public class LogAutoConfiguration { return new AccessLogAspect(logProperties); } + /** + * 日志处理器 + */ + @Bean + @ConditionalOnMissingBean + public LogHandler logHandler() { + return new AopLogHandler(); + } + /** * 日志持久层接口 */ diff --git a/continew-starter-log/continew-starter-log-aop/src/main/java/top/continew/starter/log/handler/AopLogHandler.java b/continew-starter-log/continew-starter-log-aop/src/main/java/top/continew/starter/log/handler/AopLogHandler.java new file mode 100644 index 00000000..9ba6a9e7 --- /dev/null +++ b/continew-starter-log/continew-starter-log-aop/src/main/java/top/continew/starter/log/handler/AopLogHandler.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.starter.log.handler; + +import cn.hutool.core.text.CharSequenceUtil; +import top.continew.starter.log.model.LogRecord; + +import java.lang.reflect.Method; + +/** + * 日志处理器-AOP 版实现 + * + * @author Charles7c + * @since 2.8.0 + */ +public class AopLogHandler extends AbstractLogHandler { + + @Override + public void logDescription(LogRecord logRecord, Method targetMethod) { + super.logDescription(logRecord, targetMethod); + if (CharSequenceUtil.isBlank(logRecord.getDescription())) { + logRecord.setDescription("请在该接口方法上指定日志描述"); + } + } + + @Override + public void logModule(LogRecord logRecord, Method targetMethod, Class targetClass) { + super.logModule(logRecord, targetMethod, targetClass); + if (CharSequenceUtil.isBlank(logRecord.getModule())) { + logRecord.setModule("请在该接口类上指定所属模块"); + } + } +} diff --git a/continew-starter-log/continew-starter-log-core/src/main/java/top/continew/starter/log/handler/AbstractLogHandler.java b/continew-starter-log/continew-starter-log-core/src/main/java/top/continew/starter/log/handler/AbstractLogHandler.java new file mode 100644 index 00000000..26702246 --- /dev/null +++ b/continew-starter-log/continew-starter-log-core/src/main/java/top/continew/starter/log/handler/AbstractLogHandler.java @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.starter.log.handler; + +import cn.hutool.core.annotation.AnnotationUtil; +import cn.hutool.core.text.CharSequenceUtil; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import top.continew.starter.log.annotation.Log; +import top.continew.starter.log.enums.Include; +import top.continew.starter.log.http.recordable.impl.RecordableServletHttpRequest; +import top.continew.starter.log.http.recordable.impl.RecordableServletHttpResponse; +import top.continew.starter.log.model.LogRecord; + +import java.lang.reflect.Method; +import java.time.Instant; +import java.util.HashSet; +import java.util.Set; + +/** + * 日志处理器基类 + * + * @author Charles7c + * @since 2.8.0 + */ +public abstract class AbstractLogHandler implements LogHandler { + + @Override + public LogRecord.Started start(Instant startTime, HttpServletRequest request) { + return LogRecord.start(startTime, new RecordableServletHttpRequest(request)); + } + + @Override + public LogRecord finish(LogRecord.Started started, + Instant endTime, + HttpServletResponse response, + Set includes, + Method targetMethod, + Class targetClass) { + Set includeSet = this.getIncludes(includes, targetMethod, targetClass); + LogRecord logRecord = this.finish(started, endTime, response, includeSet); + // 记录日志描述 + if (includeSet.contains(Include.DESCRIPTION)) { + this.logDescription(logRecord, targetMethod); + } + // 记录所属模块 + if (includeSet.contains(Include.MODULE)) { + this.logModule(logRecord, targetMethod, targetClass); + } + return logRecord; + } + + @Override + public LogRecord finish(LogRecord.Started started, + Instant endTime, + HttpServletResponse response, + Set includes) { + return started.finish(endTime, new RecordableServletHttpResponse(response, response.getStatus()), includes); + } + + /** + * 记录日志描述 + * + * @param logRecord 日志记录 + * @param targetMethod 目标方法 + */ + @Override + public void logDescription(LogRecord logRecord, Method targetMethod) { + Log methodLog = AnnotationUtil.getAnnotation(targetMethod, Log.class); + // 例如:@Log("新增部门") -> 新增部门 + if (null != methodLog && CharSequenceUtil.isNotBlank(methodLog.value())) { + logRecord.setDescription(methodLog.value()); + } + } + + /** + * 记录所属模块 + * + * @param logRecord 日志记录 + * @param targetMethod 目标方法 + * @param targetClass 目标类 + */ + @Override + public void logModule(LogRecord logRecord, Method targetMethod, Class targetClass) { + Log methodLog = AnnotationUtil.getAnnotation(targetMethod, Log.class); + // 例如:@Log(module = "部门管理") -> 部门管理 + // 方法级注解优先级高于类级注解 + if (null != methodLog && CharSequenceUtil.isNotBlank(methodLog.module())) { + logRecord.setModule(methodLog.module()); + return; + } + Log classLog = AnnotationUtil.getAnnotation(targetClass, Log.class); + if (null != classLog && CharSequenceUtil.isNotBlank(classLog.module())) { + logRecord.setModule(classLog.module()); + } + } + + @Override + public Set getIncludes(Set includes, Method targetMethod, Class targetClass) { + Log classLog = AnnotationUtil.getAnnotation(targetClass, Log.class); + Set includeSet = new HashSet<>(includes); + if (null != classLog) { + this.processInclude(includeSet, classLog); + } + // 方法级注解优先级高于类级注解 + Log methodLog = AnnotationUtil.getAnnotation(targetMethod, Log.class); + if (null != methodLog) { + this.processInclude(includeSet, methodLog); + } + return includeSet; + } + + /** + * 处理日志包含信息 + * + * @param includes 日志包含信息 + * @param logAnnotation Log 注解 + */ + private void processInclude(Set includes, Log logAnnotation) { + Include[] includeArr = logAnnotation.includes(); + if (includeArr.length > 0) { + includes.addAll(Set.of(includeArr)); + } + Include[] excludeArr = logAnnotation.excludes(); + if (excludeArr.length > 0) { + includes.removeAll(Set.of(excludeArr)); + } + } +} diff --git a/continew-starter-log/continew-starter-log-core/src/main/java/top/continew/starter/log/handler/LogHandler.java b/continew-starter-log/continew-starter-log-core/src/main/java/top/continew/starter/log/handler/LogHandler.java new file mode 100644 index 00000000..fbc4c229 --- /dev/null +++ b/continew-starter-log/continew-starter-log-core/src/main/java/top/continew/starter/log/handler/LogHandler.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.starter.log.handler; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import top.continew.starter.log.enums.Include; +import top.continew.starter.log.model.LogRecord; + +import java.lang.reflect.Method; +import java.time.Instant; +import java.util.Set; + +/** + * 日志处理器 + * + * @author Charles7c + * @since 2.8.0 + */ +public interface LogHandler { + + /** + * 开始日志记录 + * + * @param startTime 开始时间 + * @param request 请求对象 + * @return 日志记录器 + */ + LogRecord.Started start(Instant startTime, HttpServletRequest request); + + /** + * 结束日志记录 + * + * @param started 开始日志记录器 + * @param endTime 结束时间 + * @param response 响应对象 + * @param includes 包含信息 + * @return 日志记录 + */ + LogRecord finish(LogRecord.Started started, Instant endTime, HttpServletResponse response, Set includes); + + /** + * 结束日志记录 + * + * @param started 开始日志记录器- + * @param endTime 结束时间 + * @param response 响应对象 + * @param includes 包含信息 + * @param targetMethod 目标方法 + * @param targetClass 目标类 + * @return 日志记录 + */ + LogRecord finish(LogRecord.Started started, + Instant endTime, + HttpServletResponse response, + Set includes, + Method targetMethod, + Class targetClass); + + /** + * 记录日志描述 + * + * @param logRecord 日志记录 + * @param targetMethod 目标方法 + */ + void logDescription(LogRecord logRecord, Method targetMethod); + + /** + * 记录所属模块 + * + * @param logRecord 日志记录 + * @param targetMethod 目标方法 + * @param targetClass 目标类 + */ + void logModule(LogRecord logRecord, Method targetMethod, Class targetClass); + + /** + * 获取日志包含信息 + * + * @param includes 默认包含信息 + * @param targetMethod 目标方法 + * @param targetClass 目标类 + * @return 日志包含信息 + */ + Set getIncludes(Set includes, Method targetMethod, Class targetClass); +} diff --git a/continew-starter-log/continew-starter-log-interceptor/src/main/java/top/continew/starter/log/autoconfigure/LogAutoConfiguration.java b/continew-starter-log/continew-starter-log-interceptor/src/main/java/top/continew/starter/log/autoconfigure/LogAutoConfiguration.java index 5243476e..d3a917be 100644 --- a/continew-starter-log/continew-starter-log-interceptor/src/main/java/top/continew/starter/log/autoconfigure/LogAutoConfiguration.java +++ b/continew-starter-log/continew-starter-log-interceptor/src/main/java/top/continew/starter/log/autoconfigure/LogAutoConfiguration.java @@ -26,11 +26,13 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import top.continew.starter.log.annotation.ConditionalOnEnabledLog; import top.continew.starter.log.dao.LogDao; import top.continew.starter.log.dao.impl.DefaultLogDaoImpl; -import top.continew.starter.log.annotation.ConditionalOnEnabledLog; +import top.continew.starter.log.handler.InterceptorLogHandler; import top.continew.starter.log.handler.LogFilter; -import top.continew.starter.log.handler.LogInterceptor; +import top.continew.starter.log.handler.LogHandler; +import top.continew.starter.log.interceptor.LogInterceptor; /** * 日志自动配置 @@ -53,7 +55,7 @@ public class LogAutoConfiguration implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { - registry.addInterceptor(new LogInterceptor(logDao(), logProperties)); + registry.addInterceptor(new LogInterceptor(logProperties, logHandler(), logDao())); } /** @@ -65,6 +67,15 @@ public class LogAutoConfiguration implements WebMvcConfigurer { return new LogFilter(logProperties); } + /** + * 日志处理器 + */ + @Bean + @ConditionalOnMissingBean + public LogHandler logHandler() { + return new InterceptorLogHandler(); + } + /** * 日志持久层接口 */ diff --git a/continew-starter-log/continew-starter-log-interceptor/src/main/java/top/continew/starter/log/handler/InterceptorLogHandler.java b/continew-starter-log/continew-starter-log-interceptor/src/main/java/top/continew/starter/log/handler/InterceptorLogHandler.java new file mode 100644 index 00000000..a44435f4 --- /dev/null +++ b/continew-starter-log/continew-starter-log-interceptor/src/main/java/top/continew/starter/log/handler/InterceptorLogHandler.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.starter.log.handler; + +import cn.hutool.core.annotation.AnnotationUtil; +import cn.hutool.core.text.CharSequenceUtil; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import top.continew.starter.log.model.LogRecord; + +import java.lang.reflect.Method; + +/** + * 日志处理器-拦截器版实现 + * + * @author Charles7c + * @since 2.8.0 + */ +public class InterceptorLogHandler extends AbstractLogHandler { + + @Override + public void logDescription(LogRecord logRecord, Method targetMethod) { + super.logDescription(logRecord, targetMethod); + if (CharSequenceUtil.isNotBlank(logRecord.getDescription())) { + return; + } + // 例如:@Operation(summary="新增部门") -> 新增部门 + Operation methodOperation = AnnotationUtil.getAnnotation(targetMethod, Operation.class); + if (null != methodOperation) { + logRecord.setDescription(CharSequenceUtil.blankToDefault(methodOperation.summary(), "请在该接口方法上指定日志描述")); + } + } + + @Override + public void logModule(LogRecord logRecord, Method targetMethod, Class targetClass) { + super.logModule(logRecord, targetMethod, targetClass); + if (CharSequenceUtil.isNotBlank(logRecord.getModule())) { + return; + } + // 例如:@Tag(name = "部门管理") -> 部门管理 + Tag classTag = AnnotationUtil.getAnnotation(targetClass, Tag.class); + if (null != classTag) { + String name = classTag.name(); + logRecord.setModule(CharSequenceUtil.blankToDefault(name, "请在该接口类上指定所属模块")); + } + } +} diff --git a/continew-starter-log/continew-starter-log-interceptor/src/main/java/top/continew/starter/log/handler/LogInterceptor.java b/continew-starter-log/continew-starter-log-interceptor/src/main/java/top/continew/starter/log/interceptor/LogInterceptor.java similarity index 52% rename from continew-starter-log/continew-starter-log-interceptor/src/main/java/top/continew/starter/log/handler/LogInterceptor.java rename to continew-starter-log/continew-starter-log-interceptor/src/main/java/top/continew/starter/log/interceptor/LogInterceptor.java index 9beb12d3..73b633f9 100644 --- a/continew-starter-log/continew-starter-log-interceptor/src/main/java/top/continew/starter/log/handler/LogInterceptor.java +++ b/continew-starter-log/continew-starter-log-interceptor/src/main/java/top/continew/starter/log/interceptor/LogInterceptor.java @@ -14,13 +14,11 @@ * limitations under the License. */ -package top.continew.starter.log.handler; +package top.continew.starter.log.interceptor; -import cn.hutool.core.text.CharSequenceUtil; import com.alibaba.ttl.TransmittableThreadLocal; import io.swagger.v3.oas.annotations.Hidden; import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.slf4j.Logger; @@ -28,18 +26,15 @@ import org.slf4j.LoggerFactory; import org.springframework.lang.NonNull; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; -import top.continew.starter.log.http.recordable.impl.RecordableServletHttpRequest; -import top.continew.starter.log.http.recordable.impl.RecordableServletHttpResponse; import top.continew.starter.log.annotation.Log; -import top.continew.starter.log.dao.LogDao; -import top.continew.starter.log.enums.Include; -import top.continew.starter.log.model.LogRecord; import top.continew.starter.log.autoconfigure.LogProperties; +import top.continew.starter.log.dao.LogDao; +import top.continew.starter.log.handler.LogHandler; +import top.continew.starter.log.model.LogRecord; +import java.lang.reflect.Method; import java.time.Duration; import java.time.Instant; -import java.util.HashSet; -import java.util.Set; /** * 日志拦截器 @@ -50,14 +45,16 @@ import java.util.Set; public class LogInterceptor implements HandlerInterceptor { private static final Logger log = LoggerFactory.getLogger(LogInterceptor.class); - private final LogDao logDao; private final LogProperties logProperties; + private final LogHandler logHandler; + private final LogDao logDao; private final TransmittableThreadLocal timeTtl = new TransmittableThreadLocal<>(); private final TransmittableThreadLocal logTtl = new TransmittableThreadLocal<>(); - public LogInterceptor(LogDao logDao, LogProperties logProperties) { - this.logDao = logDao; + public LogInterceptor(LogProperties logProperties, LogHandler logHandler, LogDao logDao) { this.logProperties = logProperties; + this.logHandler = logHandler; + this.logDao = logDao; } @Override @@ -69,8 +66,9 @@ public class LogInterceptor implements HandlerInterceptor { log.info("[{}] {}", request.getMethod(), request.getRequestURI()); timeTtl.set(startTime); } + // 开始日志记录 if (this.isRequestRecord(handler, request)) { - LogRecord.Started startedLogRecord = LogRecord.start(startTime, new RecordableServletHttpRequest(request)); + LogRecord.Started startedLogRecord = logHandler.start(startTime, request); logTtl.set(startedLogRecord); } return true; @@ -92,21 +90,13 @@ public class LogInterceptor implements HandlerInterceptor { if (null == startedLogRecord) { return; } + // 结束日志记录 HandlerMethod handlerMethod = (HandlerMethod)handler; - Log methodLog = handlerMethod.getMethodAnnotation(Log.class); - Log classLog = handlerMethod.getBeanType().getDeclaredAnnotation(Log.class); - Set includeSet = this.getIncludes(methodLog, classLog); - LogRecord finishedLogRecord = startedLogRecord - .finish(endTime, new RecordableServletHttpResponse(response, response.getStatus()), includeSet); - // 记录日志描述 - if (includeSet.contains(Include.DESCRIPTION)) { - this.logDescription(finishedLogRecord, methodLog, handlerMethod); - } - // 记录所属模块 - if (includeSet.contains(Include.MODULE)) { - this.logModule(finishedLogRecord, methodLog, classLog, handlerMethod); - } - logDao.add(finishedLogRecord); + Method targetMethod = handlerMethod.getMethod(); + Class targetClass = handlerMethod.getBeanType(); + LogRecord logRecord = logHandler.finish(startedLogRecord, endTime, response, logProperties + .getIncludes(), targetMethod, targetClass); + logDao.add(logRecord); } catch (Exception ex) { log.error("Logging http log occurred an error: {}.", ex.getMessage(), ex); throw ex; @@ -116,87 +106,6 @@ public class LogInterceptor implements HandlerInterceptor { } } - /** - * 获取日志包含信息 - * - * @param methodLog 方法级 Log 注解 - * @param classLog 类级 Log 注解 - * @return 日志包含信息 - */ - private Set getIncludes(Log methodLog, Log classLog) { - Set includeSet = new HashSet<>(logProperties.getIncludes()); - if (null != classLog) { - this.processInclude(includeSet, classLog); - } - if (null != methodLog) { - this.processInclude(includeSet, methodLog); - } - return includeSet; - } - - /** - * 处理日志包含信息 - * - * @param includes 日志包含信息 - * @param logAnnotation Log 注解 - */ - private void processInclude(Set includes, Log logAnnotation) { - Include[] includeArr = logAnnotation.includes(); - if (includeArr.length > 0) { - includes.addAll(Set.of(includeArr)); - } - Include[] excludeArr = logAnnotation.excludes(); - if (excludeArr.length > 0) { - includes.removeAll(Set.of(excludeArr)); - } - } - - /** - * 记录描述 - * - * @param logRecord 日志信息 - * @param methodLog 方法级 Log 注解 - * @param handlerMethod 处理器方法 - */ - private void logDescription(LogRecord logRecord, Log methodLog, HandlerMethod handlerMethod) { - // 例如:@Log("新增部门") -> 新增部门 - if (null != methodLog && CharSequenceUtil.isNotBlank(methodLog.value())) { - logRecord.setDescription(methodLog.value()); - return; - } - // 例如:@Operation(summary="新增部门") -> 新增部门 - Operation methodOperation = handlerMethod.getMethodAnnotation(Operation.class); - if (null != methodOperation) { - logRecord.setDescription(CharSequenceUtil.blankToDefault(methodOperation.summary(), "请在该接口方法上指定日志描述")); - } - } - - /** - * 记录模块 - * - * @param logRecord 日志信息 - * @param methodLog 方法级 Log 注解 - * @param classLog 类级 Log 注解 - * @param handlerMethod 处理器方法 - */ - private void logModule(LogRecord logRecord, Log methodLog, Log classLog, HandlerMethod handlerMethod) { - // 例如:@Log(module = "部门管理") -> 部门管理 - if (null != methodLog && CharSequenceUtil.isNotBlank(methodLog.module())) { - logRecord.setModule(methodLog.module()); - return; - } - if (null != classLog && CharSequenceUtil.isNotBlank(classLog.module())) { - logRecord.setModule(classLog.module()); - return; - } - // 例如:@Tag(name = "部门管理") -> 部门管理 - Tag classTag = handlerMethod.getBeanType().getDeclaredAnnotation(Tag.class); - if (null != classTag) { - String name = classTag.name(); - logRecord.setModule(CharSequenceUtil.blankToDefault(name, "请在该接口类上指定所属模块")); - } - } - /** * 是否要记录日志 *