diff --git a/axzo-common-autoconfigure/pom.xml b/axzo-common-autoconfigure/pom.xml index 20704d6..97ef0fc 100644 --- a/axzo-common-autoconfigure/pom.xml +++ b/axzo-common-autoconfigure/pom.xml @@ -7,7 +7,7 @@ axzo-framework-commons cn.axzo.framework - 1.0.0-SNAPSHOT + ${revision} axzo-common-autoconfigure @@ -94,5 +94,11 @@ org.springframework.security spring-security-web + + io.github.openfeign + feign-core + compile + true + - \ No newline at end of file + diff --git a/axzo-common-autoconfigure/src/main/java/cn/axzo/framework/autoconfigure/feign/FeignErrorDecoderAutoConfiguration.java b/axzo-common-autoconfigure/src/main/java/cn/axzo/framework/autoconfigure/feign/FeignErrorDecoderAutoConfiguration.java new file mode 100644 index 0000000..a2b46a7 --- /dev/null +++ b/axzo-common-autoconfigure/src/main/java/cn/axzo/framework/autoconfigure/feign/FeignErrorDecoderAutoConfiguration.java @@ -0,0 +1,46 @@ +package cn.axzo.framework.autoconfigure.feign; + +import cn.axzo.framework.core.RecordException; +import feign.Feign; +import feign.Response; +import feign.Util; +import feign.codec.ErrorDecoder; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.DispatcherServlet; + +import javax.servlet.Servlet; +import java.io.IOException; + +import static java.nio.charset.StandardCharsets.UTF_8; + +/** + * 为兼容 X-Metadata-Tag 的异常解码器 + * + * @author wangli + * @since 2024/6/25 21:55 + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnWebApplication +@ConditionalOnClass({Feign.class, Servlet.class, DispatcherServlet.class}) +public class FeignErrorDecoderAutoConfiguration { + + @Bean + public ErrorDecoder feignErrorDecoder() { + return (methodKey, response) -> { + if (response.status() != 512) { + return new ErrorDecoder.Default().decode(methodKey, response); + } + byte[] body = {}; + try { + if (response.body() != null) { + body = Util.toByteArray(response.body().asInputStream()); + } + } catch (IOException ignored) { // NOPMD + } + return new RecordException(new String(body, UTF_8)); + }; + } +} diff --git a/axzo-common-autoconfigure/src/main/java/cn/axzo/framework/autoconfigure/web/exception/ExceptionHandlerAutoConfiguration.java b/axzo-common-autoconfigure/src/main/java/cn/axzo/framework/autoconfigure/web/exception/ExceptionHandlerAutoConfiguration.java index fed8d6e..7627737 100644 --- a/axzo-common-autoconfigure/src/main/java/cn/axzo/framework/autoconfigure/web/exception/ExceptionHandlerAutoConfiguration.java +++ b/axzo-common-autoconfigure/src/main/java/cn/axzo/framework/autoconfigure/web/exception/ExceptionHandlerAutoConfiguration.java @@ -8,6 +8,10 @@ import cn.axzo.framework.autoconfigure.web.exception.resolver.internal.RequestRe import cn.axzo.framework.autoconfigure.web.exception.support.GlobalErrorController; import cn.axzo.framework.autoconfigure.web.exception.support.GlobalExceptionHandler; import cn.axzo.framework.domain.web.result.Result; +import cn.axzo.framework.web.feign.FeignRecordExceptionInterceptor; +import cn.axzo.framework.web.filter.BasicRecordExceptionFilter; +import feign.RequestInterceptor; +import org.slf4j.MDC; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -22,6 +26,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.security.oauth2.common.exceptions.OAuth2Exception; import org.springframework.security.oauth2.provider.error.DefaultWebResponseExceptionTranslator; import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator; +import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; @@ -184,4 +189,19 @@ public class ExceptionHandlerAutoConfiguration implements WebMvcConfigurer { return new RequestRejectedExceptionHttpStatusResolver(); } } + + @Configuration(proxyBeanMethods = false) + @ConditionalOnClass(RequestInterceptor.class) + public static class RecordExceptionConfiguration { + @Bean + public RequestInterceptor requestInterceptor(){ + return new FeignRecordExceptionInterceptor(); + } + + @Bean + @ConditionalOnClass(MDC.class) + public OncePerRequestFilter oncePerRequestFilter() { + return new BasicRecordExceptionFilter(); + } + } } diff --git a/axzo-common-autoconfigure/src/main/java/cn/axzo/framework/autoconfigure/web/exception/support/GlobalExceptionHandler.java b/axzo-common-autoconfigure/src/main/java/cn/axzo/framework/autoconfigure/web/exception/support/GlobalExceptionHandler.java index b5739d8..df3f6c9 100644 --- a/axzo-common-autoconfigure/src/main/java/cn/axzo/framework/autoconfigure/web/exception/support/GlobalExceptionHandler.java +++ b/axzo-common-autoconfigure/src/main/java/cn/axzo/framework/autoconfigure/web/exception/support/GlobalExceptionHandler.java @@ -3,14 +3,22 @@ package cn.axzo.framework.autoconfigure.web.exception.support; import cn.axzo.framework.autoconfigure.web.exception.handler.ExceptionResultHandler; import cn.axzo.framework.autoconfigure.web.exception.handler.internal.StandardExceptionResultHandler; import cn.axzo.framework.core.InternalException; +import cn.axzo.framework.core.RecordException; import cn.axzo.framework.core.util.ClassUtil; import cn.axzo.framework.domain.web.code.BaseCode; -import cn.axzo.framework.domain.web.result.ApiResult; +import cn.axzo.framework.domain.web.result.ApiReportResult; import cn.axzo.framework.domain.web.result.Result; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.exc.InvalidFormatException; +import com.google.common.collect.Lists; +import lombok.Data; +import lombok.Getter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; import org.springframework.beans.TypeMismatchException; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.web.ErrorProperties; import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.web.servlet.error.ErrorAttributes; @@ -19,6 +27,8 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.stereotype.Controller; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; import org.springframework.validation.BindException; import org.springframework.validation.FieldError; import org.springframework.web.bind.MethodArgumentNotValidException; @@ -34,12 +44,22 @@ import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExcep import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; +import java.util.stream.Collectors; +import static cn.axzo.framework.domain.web.result.ApiReportResult.err; +import static cn.axzo.framework.web.filter.BasicRecordExceptionFilter.MICRO_SERVER_RECORD_ERROR_FILTER_PACKAGE_VALUE; +import static cn.axzo.framework.web.filter.BasicRecordExceptionFilter.MICRO_SERVER_RECORD_ERROR_GET_PARAM_NAME; import static java.lang.String.format; import static jodd.util.StringUtil.isNotBlank; +import static org.springframework.http.HttpHeaders.CACHE_CONTROL; /** * @Description spring mvc全局异常捕获 @@ -48,6 +68,7 @@ import static jodd.util.StringUtil.isNotBlank; **/ @RestControllerAdvice(annotations = {Controller.class, RestController.class}) public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { + private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); private final List> handlers; @@ -55,6 +76,19 @@ public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { private final ErrorAttributes errorAttributes; + public static final String CTX_LOG_ID = "ctxLogId"; + /** + * 多设置一个key = TraceId, value为traceId的变量到MDC. 以兼容目前的logback-spring.xml的配置 + */ + public static final String TRACE_ID_IN_MDC = "traceId"; + private static final String RECORD_FLAG = "debug"; + + + @Value("${axzo.framework.debug:X-Metadata-Tag}") + private String recordExceptionHeaderName; + @Value("${spring.application.name:}") + private String applicationName; + public GlobalExceptionHandler(List> handlers, ErrorAttributes errorAttributes, ServerProperties properties) { @@ -85,7 +119,7 @@ public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { } // 3.响应 - return ApiResult.err(Integer.parseInt(code), message); + return err(Integer.parseInt(code), message); }); return super.handleExceptionInternal(ex, result, headers, status, request); @@ -111,7 +145,7 @@ public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { } // 3.响应 - return ApiResult.err(Integer.parseInt(code), message); + return err(Integer.parseInt(code), message); }); return super.handleExceptionInternal(ex, result, headers, status, request); } @@ -136,7 +170,7 @@ public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { } // 3.响应 - return ApiResult.err(Integer.parseInt(code), message); + return err(Integer.parseInt(code), message); }); return super.handleExceptionInternal(ex, result, headers, status, request); } @@ -162,7 +196,7 @@ public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { } // 3.响应 - return ApiResult.err(Integer.parseInt(code), message); + return err(Integer.parseInt(code), message); }); return super.handleExceptionInternal(ex, result, headers, status, request); } @@ -185,7 +219,7 @@ public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { message = "Required parameter '" + field + "' is not present"; // 3.响应 - return ApiResult.err(Integer.parseInt(code), message); + return err(Integer.parseInt(code), message); }); return super.handleExceptionInternal(ex, result, headers, status, request); } @@ -230,7 +264,7 @@ public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { } // 3.响应 - return ApiResult.err(Integer.parseInt(code), message); + return err(Integer.parseInt(code), message); }); return super.handleExceptionInternal(ex, result, headers, status, request); } @@ -240,8 +274,16 @@ public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { throw e; } + @ExceptionHandler(RecordException.class) + public String recordException(HttpServletRequest request, HttpServletResponse response, Throwable e) { + return e.getMessage(); + } + @ExceptionHandler(Throwable.class) public Result handleAllException(HttpServletRequest request, HttpServletResponse response, Throwable e) { + if (shouldRecordException(request)) { + return buildResult(request, response, e); + } Map attributes = Requests.from(request).getErrorAttributes(errorAttributes, errorProperties); return _handleException(request, response, e, attributes) .orElseGet(() -> handlers.stream() @@ -252,6 +294,16 @@ public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { ); } + private Result buildResult(HttpServletRequest request, HttpServletResponse response, Throwable e) { + response.setHeader(CACHE_CONTROL, "no-store"); + response.setStatus(512); + ApiReportResult err = err(512, "discovery server internal error"); + Map recordExceptionMap = new HashMap<>(); + recordExceptionMap.put(StringUtils.hasText(applicationName) ? applicationName : "not applicationName found", rebuildThrowable(e, request)); + err.setAnalysis(recordExceptionMap); + return err; + } + private Optional _handleException(WebRequest webRequest, Throwable e) { Map attributes = Requests.from(webRequest).getErrorAttributes(errorAttributes, errorProperties); if (webRequest instanceof ServletWebRequest) { @@ -279,10 +331,84 @@ public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { } } + private boolean shouldRecordException(HttpServletRequest request) { + String headerValue = request.getHeader(recordExceptionHeaderName); + if (!StringUtils.hasText(headerValue)) { + headerValue = MDC.get(recordExceptionHeaderName); + } + log.info("recordException HeaderName: {}", headerValue); + if(!StringUtils.hasText(headerValue)) { + return false; + } + return headerValue.contains(RECORD_FLAG); + } + private boolean shouldFilter(ExceptionResultHandler handler, Throwable e) { if (handler.isRecursive()) { return handler.getExceptionClass().isAssignableFrom(e.getClass()); } return handler.getExceptionClass() == e.getClass(); } + + private ExceptionWrapper rebuildThrowable(Throwable throwable, HttpServletRequest request) { + return new ExceptionWrapper(throwable, request); + } + + @Getter + static class ExceptionWrapper implements Serializable { + private final String traceId = getOutsideTraceId(); + private final String causeMessage; + private final List stackTrace = new ArrayList<>(); + + public ExceptionWrapper(Throwable throwable, HttpServletRequest request) { + this.causeMessage = throwable.getMessage(); + init(throwable, request); + } + + private void init(Throwable throwable, HttpServletRequest request) { + List elements = Arrays.stream(throwable.getStackTrace()).collect(Collectors.toList()); + if (CollectionUtils.isEmpty(elements)) { + return; + } + String[] parameterValues = request.getParameterValues(MICRO_SERVER_RECORD_ERROR_GET_PARAM_NAME); + List filterPackageNames = Lists.newArrayList(MICRO_SERVER_RECORD_ERROR_FILTER_PACKAGE_VALUE); + if (Objects.nonNull(parameterValues) && parameterValues.length > 0) { + filterPackageNames = Arrays.asList(parameterValues); + } else if (StringUtils.hasText(MDC.get(MICRO_SERVER_RECORD_ERROR_GET_PARAM_NAME))) { + filterPackageNames = Arrays.asList(StringUtils.commaDelimitedListToStringArray(MDC.get(MICRO_SERVER_RECORD_ERROR_GET_PARAM_NAME))); + } + for (StackTraceElement e : elements) { + if (StringUtils.hasText(e.getClassName())) { + filterPackageNames.stream().filter(i -> e.getClassName().contains(i)).findAny().ifPresent(t -> { + StackTraceWrapper wrapper = new StackTraceWrapper(); + wrapper.setClassName(e.getClassName()); + wrapper.setMethodName(e.getMethodName()); + wrapper.setLineNumber(e.getLineNumber()); + stackTrace.add(wrapper.toString()); + }); + } + } + } + } + + @Data + static class StackTraceWrapper implements Serializable { + private String className; + private String methodName; + private Integer lineNumber; + + @Override + public String toString() { + return className + "#" + methodName + ":" + lineNumber; + } + } + + + private static String getOutsideTraceId() { + String res = MDC.get(TRACE_ID_IN_MDC); + if (Objects.isNull(res)) { + res = MDC.get(CTX_LOG_ID); + } + return res; + } } diff --git a/axzo-common-autoconfigure/src/main/resources/META-INF/spring.factories b/axzo-common-autoconfigure/src/main/resources/META-INF/spring.factories index 267add4..9233055 100644 --- a/axzo-common-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/axzo-common-autoconfigure/src/main/resources/META-INF/spring.factories @@ -16,5 +16,6 @@ cn.axzo.framework.autoconfigure.web.rest.EnumResource,\ cn.axzo.framework.autoconfigure.validation.SpringValidatorAutoConfiguration,\ cn.axzo.framework.autoconfigure.web.advice.BodyAdviceAutoConfiguration,\ cn.axzo.framework.autoconfigure.web.FilterAutoConfiguration,\ -cn.axzo.framework.autoconfigure.web.context.WebMvcAwareAutoConfiguration +cn.axzo.framework.autoconfigure.web.context.WebMvcAwareAutoConfiguration,\ +cn.axzo.framework.autoconfigure.feign.FeignErrorDecoderAutoConfiguration # cn.axzo.framework.autoconfigure.web.swagger.SwaggerAutoConfiguration,\ diff --git a/axzo-common-boot/pom.xml b/axzo-common-boot/pom.xml index e67695e..0ade890 100644 --- a/axzo-common-boot/pom.xml +++ b/axzo-common-boot/pom.xml @@ -7,7 +7,7 @@ axzo-framework-commons cn.axzo.framework - 1.0.0-SNAPSHOT + ${revision} axzo-common-boot @@ -82,4 +82,4 @@ - \ No newline at end of file + diff --git a/axzo-common-cache/pom.xml b/axzo-common-cache/pom.xml index 198a364..bfdadb6 100644 --- a/axzo-common-cache/pom.xml +++ b/axzo-common-cache/pom.xml @@ -6,7 +6,7 @@ cn.axzo.framework axzo-framework-commons - 1.0.0-SNAPSHOT + ${revision} Axzo Common Cache axzo-common-cache @@ -21,4 +21,4 @@ jetcache-starter-redis - \ No newline at end of file + diff --git a/axzo-common-clients/feign-starter/pom.xml b/axzo-common-clients/feign-starter/pom.xml index 8062c81..31ebf0b 100644 --- a/axzo-common-clients/feign-starter/pom.xml +++ b/axzo-common-clients/feign-starter/pom.xml @@ -7,7 +7,7 @@ axzo-common-clients cn.axzo.framework.client - 1.0.0-SNAPSHOT + ${revision} feign-starter @@ -62,4 +62,4 @@ - \ No newline at end of file + diff --git a/axzo-common-clients/pom.xml b/axzo-common-clients/pom.xml index 4ee445d..97f80e2 100644 --- a/axzo-common-clients/pom.xml +++ b/axzo-common-clients/pom.xml @@ -7,7 +7,7 @@ axzo-framework-commons cn.axzo.framework - 1.0.0-SNAPSHOT + ${revision} cn.axzo.framework.client @@ -19,4 +19,4 @@ retrofit-starter feign-starter - \ No newline at end of file + diff --git a/axzo-common-clients/retrofit-starter/pom.xml b/axzo-common-clients/retrofit-starter/pom.xml index c84f001..4a739de 100644 --- a/axzo-common-clients/retrofit-starter/pom.xml +++ b/axzo-common-clients/retrofit-starter/pom.xml @@ -7,7 +7,7 @@ cn.axzo.framework.client axzo-common-clients - 1.0.0-SNAPSHOT + ${revision} retrofit-starter @@ -35,4 +35,4 @@ adapter-rxjava2 - \ No newline at end of file + diff --git a/axzo-common-core/pom.xml b/axzo-common-core/pom.xml index 3956f45..10a1c5b 100644 --- a/axzo-common-core/pom.xml +++ b/axzo-common-core/pom.xml @@ -7,7 +7,7 @@ axzo-framework-commons cn.axzo.framework - 1.0.0-SNAPSHOT + ${revision} axzo-common-core diff --git a/axzo-common-core/src/main/java/cn/axzo/framework/core/RecordException.java b/axzo-common-core/src/main/java/cn/axzo/framework/core/RecordException.java new file mode 100644 index 0000000..8444684 --- /dev/null +++ b/axzo-common-core/src/main/java/cn/axzo/framework/core/RecordException.java @@ -0,0 +1,22 @@ +package cn.axzo.framework.core; + +/** + * 记录异常 + * + * @author wangli + * @since 2024/6/25 21:06 + */ +public class RecordException extends RuntimeException { + public RecordException(String message) { + super(message); + } + + public RecordException(String message, Throwable cause) { + super(message, cause); + } + + public RecordException(Throwable cause) { + super(cause); + } + +} diff --git a/axzo-common-datas/axzo-data-mybatis-plus/pom.xml b/axzo-common-datas/axzo-data-mybatis-plus/pom.xml index 3badcb3..31b190f 100644 --- a/axzo-common-datas/axzo-data-mybatis-plus/pom.xml +++ b/axzo-common-datas/axzo-data-mybatis-plus/pom.xml @@ -7,7 +7,7 @@ axzo-common-datas cn.axzo.framework.data - 1.0.0-SNAPSHOT + ${revision} axzo-data-mybatis-plus @@ -50,4 +50,4 @@ fastjson - \ No newline at end of file + diff --git a/axzo-common-datas/pom.xml b/axzo-common-datas/pom.xml index 79cf886..8c10f54 100644 --- a/axzo-common-datas/pom.xml +++ b/axzo-common-datas/pom.xml @@ -7,7 +7,7 @@ axzo-framework-commons cn.axzo.framework - 1.0.0-SNAPSHOT + ${revision} cn.axzo.framework.data @@ -18,4 +18,4 @@ axzo-data-mybatis-plus - \ No newline at end of file + diff --git a/axzo-common-domain/pom.xml b/axzo-common-domain/pom.xml index cdda956..2739554 100644 --- a/axzo-common-domain/pom.xml +++ b/axzo-common-domain/pom.xml @@ -7,7 +7,7 @@ axzo-framework-commons cn.axzo.framework - 1.0.0-SNAPSHOT + ${revision} axzo-common-domain @@ -103,4 +103,4 @@ provided - \ No newline at end of file + diff --git a/axzo-common-domain/src/main/java/cn/axzo/framework/domain/web/result/ApiReportResult.java b/axzo-common-domain/src/main/java/cn/axzo/framework/domain/web/result/ApiReportResult.java new file mode 100644 index 0000000..266ad7f --- /dev/null +++ b/axzo-common-domain/src/main/java/cn/axzo/framework/domain/web/result/ApiReportResult.java @@ -0,0 +1,100 @@ +package cn.axzo.framework.domain.web.result; + +import cn.axzo.framework.domain.web.ApiException; +import cn.axzo.framework.domain.web.code.IRespCode; +import io.swagger.annotations.ApiModelProperty; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.beans.ConstructorProperties; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +import static cn.axzo.framework.domain.web.code.BaseCode.SERVER_ERROR; +import static cn.axzo.framework.domain.web.code.BaseCode.SUCCESS; + +/** + * @Description + * @Author liyong.tian + * @Date 2020/9/7 20:33 + **/ +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ApiReportResult extends ApiCoreResult { + + @ApiModelProperty(value = "异常记录信息", position = 101) + protected Map analysis = new HashMap<>(); + + public static ApiReportResult ok() { + return ok(null); + } + + public static ApiReportResult ok(E data) { + return build(SUCCESS, data); + } + + public static ApiReportResult err(IRespCode code) { + return err(code, code.getMessage()); + } + + public static ApiReportResult err(IRespCode code, String message) { + return err(Integer.parseInt(code.getRespCode()), message); + } + + public static ApiReportResult err(IRespCode code, E data) { + return build(Integer.parseInt(code.getRespCode()), code.getMessage(), data); + } + + public static ApiReportResult err(String message) { + return err(Integer.parseInt(SERVER_ERROR.getRespCode()), message); + } + + public static ApiReportResult err(Integer code, String message) { + if (code == null) { + return err(message); + } + return build(code, message, null); + } + + public static ApiReportResult build(IRespCode code) { + return build(code, null); + } + + public static ApiReportResult build(IRespCode code, E data) { + return build(Integer.parseInt(code.getRespCode()), code.getMessage(), data); + } + + public static ApiReportResult build(Integer code, String message, E data) { + return new ApiReportResult<>(code, message, data); + } + + public static ApiReportResult with(Throwable e) { + if (e != null && e instanceof ApiException) { + return err(((ApiException) e).getCode(), e.getMessage()); + } + if (e != null) { + return err(SERVER_ERROR, e.getMessage()); + } + return err(SERVER_ERROR); + } + + public ApiReportResult(Integer code, String msg, E data) { + super(code, msg, data); + } + + public Map getAnalysis() { + return analysis; + } + + public void setAnalysis(Map analysis) { + this.analysis = analysis; + } + + @Override + public Map toMap() { + Map map = super.toMap(); + map.put("analysis", getAnalysis()); + return map; + } +} diff --git a/axzo-common-framework/axzo-common-context/pom.xml b/axzo-common-framework/axzo-common-context/pom.xml index e4bb04a..c833457 100644 --- a/axzo-common-framework/axzo-common-context/pom.xml +++ b/axzo-common-framework/axzo-common-context/pom.xml @@ -7,7 +7,7 @@ axzo-common-framework cn.axzo.framework.framework - 1.0.0-SNAPSHOT + ${revision} axzo-common-context diff --git a/axzo-common-framework/pom.xml b/axzo-common-framework/pom.xml index 3193757..7882835 100644 --- a/axzo-common-framework/pom.xml +++ b/axzo-common-framework/pom.xml @@ -7,7 +7,7 @@ axzo-framework-commons cn.axzo.framework - 1.0.0-SNAPSHOT + ${revision} cn.axzo.framework.framework diff --git a/axzo-common-jackson/jackson-datatype-enumstd/pom.xml b/axzo-common-jackson/jackson-datatype-enumstd/pom.xml index 3dba5fb..59faa66 100644 --- a/axzo-common-jackson/jackson-datatype-enumstd/pom.xml +++ b/axzo-common-jackson/jackson-datatype-enumstd/pom.xml @@ -5,7 +5,7 @@ axzo-common-jackson cn.axzo.framework.jackson - 1.0.0-SNAPSHOT + ${revision} 4.0.0 @@ -39,4 +39,4 @@ - \ No newline at end of file + diff --git a/axzo-common-jackson/jackson-datatype-fraction/pom.xml b/axzo-common-jackson/jackson-datatype-fraction/pom.xml index 3503394..17eadd1 100644 --- a/axzo-common-jackson/jackson-datatype-fraction/pom.xml +++ b/axzo-common-jackson/jackson-datatype-fraction/pom.xml @@ -5,7 +5,7 @@ axzo-common-jackson cn.axzo.framework.jackson - 1.0.0-SNAPSHOT + ${revision} 4.0.0 @@ -44,4 +44,4 @@ - \ No newline at end of file + diff --git a/axzo-common-jackson/jackson-datatype-period/pom.xml b/axzo-common-jackson/jackson-datatype-period/pom.xml index 8a69dbb..2a16e57 100644 --- a/axzo-common-jackson/jackson-datatype-period/pom.xml +++ b/axzo-common-jackson/jackson-datatype-period/pom.xml @@ -5,7 +5,7 @@ axzo-common-jackson cn.axzo.framework.jackson - 1.0.0-SNAPSHOT + ${revision} 4.0.0 @@ -40,4 +40,4 @@ - \ No newline at end of file + diff --git a/axzo-common-jackson/jackson-datatype-string-trim/pom.xml b/axzo-common-jackson/jackson-datatype-string-trim/pom.xml index 59cdf49..9513f9d 100644 --- a/axzo-common-jackson/jackson-datatype-string-trim/pom.xml +++ b/axzo-common-jackson/jackson-datatype-string-trim/pom.xml @@ -5,7 +5,7 @@ axzo-common-jackson cn.axzo.framework.jackson - 1.0.0-SNAPSHOT + ${revision} 4.0.0 @@ -33,4 +33,4 @@ - \ No newline at end of file + diff --git a/axzo-common-jackson/jackson-starter/pom.xml b/axzo-common-jackson/jackson-starter/pom.xml index 099fbc0..b02d841 100644 --- a/axzo-common-jackson/jackson-starter/pom.xml +++ b/axzo-common-jackson/jackson-starter/pom.xml @@ -5,7 +5,7 @@ axzo-common-jackson cn.axzo.framework.jackson - 1.0.0-SNAPSHOT + ${revision} 4.0.0 @@ -86,4 +86,4 @@ - \ No newline at end of file + diff --git a/axzo-common-jackson/jackson-utility/pom.xml b/axzo-common-jackson/jackson-utility/pom.xml index e74dde0..e1e2280 100644 --- a/axzo-common-jackson/jackson-utility/pom.xml +++ b/axzo-common-jackson/jackson-utility/pom.xml @@ -5,7 +5,7 @@ axzo-common-jackson cn.axzo.framework.jackson - 1.0.0-SNAPSHOT + ${revision} 4.0.0 @@ -157,4 +157,4 @@ - \ No newline at end of file + diff --git a/axzo-common-jackson/pom.xml b/axzo-common-jackson/pom.xml index a83c3d1..c553240 100644 --- a/axzo-common-jackson/pom.xml +++ b/axzo-common-jackson/pom.xml @@ -5,7 +5,7 @@ axzo-framework-commons cn.axzo.framework - 1.0.0-SNAPSHOT + ${revision} 4.0.0 @@ -94,4 +94,4 @@ - \ No newline at end of file + diff --git a/axzo-common-loggings/axzo-common-trace/pom.xml b/axzo-common-loggings/axzo-common-trace/pom.xml index c7efbb1..e3e74f8 100644 --- a/axzo-common-loggings/axzo-common-trace/pom.xml +++ b/axzo-common-loggings/axzo-common-trace/pom.xml @@ -5,7 +5,7 @@ axzo-common-loggings cn.axzo.framework.logging - 1.0.0-SNAPSHOT + ${revision} axzo-common-trace Axzo Common trace diff --git a/axzo-common-loggings/log4j2-starter/pom.xml b/axzo-common-loggings/log4j2-starter/pom.xml index 69fc665..cb99d3c 100644 --- a/axzo-common-loggings/log4j2-starter/pom.xml +++ b/axzo-common-loggings/log4j2-starter/pom.xml @@ -7,7 +7,7 @@ axzo-common-loggings cn.axzo.framework.logging - 1.0.0-SNAPSHOT + ${revision} log4j2-starter @@ -35,4 +35,4 @@ true - \ No newline at end of file + diff --git a/axzo-common-loggings/logback-starter/pom.xml b/axzo-common-loggings/logback-starter/pom.xml index 94e8047..d1fbda6 100644 --- a/axzo-common-loggings/logback-starter/pom.xml +++ b/axzo-common-loggings/logback-starter/pom.xml @@ -7,7 +7,7 @@ axzo-common-loggings cn.axzo.framework.logging - 1.0.0-SNAPSHOT + ${revision} logback-starter @@ -19,4 +19,4 @@ spring-boot-starter-logging - \ No newline at end of file + diff --git a/axzo-common-loggings/pom.xml b/axzo-common-loggings/pom.xml index 81b55f1..7112e2f 100644 --- a/axzo-common-loggings/pom.xml +++ b/axzo-common-loggings/pom.xml @@ -5,7 +5,7 @@ axzo-framework-commons cn.axzo.framework - 1.0.0-SNAPSHOT + ${revision} 4.0.0 @@ -20,4 +20,4 @@ logback-starter axzo-common-trace - \ No newline at end of file + diff --git a/axzo-common-math/pom.xml b/axzo-common-math/pom.xml index a39907a..0f10086 100644 --- a/axzo-common-math/pom.xml +++ b/axzo-common-math/pom.xml @@ -7,7 +7,7 @@ axzo-framework-commons cn.axzo.framework - 1.0.0-SNAPSHOT + ${revision} axzo-common-math diff --git a/axzo-common-rocketmq/pom.xml b/axzo-common-rocketmq/pom.xml index baec135..2d9f0a1 100644 --- a/axzo-common-rocketmq/pom.xml +++ b/axzo-common-rocketmq/pom.xml @@ -6,7 +6,7 @@ axzo-framework-commons cn.axzo.framework - 1.0.0-SNAPSHOT + ${revision} cn.axzo.framework.rocketmq @@ -33,4 +33,4 @@ - \ No newline at end of file + diff --git a/axzo-common-rocketmq/src/main/java/cn/axzo/framework/rocketmq/EventConsumer.java b/axzo-common-rocketmq/src/main/java/cn/axzo/framework/rocketmq/EventConsumer.java index e782d67..68468d5 100644 --- a/axzo-common-rocketmq/src/main/java/cn/axzo/framework/rocketmq/EventConsumer.java +++ b/axzo-common-rocketmq/src/main/java/cn/axzo/framework/rocketmq/EventConsumer.java @@ -80,10 +80,12 @@ public interface EventConsumer { /** * 只elapse 超过多少毫秒才进行日志记录。默认 3ms */ + @Setter private Long logElapsedThreshold; private Map headers; private Map ext; + @Setter private Long maxAllowElapsedMillis; private Boolean logEnabled; private transient Predicate logFilter; diff --git a/axzo-common-validator/pom.xml b/axzo-common-validator/pom.xml index 6354142..3a38af6 100644 --- a/axzo-common-validator/pom.xml +++ b/axzo-common-validator/pom.xml @@ -7,7 +7,7 @@ axzo-framework-commons cn.axzo.framework - 1.0.0-SNAPSHOT + ${revision} axzo-common-validator diff --git a/axzo-common-web/pom.xml b/axzo-common-web/pom.xml index 6176422..e14c423 100644 --- a/axzo-common-web/pom.xml +++ b/axzo-common-web/pom.xml @@ -7,7 +7,7 @@ axzo-framework-commons cn.axzo.framework - 1.0.0-SNAPSHOT + ${revision} axzo-common-web @@ -43,5 +43,18 @@ com.baomidou mybatis-plus-core + + + javax.servlet + javax.servlet-api + 4.0.1 + provided + + + io.github.openfeign + feign-core + compile + true + - \ No newline at end of file + diff --git a/axzo-common-web/src/main/java/cn.axzo.framework.web/feign/FeignRecordExceptionInterceptor.java b/axzo-common-web/src/main/java/cn.axzo.framework.web/feign/FeignRecordExceptionInterceptor.java new file mode 100644 index 0000000..ae7da8a --- /dev/null +++ b/axzo-common-web/src/main/java/cn.axzo.framework.web/feign/FeignRecordExceptionInterceptor.java @@ -0,0 +1,69 @@ +package cn.axzo.framework.web.feign; + +import feign.RequestInterceptor; +import feign.RequestTemplate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; +import org.springframework.util.StringUtils; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; +import java.util.Objects; + +import static cn.axzo.framework.web.filter.BasicRecordExceptionFilter.MICRO_SERVER_RECORD_ERROR_GET_PARAM_NAME; +import static cn.axzo.framework.web.filter.BasicRecordExceptionFilter.MICRO_SERVER_RECORD_ERROR_HEADER_NAME; + + +/** + * 用于 Feign 调用时,传递外部 HTTP 请求中携带的特殊 Header + * + * @author wangli + * @since 2024/6/24 19:29 + */ +public class FeignRecordExceptionInterceptor implements RequestInterceptor { + private static final Logger log = LoggerFactory.getLogger(FeignRecordExceptionInterceptor.class); + + @Override + public void apply(RequestTemplate template) { + HttpServletRequest originalRequest = getOriginalRequest(); + + setRequestParams(template, originalRequest); + + setXMetaDataTag(template, originalRequest); + } + + private void setRequestParams(RequestTemplate template, HttpServletRequest originalRequest) { + String[] packageNames = null; + if(Objects.nonNull(originalRequest)) { + packageNames = originalRequest.getParameterValues(MICRO_SERVER_RECORD_ERROR_GET_PARAM_NAME); + } + if (Objects.isNull(packageNames) || packageNames.length == 0) { + packageNames = StringUtils.commaDelimitedListToStringArray(MDC.get(MICRO_SERVER_RECORD_ERROR_GET_PARAM_NAME)); + } + template.query(MICRO_SERVER_RECORD_ERROR_GET_PARAM_NAME, packageNames); + } + + private static void setXMetaDataTag(RequestTemplate template, HttpServletRequest originalRequest) { + String headerValue = null; + if(Objects.nonNull(originalRequest)) { + // 需要传递外部传入的标识 + headerValue = originalRequest.getHeader(MICRO_SERVER_RECORD_ERROR_HEADER_NAME); + } + if (!StringUtils.hasText(headerValue)) { + headerValue = MDC.get(MICRO_SERVER_RECORD_ERROR_HEADER_NAME); + } + template.header(MICRO_SERVER_RECORD_ERROR_HEADER_NAME, headerValue); + } + + public static HttpServletRequest getOriginalRequest() { + try { + ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + return attributes.getRequest(); + } catch (Exception e) { + log.debug("not HttpServletRequest instance bean found"); + return null; + } + } +} diff --git a/axzo-common-web/src/main/java/cn.axzo.framework.web/filter/BasicRecordExceptionFilter.java b/axzo-common-web/src/main/java/cn.axzo.framework.web/filter/BasicRecordExceptionFilter.java new file mode 100644 index 0000000..0a94356 --- /dev/null +++ b/axzo-common-web/src/main/java/cn.axzo.framework.web/filter/BasicRecordExceptionFilter.java @@ -0,0 +1,41 @@ +package cn.axzo.framework.web.filter; + +import org.slf4j.MDC; +import org.springframework.core.Ordered; +import org.springframework.core.PriorityOrdered; +import org.springframework.util.StringUtils; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + + +/** + * 用于入口请求收集记录异常开关信息 + * + * @author wangli + * @since 2024/6/25 09:40 + */ +public class BasicRecordExceptionFilter extends OncePerRequestFilter implements PriorityOrdered { + public static final String MICRO_SERVER_RECORD_ERROR_HEADER_NAME = "X-Metadata-Tag"; + public static final String MICRO_SERVER_RECORD_ERROR_GET_PARAM_NAME = "pkg"; + public static final String MICRO_SERVER_RECORD_ERROR_FILTER_PACKAGE_VALUE = "cn.axzo"; + @Override + public int getOrder() { + return Ordered.HIGHEST_PRECEDENCE; + } + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + String[] packageNames = request.getParameterValues(MICRO_SERVER_RECORD_ERROR_GET_PARAM_NAME); + MDC.put(MICRO_SERVER_RECORD_ERROR_GET_PARAM_NAME, StringUtils.arrayToCommaDelimitedString(packageNames)); + + // 需要传递外部传入的标识 + MDC.put(MICRO_SERVER_RECORD_ERROR_HEADER_NAME, request.getHeader(MICRO_SERVER_RECORD_ERROR_HEADER_NAME)); + + filterChain.doFilter(request, response); + } +} diff --git a/axzo-common-webmvc/pom.xml b/axzo-common-webmvc/pom.xml index d1a7453..52ba1bb 100644 --- a/axzo-common-webmvc/pom.xml +++ b/axzo-common-webmvc/pom.xml @@ -5,7 +5,7 @@ axzo-framework-commons cn.axzo.framework - 1.0.0-SNAPSHOT + ${revision} 4.0.0 @@ -73,4 +73,4 @@ provided - \ No newline at end of file + diff --git a/pom.xml b/pom.xml index 705c95d..6978671 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ cn.axzo.framework axzo-framework-commons - 1.0.0-SNAPSHOT + ${revision} pom Axzo Framework Commons @@ -41,7 +41,8 @@ 2.0.0-SNAPSHOT - 1.0.0-SNAPSHOT + 1.0.0-SNAPSHOT + ${revision}