From c0d3b26c769d53ca2b48ecf30c109a904a73305f Mon Sep 17 00:00:00 2001 From: lijiahang Date: Thu, 29 Jun 2023 13:48:11 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E6=89=93=E5=8D=B0=E5=99=A8.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/annotation/IgnoreLog.java | 2 +- .../ops/framework/common/utils/AnsiCode.java | 2 +- .../framework/mybatis/type/ITypeHandler.java | 4 +- .../spring-configuration-metadata.json | 46 +++ .../orion-ops-spring-boot-starter-web/pom.xml | 14 + .../config/OrionLogPrinterConfiguration.java | 42 ++- .../web/core/config/LogPrinterConfig.java | 38 +++ .../core/config/LogPrinterFieldConfig.java | 36 +++ .../BaseLogPrinterInterceptor.java | 269 ++++++++++++++++++ .../core/interceptor/LogPrintInterceptor.java | 154 ---------- .../interceptor/LogPrinterInterceptor.java | 14 + .../PrettyLogPrinterInterceptor.java | 101 +++++++ .../ops/framework/web/core/utils/Utils.java | 45 +++ .../spring-configuration-metadata.json | 52 ++++ .../src/main/resources/application.yaml | 14 +- 15 files changed, 668 insertions(+), 165 deletions(-) create mode 100644 orion-ops-framework/orion-ops-spring-boot-starter-swagger/src/main/resources/META-INF/spring-configuration-metadata.json create mode 100644 orion-ops-framework/orion-ops-spring-boot-starter-web/src/main/java/com/orion/ops/framework/web/core/config/LogPrinterConfig.java create mode 100644 orion-ops-framework/orion-ops-spring-boot-starter-web/src/main/java/com/orion/ops/framework/web/core/config/LogPrinterFieldConfig.java create mode 100644 orion-ops-framework/orion-ops-spring-boot-starter-web/src/main/java/com/orion/ops/framework/web/core/interceptor/BaseLogPrinterInterceptor.java delete mode 100644 orion-ops-framework/orion-ops-spring-boot-starter-web/src/main/java/com/orion/ops/framework/web/core/interceptor/LogPrintInterceptor.java create mode 100644 orion-ops-framework/orion-ops-spring-boot-starter-web/src/main/java/com/orion/ops/framework/web/core/interceptor/LogPrinterInterceptor.java create mode 100644 orion-ops-framework/orion-ops-spring-boot-starter-web/src/main/java/com/orion/ops/framework/web/core/interceptor/PrettyLogPrinterInterceptor.java create mode 100644 orion-ops-framework/orion-ops-spring-boot-starter-web/src/main/java/com/orion/ops/framework/web/core/utils/Utils.java create mode 100644 orion-ops-framework/orion-ops-spring-boot-starter-web/src/main/resources/META-INF/spring-configuration-metadata.json diff --git a/orion-ops-framework/orion-ops-common/src/main/java/com/orion/ops/framework/common/annotation/IgnoreLog.java b/orion-ops-framework/orion-ops-common/src/main/java/com/orion/ops/framework/common/annotation/IgnoreLog.java index 1e9b5682..7db9ae62 100644 --- a/orion-ops-framework/orion-ops-common/src/main/java/com/orion/ops/framework/common/annotation/IgnoreLog.java +++ b/orion-ops-framework/orion-ops-common/src/main/java/com/orion/ops/framework/common/annotation/IgnoreLog.java @@ -9,7 +9,7 @@ import java.lang.annotation.*; * @version 1.0.0 * @since 2022/4/20 10:33 */ -@Target({ElementType.METHOD}) +@Target({ElementType.METHOD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface IgnoreLog { diff --git a/orion-ops-framework/orion-ops-common/src/main/java/com/orion/ops/framework/common/utils/AnsiCode.java b/orion-ops-framework/orion-ops-common/src/main/java/com/orion/ops/framework/common/utils/AnsiCode.java index 5a0624fb..fe8eb9b0 100644 --- a/orion-ops-framework/orion-ops-common/src/main/java/com/orion/ops/framework/common/utils/AnsiCode.java +++ b/orion-ops-framework/orion-ops-common/src/main/java/com/orion/ops/framework/common/utils/AnsiCode.java @@ -28,7 +28,7 @@ package com.orion.ops.framework.common.utils; * \033[?25l 隐藏光标 * \033[?25h 显示光标 *

- * TODO 后续直接使用kit + * FIXME KIT * * @author Jiahang Li * @version 1.0.0 diff --git a/orion-ops-framework/orion-ops-spring-boot-starter-mybatis/src/main/java/com/orion/ops/framework/mybatis/type/ITypeHandler.java b/orion-ops-framework/orion-ops-spring-boot-starter-mybatis/src/main/java/com/orion/ops/framework/mybatis/type/ITypeHandler.java index ab888ca3..14bd48b2 100644 --- a/orion-ops-framework/orion-ops-spring-boot-starter-mybatis/src/main/java/com/orion/ops/framework/mybatis/type/ITypeHandler.java +++ b/orion-ops-framework/orion-ops-spring-boot-starter-mybatis/src/main/java/com/orion/ops/framework/mybatis/type/ITypeHandler.java @@ -13,7 +13,7 @@ import java.util.List; */ public interface ITypeHandler extends TypeHandler { - // TODO KIT + // FIXME KIT String COMMA = ","; /** @@ -25,7 +25,7 @@ public interface ITypeHandler extends TypeHandler { R getResult(P param); /** - * // TODO kit + * // FIXME kit * 用 , 连接 * * @param list list diff --git a/orion-ops-framework/orion-ops-spring-boot-starter-swagger/src/main/resources/META-INF/spring-configuration-metadata.json b/orion-ops-framework/orion-ops-spring-boot-starter-swagger/src/main/resources/META-INF/spring-configuration-metadata.json new file mode 100644 index 00000000..d08ae3df --- /dev/null +++ b/orion-ops-framework/orion-ops-spring-boot-starter-swagger/src/main/resources/META-INF/spring-configuration-metadata.json @@ -0,0 +1,46 @@ +{ + "groups": [ + { + "name": "orion.swagger", + "type": "com.orion.ops.framework.swagger.config.SwaggerProperties", + "sourceType": "com.orion.ops.framework.swagger.config.SwaggerProperties" + } + ], + "properties": [ + { + "name": "orion.swagger.title", + "type": "java.lang.String", + "description": "swagger 项目标题." + }, + { + "name": "orion.swagger.description", + "type": "java.lang.String", + "description": "swagger 项目描述." + }, + { + "name": "orion.swagger.version", + "type": "java.lang.String", + "description": "swagger 项目版本." + }, + { + "name": "orion.swagger.url", + "type": "java.lang.String", + "description": "swagger 项目url." + }, + { + "name": "orion.swagger.email", + "type": "java.lang.String", + "description": "swagger 项目email." + }, + { + "name": "orion.swagger.license", + "type": "java.lang.String", + "description": "swagger 项目license." + }, + { + "name": "orion.swagger.license-url", + "type": "java.lang.String", + "description": "swagger 项目license-url." + } + ] +} \ No newline at end of file diff --git a/orion-ops-framework/orion-ops-spring-boot-starter-web/pom.xml b/orion-ops-framework/orion-ops-spring-boot-starter-web/pom.xml index b7f5b1a1..2d283970 100644 --- a/orion-ops-framework/orion-ops-spring-boot-starter-web/pom.xml +++ b/orion-ops-framework/orion-ops-spring-boot-starter-web/pom.xml @@ -38,6 +38,20 @@ spring-boot-starter-jdbc provided + + + + org.springdoc + springdoc-openapi-ui + provided + + + + + com.github.xiaoymin + knife4j-openapi3-spring-boot-starter + provided + \ No newline at end of file diff --git a/orion-ops-framework/orion-ops-spring-boot-starter-web/src/main/java/com/orion/ops/framework/web/config/OrionLogPrinterConfiguration.java b/orion-ops-framework/orion-ops-spring-boot-starter-web/src/main/java/com/orion/ops/framework/web/config/OrionLogPrinterConfiguration.java index e668151b..f9ffdf4a 100644 --- a/orion-ops-framework/orion-ops-spring-boot-starter-web/src/main/java/com/orion/ops/framework/web/config/OrionLogPrinterConfiguration.java +++ b/orion-ops-framework/orion-ops-spring-boot-starter-web/src/main/java/com/orion/ops/framework/web/config/OrionLogPrinterConfiguration.java @@ -1,9 +1,16 @@ package com.orion.ops.framework.web.config; import com.orion.ops.framework.common.constant.InterceptorOrderConst; -import com.orion.ops.framework.web.core.interceptor.LogPrintInterceptor; +import com.orion.ops.framework.web.core.config.LogPrinterConfig; +import com.orion.ops.framework.web.core.interceptor.LogPrinterInterceptor; +import com.orion.ops.framework.web.core.interceptor.PrettyLogPrinterInterceptor; +import com.orion.ops.framework.web.core.interceptor.RowLogPrinterInterceptor; import org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; /** @@ -14,18 +21,41 @@ import org.springframework.context.annotation.Bean; * @since 2023/6/16 18:18 */ @AutoConfiguration +@EnableConfigurationProperties(LogPrinterConfig.class) public class OrionLogPrinterConfiguration { + /** + * @param config config + * @return 美化日志打印器 + */ @Bean - public LogPrintInterceptor logPrintInterceptor() { - return new LogPrintInterceptor(); + @ConditionalOnProperty(value = "logging.printer.mode", havingValue = "pretty") + public LogPrinterInterceptor prettyPrinter(LogPrinterConfig config) { + return new PrettyLogPrinterInterceptor(config); } + /** + * @param config config + * @return 单行日志打印器 + */ @Bean - public AspectJExpressionPointcutAdvisor logPrinterAdvisor(LogPrintInterceptor logPrintInterceptor) { + @ConditionalOnProperty(value = "logging.printer.mode", havingValue = "row") + public LogPrinterInterceptor rowPrinter(LogPrinterConfig config) { + return new RowLogPrinterInterceptor(config); + } + + /** + * @param logPrinterInterceptor logPrinterInterceptor + * @param expression 切面表达式 + * @return 日志打印切面 + */ + @Bean + @ConditionalOnBean(LogPrinterInterceptor.class) + public AspectJExpressionPointcutAdvisor logPrinterAdvisor(LogPrinterInterceptor logPrinterInterceptor, + @Value("${logging.printer.expression}") String expression) { AspectJExpressionPointcutAdvisor advisor = new AspectJExpressionPointcutAdvisor(); - advisor.setExpression("execution (* com.orion.ops.**.controller.*.*(..)) && !@annotation(com.orion.ops.framework.common.annotation.IgnoreLog)"); - advisor.setAdvice(logPrintInterceptor); + advisor.setExpression(expression); + advisor.setAdvice(logPrinterInterceptor); advisor.setOrder(InterceptorOrderConst.LOG_FILTER); return advisor; } diff --git a/orion-ops-framework/orion-ops-spring-boot-starter-web/src/main/java/com/orion/ops/framework/web/core/config/LogPrinterConfig.java b/orion-ops-framework/orion-ops-spring-boot-starter-web/src/main/java/com/orion/ops/framework/web/core/config/LogPrinterConfig.java new file mode 100644 index 00000000..a4be5fea --- /dev/null +++ b/orion-ops-framework/orion-ops-spring-boot-starter-web/src/main/java/com/orion/ops/framework/web/core/config/LogPrinterConfig.java @@ -0,0 +1,38 @@ +package com.orion.ops.framework.web.core.config; + +import com.orion.ops.framework.web.core.utils.Utils; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +import java.util.List; + +/** + * 日志打印配置 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2023/6/28 22:36 + */ +@Data +@ConfigurationProperties("logging.printer") +public class LogPrinterConfig { + + /** + * 字段配置 + */ + private LogPrinterFieldConfig field; + + /** + * 显示的请求头 + */ + private List headers; + + public void setField(LogPrinterFieldConfig field) { + this.field = field; + } + + public void setHeaders(List headers) { + this.headers = Utils.parseStringList(headers, String::toLowerCase); + } + +} diff --git a/orion-ops-framework/orion-ops-spring-boot-starter-web/src/main/java/com/orion/ops/framework/web/core/config/LogPrinterFieldConfig.java b/orion-ops-framework/orion-ops-spring-boot-starter-web/src/main/java/com/orion/ops/framework/web/core/config/LogPrinterFieldConfig.java new file mode 100644 index 00000000..38713941 --- /dev/null +++ b/orion-ops-framework/orion-ops-spring-boot-starter-web/src/main/java/com/orion/ops/framework/web/core/config/LogPrinterFieldConfig.java @@ -0,0 +1,36 @@ +package com.orion.ops.framework.web.core.config; + +import com.orion.ops.framework.web.core.utils.Utils; +import lombok.Data; + +import java.util.List; + +/** + * 日志打印字段配置 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2023/6/28 22:36 + */ +@Data +public class LogPrinterFieldConfig { + + /** + * 忽略的字段 + */ + private List ignore; + + /** + * 脱敏的字段 + */ + private List desensitization; + + public void setIgnore(List ignore) { + this.ignore = Utils.parseStringList(ignore); + } + + public void setDesensitization(List desensitization) { + this.desensitization = Utils.parseStringList(desensitization); + } + +} diff --git a/orion-ops-framework/orion-ops-spring-boot-starter-web/src/main/java/com/orion/ops/framework/web/core/interceptor/BaseLogPrinterInterceptor.java b/orion-ops-framework/orion-ops-spring-boot-starter-web/src/main/java/com/orion/ops/framework/web/core/interceptor/BaseLogPrinterInterceptor.java new file mode 100644 index 00000000..92935e33 --- /dev/null +++ b/orion-ops-framework/orion-ops-spring-boot-starter-web/src/main/java/com/orion/ops/framework/web/core/interceptor/BaseLogPrinterInterceptor.java @@ -0,0 +1,269 @@ +package com.orion.ops.framework.web.core.interceptor; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.serializer.PropertyFilter; +import com.alibaba.fastjson.serializer.SerializeFilter; +import com.alibaba.fastjson.serializer.ValueFilter; +import com.orion.lang.utils.Objects1; +import com.orion.lang.utils.collect.Maps; +import com.orion.lang.utils.reflect.Annotations; +import com.orion.lang.utils.reflect.Classes; +import com.orion.ops.framework.common.annotation.IgnoreLog; +import com.orion.ops.framework.common.constant.Const; +import com.orion.ops.framework.common.meta.TraceIdHolder; +import com.orion.ops.framework.web.core.config.LogPrinterConfig; +import io.swagger.v3.oas.annotations.Operation; +import org.aopalliance.intercept.MethodInvocation; + +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.Date; +import java.util.Map; +import java.util.function.Predicate; + +/** + * 日志打印拦截器 基类 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2023/6/29 10:36 + */ +public abstract class BaseLogPrinterInterceptor implements LogPrinterInterceptor { + + private static final String EMPTY_ARG = ""; + + private static final String ERROR_ARG = ""; + + /** + * 请求头过滤器 + */ + protected Predicate headerFilter; + + /** + * 字段过滤器 + */ + protected SerializeFilter[] fieldFilter; + + /** + * api 描述 + */ + private final Map summaryMapping; + + /** + * 忽略的参数 + */ + private final Map ignoreParameter; + + public BaseLogPrinterInterceptor(LogPrinterConfig config) { + this.summaryMapping = Maps.newMap(); + this.ignoreParameter = Maps.newMap(); + this.init(config); + } + + /** + * 初始化 + * + * @param config config + */ + protected void init(LogPrinterConfig config) { + // 请求头过滤器 + this.headerFilter = header -> config.getHeaders().contains(header); + // 忽略字段过滤器 + PropertyFilter ignoreFilter = (Object object, String name, Object value) -> !config.getField().getIgnore().contains(name); + // 脱敏字段过滤器 + ValueFilter desensitizationFilter = (Object object, String name, Object value) -> { + if (config.getField().getDesensitization().contains(name)) { + String s = Objects1.toString(value); + // Safes.mix() + // TODO + return "xxxxxx"; + } else { + return value; + } + }; + this.fieldFilter = new SerializeFilter[]{ignoreFilter, desensitizationFilter}; + } + + @Override + public Object invoke(MethodInvocation invocation) throws Throwable { + Date startTime = new Date(); + String traceId = TraceIdHolder.get(); + // 打印请求日志 + this.requestPrinter(startTime, traceId, invocation); + try { + // 执行方法 + Object ret = invocation.proceed(); + // 打印响应日志 + this.responsePrinter(startTime, traceId, ret); + return ret; + } catch (Throwable t) { + // 打印异常日志 + this.errorPrinter(startTime, traceId, t); + throw t; + } + } + + /** + * 打印请求信息 + * + * @param startTime 开始时间 + * @param traceId traceId + * @param invocation invocation + */ + protected abstract void requestPrinter(Date startTime, String traceId, MethodInvocation invocation); + + /** + * 打印响应信息 + * + * @param startTime 开始时间 + * @param traceId traceId + * @param ret return + */ + protected abstract void responsePrinter(Date startTime, String traceId, Object ret); + + /** + * 打印异常信息 + * + * @param startTime 开始时间 + * @param traceId traceId + * @param throwable ex + */ + protected abstract void errorPrinter(Date startTime, String traceId, Throwable throwable); + + /** + * 获取 api 描述 + * + * @param m method + * @return summary + */ + protected String getApiSummary(Method m) { + // 缓存中获取描述 + String key = m.toString(); + String cache = summaryMapping.get(key); + if (cache != null) { + return cache; + } + // 获取注解描述 + Operation operation = Annotations.getAnnotation(m, Operation.class); + String summary = Const.EMPTY; + if (operation != null) { + summary = operation.summary(); + } + summaryMapping.put(key, summary); + return summary; + } + + /** + * 请求参数 json + * + * @param method method + * @param args object + * @return json + */ + protected String requestToString(Method method, Object[] args) { + int length = args.length; + if (length == 0) { + return EMPTY_ARG; + } + try { + // 检查是否需要忽略字段 + boolean[] ignored = this.getParameterIgnoreConfig(method, args); + int printCount = 0; + int lastPrintIndex = 0; + for (int i = 0; i < ignored.length; i++) { + if (!ignored[i]) { + printCount++; + lastPrintIndex = i; + } + } + if (printCount == 0) { + // 无打印参数 + return EMPTY_ARG; + } else if (printCount == 1) { + // 单个打印参数 + return JSON.toJSONString(args[lastPrintIndex], fieldFilter); + } else { + // 多个打印参数 + JSONArray arr = new JSONArray(); + for (int i = 0; i < ignored.length; i++) { + if (!ignored[i]) { + arr.add(args[i]); + } + } + return JSON.toJSONString(arr, fieldFilter); + } + } catch (Exception e) { + return ERROR_ARG; + } + } + + /** + * 响应结果 json + * + * @param o object + * @return json + */ + protected String responseToString(Object o) { + try { + return JSON.toJSONString(o, fieldFilter); + } catch (Exception e) { + return ERROR_ARG; + } + } + + /** + * 获取参数忽略字段配置 + * + * @param method method + * @param args args + * @return ignoreArr + */ + private boolean[] getParameterIgnoreConfig(Method method, Object[] args) { + int length = args.length; + // 获取缓存 + String key = method.toString(); + boolean[] ignored = ignoreParameter.get(key); + if (ignored != null) { + return ignored; + } + ignored = new boolean[length]; + ignoreParameter.put(key, ignored); + // 检查是否有忽略字段注解 + Annotation[][] annotations = method.getParameterAnnotations(); + for (int parameterIndex = 0; parameterIndex < length; parameterIndex++) { + boolean ignore = false; + Annotation[] parameterAnnotations = annotations[parameterIndex]; + for (Annotation parameterAnnotation : parameterAnnotations) { + // 需要忽略该字段 + if (parameterAnnotation.annotationType().equals(IgnoreLog.class)) { + ignore = true; + break; + } + } + ignored[parameterIndex] = ignore; + } + // 检查是否可以序列化 + for (int i = 0; i < args.length; i++) { + if (ignored[i]) { + continue; + } + Object arg = args[i]; + // 是否为 request / response + if (arg instanceof ServletRequest || arg instanceof ServletResponse) { + ignored[i] = true; + continue; + } + // 是否为代理对象 (bean) + if (arg != null) { + if (Classes.isJdkProxy(arg) || Classes.isCglibProxy(arg)) { + ignored[i] = true; + } + } + } + return ignored; + } + +} diff --git a/orion-ops-framework/orion-ops-spring-boot-starter-web/src/main/java/com/orion/ops/framework/web/core/interceptor/LogPrintInterceptor.java b/orion-ops-framework/orion-ops-spring-boot-starter-web/src/main/java/com/orion/ops/framework/web/core/interceptor/LogPrintInterceptor.java deleted file mode 100644 index 660b4514..00000000 --- a/orion-ops-framework/orion-ops-spring-boot-starter-web/src/main/java/com/orion/ops/framework/web/core/interceptor/LogPrintInterceptor.java +++ /dev/null @@ -1,154 +0,0 @@ -package com.orion.ops.framework.web.core.interceptor; - -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.serializer.PropertyFilter; -import com.orion.lang.constant.Const; -import com.orion.lang.utils.Arrays1; -import com.orion.lang.utils.Exceptions; -import com.orion.lang.utils.time.Dates; -import com.orion.ops.framework.common.meta.TraceIdHolder; -import com.orion.web.servlet.web.Servlets; -import lombok.extern.slf4j.Slf4j; -import org.aopalliance.intercept.MethodInterceptor; -import org.aopalliance.intercept.MethodInvocation; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.core.annotation.Order; -import org.springframework.stereotype.Component; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.context.request.ServletRequestAttributes; - -import java.lang.reflect.Method; -import java.util.Date; -import java.util.Optional; - -/** - * 日志打印拦截器 - * - * @author Jiahang Li - * @version 1.0.0 - * @since 2022/7/14 18:25 - */ -@Slf4j -@Order(10) -public class LogPrintInterceptor implements MethodInterceptor { - - // TODO @OperatorLog(bizName = "") - // TODO 日志规格模型 SIMPLIFY FULL - - // https://blog.csdn.net/gaojie_csdn/article/details/127810618 - // 加注解 - - // TODO 改为正则? - @Value("#{'${log.interceptor.ignore.fields:}'.split(',')}") - private String[] ignoreFields; - - @Override - public Object invoke(MethodInvocation invocation) throws Throwable { - Date startTime = new Date(); - String traceId = TraceIdHolder.get(); - // 打印开始日志 - this.beforeLogPrint(startTime, traceId, invocation); - try { - // 执行方法 - Object ret = invocation.proceed(); - // 返回打印 - this.afterReturnLogPrint(startTime, traceId, ret); - return ret; - } catch (Throwable t) { - // 异常打印 - this.afterThrowingLogPrint(startTime, traceId, t); - throw t; - } - } - - /** - * 方法进入打印 - * - * @param startTime 开始时间 - * @param traceId trace - * @param invocation invocation - */ - public void beforeLogPrint(Date startTime, String traceId, MethodInvocation invocation) { - StringBuilder requestLog = new StringBuilder("\napi请求-开始-traceId: ").append(traceId).append('\n'); - // TODO 登陆用户 - // requestLog.append("\t当前用户: ").append(JSON.toJSONString(UserHolder.get())).append('\n'); - // TODO @OperatorLog - // http请求信息 - Optional.ofNullable(RequestContextHolder.getRequestAttributes()) - .map(s -> (ServletRequestAttributes) s) - .map(ServletRequestAttributes::getRequest) - .ifPresent(request -> { - // url - requestLog.append("\t").append(Servlets.getMethod(request)).append(" ") - .append(Servlets.getRequestUrl(request)).append('\n'); - // query - requestLog.append("\tip: ").append(Servlets.getRemoteAddr(request)).append('\n') - .append("\tquery: ").append(Servlets.getQueryString(request)).append('\n'); - // header - Servlets.getHeaderMap(request).forEach((hk, hv) -> requestLog.append('\t') - .append(hk).append(": ") - .append(hv).append('\n')); - }); - // 方法信息 - Method method = invocation.getMethod(); - requestLog.append("\t开始时间: ").append(Dates.format(startTime, Dates.YMD_HMSS)).append('\n') - .append("\t方法签名: ").append(method.getDeclaringClass().getName()).append('#') - .append(method.getName()).append("\n") - .append("\t请求参数: ").append(this.argsToString(invocation.getArguments())); - log.info(requestLog.toString()); - } - - /** - * 返回打印 - * - * @param startTime 开始时间 - * @param traceId traceId - * @param ret return - */ - private void afterReturnLogPrint(Date startTime, String traceId, Object ret) { - Date endTime = new Date(); - // 响应日志 - StringBuilder responseLog = new StringBuilder("\napi请求-结束-traceId: ").append(traceId).append('\n'); - responseLog.append("\t结束时间: ").append(Dates.format(endTime, Dates.YMD_HMSS)) - .append(" used: ").append(endTime.getTime() - startTime.getTime()).append("ms \n") - .append("\t响应结果: ").append(this.argsToString(ret)); - log.info(responseLog.toString()); - } - - /** - * 异常打印 - * - * @param startTime 开始时间 - * @param traceId trace - * @param throwable ex - */ - private void afterThrowingLogPrint(Date startTime, String traceId, Throwable throwable) { - Date endTime = new Date(); - // 响应日志 - StringBuilder responseLog = new StringBuilder("\napi请求-异常-traceId: ").append(traceId).append('\n'); - responseLog.append("\t结束时间: ").append(Dates.format(endTime, Dates.YMD_HMSS)) - .append(" used: ").append(endTime.getTime() - startTime.getTime()).append("ms \n") - .append("\t异常摘要: ").append(Exceptions.getDigest(throwable)); - log.error(responseLog.toString()); - } - - /** - * 参数转json - * - * @param o object - * @return json - */ - private String argsToString(Object o) { - try { - if (ignoreFields.length == 1 && Const.EMPTY.equals(ignoreFields[0])) { - // 不过滤 - return JSON.toJSONString(o); - } else { - return JSON.toJSONString(o, (PropertyFilter) (object, name, value) -> !Arrays1.contains(ignoreFields, name)); - } - } catch (Exception e) { - return String.valueOf(o); - } - } - -} diff --git a/orion-ops-framework/orion-ops-spring-boot-starter-web/src/main/java/com/orion/ops/framework/web/core/interceptor/LogPrinterInterceptor.java b/orion-ops-framework/orion-ops-spring-boot-starter-web/src/main/java/com/orion/ops/framework/web/core/interceptor/LogPrinterInterceptor.java new file mode 100644 index 00000000..1b242b37 --- /dev/null +++ b/orion-ops-framework/orion-ops-spring-boot-starter-web/src/main/java/com/orion/ops/framework/web/core/interceptor/LogPrinterInterceptor.java @@ -0,0 +1,14 @@ +package com.orion.ops.framework.web.core.interceptor; + +import org.aopalliance.intercept.MethodInterceptor; + +/** + * 日志打印拦截器 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2023/6/29 10:36 + */ +public interface LogPrinterInterceptor extends MethodInterceptor { + +} diff --git a/orion-ops-framework/orion-ops-spring-boot-starter-web/src/main/java/com/orion/ops/framework/web/core/interceptor/PrettyLogPrinterInterceptor.java b/orion-ops-framework/orion-ops-spring-boot-starter-web/src/main/java/com/orion/ops/framework/web/core/interceptor/PrettyLogPrinterInterceptor.java new file mode 100644 index 00000000..84f4e1b9 --- /dev/null +++ b/orion-ops-framework/orion-ops-spring-boot-starter-web/src/main/java/com/orion/ops/framework/web/core/interceptor/PrettyLogPrinterInterceptor.java @@ -0,0 +1,101 @@ +package com.orion.ops.framework.web.core.interceptor; + +import com.orion.lang.utils.Exceptions; +import com.orion.lang.utils.Strings; +import com.orion.lang.utils.time.Dates; +import com.orion.ops.framework.web.core.config.LogPrinterConfig; +import com.orion.web.servlet.web.Servlets; +import lombok.extern.slf4j.Slf4j; +import org.aopalliance.intercept.MethodInvocation; +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.util.Date; +import java.util.Optional; + +/** + * 美化 日志打印拦截器 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2023/6/29 10:36 + */ +@Slf4j +public class PrettyLogPrinterInterceptor extends BaseLogPrinterInterceptor { + + public PrettyLogPrinterInterceptor(LogPrinterConfig config) { + super(config); + } + + @Override + protected void requestPrinter(Date startTime, String traceId, MethodInvocation invocation) { + StringBuilder requestLog = new StringBuilder("\napi请求-开始\n"); + // http请求信息 + HttpServletRequest request = Optional.ofNullable(RequestContextHolder.getRequestAttributes()) + .map(s -> (ServletRequestAttributes) s) + .map(ServletRequestAttributes::getRequest) + .orElse(null); + // url + if (request != null) { + requestLog.append("\t").append(Servlets.getMethod(request)).append(" ") + .append(Servlets.getRequestUrl(request)).append('\n'); + } + // traceId + requestLog.append("\ttraceId: ").append(traceId).append("\n"); + // 开始时间 + requestLog.append("\tstart: ").append(Dates.format(startTime, Dates.YMD_HMSS)).append('\n'); + // api 描述 + String summary = this.getApiSummary(invocation.getMethod()); + if (!Strings.isEmpty(summary)) { + requestLog.append("\tsummary: ").append(summary).append('\n'); + } + // TODO 登陆用户 + // http + if (request != null) { + // remoteAddr + requestLog.append("\tremoteAddr: ").append(Servlets.getRemoteAddr(request)).append('\n'); + // header + Servlets.getHeaderMap(request).forEach((hk, hv) -> { + if (headerFilter.test(hk.toLowerCase())) { + requestLog.append('\t') + .append(hk).append(": ") + .append(hv).append('\n'); + } + }); + } + Method method = invocation.getMethod(); + // 方法签名 + requestLog.append("\tmethodSign: ").append(method.getDeclaringClass().getName()).append('#') + .append(method.getName()).append("\n"); + // 参数 + requestLog.append("\tparameter: ").append(this.requestToString(method, invocation.getArguments())); + log.info(requestLog.toString()); + } + + @Override + protected void responsePrinter(Date startTime, String traceId, Object ret) { + Date endTime = new Date(); + // 响应日志 + StringBuilder responseLog = new StringBuilder("\napi请求-结束\n") + .append("\ttraceId: ").append(traceId).append('\n') + .append("\tend: ").append(Dates.format(endTime, Dates.YMD_HMSS)).append('\n') + .append("\tused: ").append(endTime.getTime() - startTime.getTime()).append("ms \n") + .append("\tresponse: ").append(this.responseToString(ret)); + log.info(responseLog.toString()); + } + + @Override + protected void errorPrinter(Date startTime, String traceId, Throwable throwable) { + Date endTime = new Date(); + // 异常日志 + StringBuilder errorLog = new StringBuilder("\napi请求-异常\n") + .append("\ttraceId: ").append(traceId).append('\n') + .append("\tend: ").append(Dates.format(endTime, Dates.YMD_HMSS)).append('\n') + .append("\tused: ").append(endTime.getTime() - startTime.getTime()).append("ms \n") + .append("\terrorDigest: ").append(Exceptions.getDigest(throwable)); + log.error(errorLog.toString()); + } + +} diff --git a/orion-ops-framework/orion-ops-spring-boot-starter-web/src/main/java/com/orion/ops/framework/web/core/utils/Utils.java b/orion-ops-framework/orion-ops-spring-boot-starter-web/src/main/java/com/orion/ops/framework/web/core/utils/Utils.java new file mode 100644 index 00000000..5a782041 --- /dev/null +++ b/orion-ops-framework/orion-ops-spring-boot-starter-web/src/main/java/com/orion/ops/framework/web/core/utils/Utils.java @@ -0,0 +1,45 @@ +package com.orion.ops.framework.web.core.utils; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * 工具类 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2023/6/28 23:21 + */ +public class Utils { + + private Utils() { + } + + public static List parseStringList(List list) { + return parseStringList(list, Function.identity()); + } + + /** + * 解析配置 List + * + * @param list list + * @param mapper mapper + * @return config + */ + public static List parseStringList(List list, Function mapper) { + return Optional.ofNullable(list) + .map(List::stream) + .orElseGet(Stream::empty) + // FIXME kit + .map(s -> s.split(",")) + .flatMap(Arrays::stream) + .map(String::trim) + .map(mapper) + .collect(Collectors.toList()); + } + +} diff --git a/orion-ops-framework/orion-ops-spring-boot-starter-web/src/main/resources/META-INF/spring-configuration-metadata.json b/orion-ops-framework/orion-ops-spring-boot-starter-web/src/main/resources/META-INF/spring-configuration-metadata.json new file mode 100644 index 00000000..ad804169 --- /dev/null +++ b/orion-ops-framework/orion-ops-spring-boot-starter-web/src/main/resources/META-INF/spring-configuration-metadata.json @@ -0,0 +1,52 @@ +{ + "groups": [ + { + "name": "logging.printer", + "type": "com.orion.ops.framework.web.core.config.LogPrinterConfig", + "sourceType": "com.orion.ops.framework.web.core.config.LogPrinterConfig" + } + ], + "properties": [ + { + "name": "logging.printer.mode", + "type": "com.orion.ops.framework.web.core.enums.LogPrinterMode", + "description": "日志打印模型.", + "defaultValue": "NONE" + }, + { + "name": "logging.printer.expression", + "type": "java.lang.String", + "description": "aspectj 表达式." + }, + { + "name": "logging.printer.headers", + "type": "java.util.List", + "description": "需要打印的 HttpHandlers." + }, + { + "name": "logging.printer.field.ignore", + "type": "java.util.List", + "description": "忽略打印的字段." + }, + { + "name": "logging.printer.field.desensitization", + "type": "java.util.List", + "description": "需要脱敏的字段." + }, + { + "name": "orion.version", + "type": "java.lang.String", + "description": "项目版本." + }, + { + "name": "orion.api.prefix", + "type": "java.lang.String", + "description": "项目 api 前缀." + }, + { + "name": "orion.api.cors", + "type": "java.lang.Boolean", + "description": "是否开启 cors 过滤器." + } + ] +} \ No newline at end of file diff --git a/orion-ops-launch/src/main/resources/application.yaml b/orion-ops-launch/src/main/resources/application.yaml index 57912249..416cfafe 100644 --- a/orion-ops-launch/src/main/resources/application.yaml +++ b/orion-ops-launch/src/main/resources/application.yaml @@ -108,6 +108,19 @@ logging: pattern: console: '%clr(%d{${LOG_DATEFORMAT_PATTERN:yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %boldBlue([%X{tid}]) %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:%wEx}' file: "%d{${LOG_DATEFORMAT_PATTERN:yyyy-MM-dd HH:mm:ss.SSS}} ${LOG_LEVEL_PATTERN:-%5p} [%X{tid}] [%t] %-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:%wEx}" + printer: + mode: PRETTY + expression: 'execution (* com.orion.ops.**.controller.*.*(..)) && !@annotation(com.orion.ops.framework.common.annotation.IgnoreLog)' + headers: + - user-agent,accept + - content-type + field: + ignore: + - password,newPassword + - metrics + desensitization: + - phone,phoneNumber + - email,sendEmail orion: # 版本 @@ -118,7 +131,6 @@ orion: prefix: /orion-api # 是否开启跨域 cors: true - # 文档配置 swagger: title: orion-ops-pro 运维平台 description: 一站式提供运维功能