Merge remote-tracking branch 'origin/master' into feature/REQ-2300

This commit is contained in:
李昆鹏 2024-08-12 18:03:12 +08:00
commit f5b72655b4
38 changed files with 507 additions and 60 deletions

View File

@ -7,7 +7,7 @@
<parent>
<artifactId>axzo-framework-commons</artifactId>
<groupId>cn.axzo.framework</groupId>
<version>1.0.0-SNAPSHOT</version>
<version>${revision}</version>
</parent>
<artifactId>axzo-common-autoconfigure</artifactId>
@ -94,5 +94,11 @@
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-core</artifactId>
<scope>compile</scope>
<optional>true</optional>
</dependency>
</dependencies>
</project>
</project>

View File

@ -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));
};
}
}

View File

@ -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();
}
}
}

View File

@ -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<ExceptionResultHandler<? extends Throwable, ? extends Result>> 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<ExceptionResultHandler<? extends Throwable, ? extends Result>> 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<String, Object> 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<Object> err = err(512, "discovery server internal error");
Map<String, ExceptionWrapper> recordExceptionMap = new HashMap<>();
recordExceptionMap.put(StringUtils.hasText(applicationName) ? applicationName : "not applicationName found", rebuildThrowable(e, request));
err.setAnalysis(recordExceptionMap);
return err;
}
private Optional<Result> _handleException(WebRequest webRequest, Throwable e) {
Map<String, Object> 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<? extends Throwable, ? extends Result> 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<String> stackTrace = new ArrayList<>();
public ExceptionWrapper(Throwable throwable, HttpServletRequest request) {
this.causeMessage = throwable.getMessage();
init(throwable, request);
}
private void init(Throwable throwable, HttpServletRequest request) {
List<StackTraceElement> 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<String> 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;
}
}

View File

@ -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,\

View File

@ -7,7 +7,7 @@
<parent>
<artifactId>axzo-framework-commons</artifactId>
<groupId>cn.axzo.framework</groupId>
<version>1.0.0-SNAPSHOT</version>
<version>${revision}</version>
</parent>
<artifactId>axzo-common-boot</artifactId>
@ -82,4 +82,4 @@
</plugin>
</plugins>
</build>
</project>
</project>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>cn.axzo.framework</groupId>
<artifactId>axzo-framework-commons</artifactId>
<version>1.0.0-SNAPSHOT</version>
<version>${revision}</version>
</parent>
<name>Axzo Common Cache</name>
<artifactId>axzo-common-cache</artifactId>
@ -21,4 +21,4 @@
<artifactId>jetcache-starter-redis</artifactId>
</dependency>
</dependencies>
</project>
</project>

View File

@ -7,7 +7,7 @@
<parent>
<artifactId>axzo-common-clients</artifactId>
<groupId>cn.axzo.framework.client</groupId>
<version>1.0.0-SNAPSHOT</version>
<version>${revision}</version>
</parent>
<artifactId>feign-starter</artifactId>
@ -62,4 +62,4 @@
</dependency>
</dependencies>
</project>
</project>

View File

@ -7,7 +7,7 @@
<parent>
<artifactId>axzo-framework-commons</artifactId>
<groupId>cn.axzo.framework</groupId>
<version>1.0.0-SNAPSHOT</version>
<version>${revision}</version>
</parent>
<groupId>cn.axzo.framework.client</groupId>
@ -19,4 +19,4 @@
<module>retrofit-starter</module>
<module>feign-starter</module>
</modules>
</project>
</project>

View File

@ -7,7 +7,7 @@
<parent>
<groupId>cn.axzo.framework.client</groupId>
<artifactId>axzo-common-clients</artifactId>
<version>1.0.0-SNAPSHOT</version>
<version>${revision}</version>
</parent>
<artifactId>retrofit-starter</artifactId>
@ -35,4 +35,4 @@
<artifactId>adapter-rxjava2</artifactId>
</dependency>
</dependencies>
</project>
</project>

View File

@ -7,7 +7,7 @@
<parent>
<artifactId>axzo-framework-commons</artifactId>
<groupId>cn.axzo.framework</groupId>
<version>1.0.0-SNAPSHOT</version>
<version>${revision}</version>
</parent>
<artifactId>axzo-common-core</artifactId>

View File

@ -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);
}
}

View File

@ -7,7 +7,7 @@
<parent>
<artifactId>axzo-common-datas</artifactId>
<groupId>cn.axzo.framework.data</groupId>
<version>1.0.0-SNAPSHOT</version>
<version>${revision}</version>
</parent>
<artifactId>axzo-data-mybatis-plus</artifactId>
@ -50,4 +50,4 @@
<artifactId>fastjson</artifactId>
</dependency>
</dependencies>
</project>
</project>

View File

@ -7,7 +7,7 @@
<parent>
<artifactId>axzo-framework-commons</artifactId>
<groupId>cn.axzo.framework</groupId>
<version>1.0.0-SNAPSHOT</version>
<version>${revision}</version>
</parent>
<groupId>cn.axzo.framework.data</groupId>
@ -18,4 +18,4 @@
<modules>
<module>axzo-data-mybatis-plus</module>
</modules>
</project>
</project>

View File

@ -7,7 +7,7 @@
<parent>
<artifactId>axzo-framework-commons</artifactId>
<groupId>cn.axzo.framework</groupId>
<version>1.0.0-SNAPSHOT</version>
<version>${revision}</version>
</parent>
<artifactId>axzo-common-domain</artifactId>
@ -103,4 +103,4 @@
<scope>provided</scope>
</dependency>
</dependencies>
</project>
</project>

View File

@ -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<E> extends ApiCoreResult<E> {
@ApiModelProperty(value = "异常记录信息", position = 101)
protected Map<String, ?> analysis = new HashMap<>();
public static <E> ApiReportResult<E> ok() {
return ok(null);
}
public static <E> ApiReportResult<E> ok(E data) {
return build(SUCCESS, data);
}
public static <E> ApiReportResult<E> err(IRespCode code) {
return err(code, code.getMessage());
}
public static <E> ApiReportResult<E> err(IRespCode code, String message) {
return err(Integer.parseInt(code.getRespCode()), message);
}
public static <E> ApiReportResult<E> err(IRespCode code, E data) {
return build(Integer.parseInt(code.getRespCode()), code.getMessage(), data);
}
public static <E> ApiReportResult<E> err(String message) {
return err(Integer.parseInt(SERVER_ERROR.getRespCode()), message);
}
public static <E> ApiReportResult<E> err(Integer code, String message) {
if (code == null) {
return err(message);
}
return build(code, message, null);
}
public static <E> ApiReportResult<E> build(IRespCode code) {
return build(code, null);
}
public static <E> ApiReportResult<E> build(IRespCode code, E data) {
return build(Integer.parseInt(code.getRespCode()), code.getMessage(), data);
}
public static <E> ApiReportResult<E> build(Integer code, String message, E data) {
return new ApiReportResult<>(code, message, data);
}
public static <E> ApiReportResult<E> 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<String, ?> getAnalysis() {
return analysis;
}
public void setAnalysis(Map<String, ?> analysis) {
this.analysis = analysis;
}
@Override
public Map<String, Object> toMap() {
Map<String, Object> map = super.toMap();
map.put("analysis", getAnalysis());
return map;
}
}

View File

@ -7,7 +7,7 @@
<parent>
<artifactId>axzo-common-framework</artifactId>
<groupId>cn.axzo.framework.framework</groupId>
<version>1.0.0-SNAPSHOT</version>
<version>${revision}</version>
</parent>
<artifactId>axzo-common-context</artifactId>

View File

@ -7,7 +7,7 @@
<parent>
<artifactId>axzo-framework-commons</artifactId>
<groupId>cn.axzo.framework</groupId>
<version>1.0.0-SNAPSHOT</version>
<version>${revision}</version>
</parent>
<groupId>cn.axzo.framework.framework</groupId>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>axzo-common-jackson</artifactId>
<groupId>cn.axzo.framework.jackson</groupId>
<version>1.0.0-SNAPSHOT</version>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -39,4 +39,4 @@
</plugin>
</plugins>
</build>
</project>
</project>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>axzo-common-jackson</artifactId>
<groupId>cn.axzo.framework.jackson</groupId>
<version>1.0.0-SNAPSHOT</version>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -44,4 +44,4 @@
</plugins>
</build>
</project>
</project>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>axzo-common-jackson</artifactId>
<groupId>cn.axzo.framework.jackson</groupId>
<version>1.0.0-SNAPSHOT</version>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -40,4 +40,4 @@
</plugin>
</plugins>
</build>
</project>
</project>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>axzo-common-jackson</artifactId>
<groupId>cn.axzo.framework.jackson</groupId>
<version>1.0.0-SNAPSHOT</version>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -33,4 +33,4 @@
</plugin>
</plugins>
</build>
</project>
</project>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>axzo-common-jackson</artifactId>
<groupId>cn.axzo.framework.jackson</groupId>
<version>1.0.0-SNAPSHOT</version>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -86,4 +86,4 @@
</plugin>
</plugins>
</build>
</project>
</project>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>axzo-common-jackson</artifactId>
<groupId>cn.axzo.framework.jackson</groupId>
<version>1.0.0-SNAPSHOT</version>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -157,4 +157,4 @@
</plugin>
</plugins>
</build>
</project>
</project>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>axzo-framework-commons</artifactId>
<groupId>cn.axzo.framework</groupId>
<version>1.0.0-SNAPSHOT</version>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -94,4 +94,4 @@
</plugins>
</pluginManagement>
</build>
</project>
</project>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>axzo-common-loggings</artifactId>
<groupId>cn.axzo.framework.logging</groupId>
<version>1.0.0-SNAPSHOT</version>
<version>${revision}</version>
</parent>
<artifactId>axzo-common-trace</artifactId>
<name>Axzo Common trace</name>

View File

@ -7,7 +7,7 @@
<parent>
<artifactId>axzo-common-loggings</artifactId>
<groupId>cn.axzo.framework.logging</groupId>
<version>1.0.0-SNAPSHOT</version>
<version>${revision}</version>
</parent>
<artifactId>log4j2-starter</artifactId>
@ -35,4 +35,4 @@
<optional>true</optional>
</dependency>
</dependencies>
</project>
</project>

View File

@ -7,7 +7,7 @@
<parent>
<artifactId>axzo-common-loggings</artifactId>
<groupId>cn.axzo.framework.logging</groupId>
<version>1.0.0-SNAPSHOT</version>
<version>${revision}</version>
</parent>
<artifactId>logback-starter</artifactId>
@ -19,4 +19,4 @@
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
</dependencies>
</project>
</project>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>axzo-framework-commons</artifactId>
<groupId>cn.axzo.framework</groupId>
<version>1.0.0-SNAPSHOT</version>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -20,4 +20,4 @@
<module>logback-starter</module>
<module>axzo-common-trace</module>
</modules>
</project>
</project>

View File

@ -7,7 +7,7 @@
<parent>
<artifactId>axzo-framework-commons</artifactId>
<groupId>cn.axzo.framework</groupId>
<version>1.0.0-SNAPSHOT</version>
<version>${revision}</version>
</parent>
<artifactId>axzo-common-math</artifactId>

View File

@ -6,7 +6,7 @@
<parent>
<artifactId>axzo-framework-commons</artifactId>
<groupId>cn.axzo.framework</groupId>
<version>1.0.0-SNAPSHOT</version>
<version>${revision}</version>
</parent>
<groupId>cn.axzo.framework.rocketmq</groupId>
@ -33,4 +33,4 @@
</dependency>
</dependencies>
</project>
</project>

View File

@ -80,10 +80,12 @@ public interface EventConsumer {
/**
* 只elapse 超过多少毫秒才进行日志记录默认 3ms
*/
@Setter
private Long logElapsedThreshold;
private Map<String, byte[]> headers;
private Map<String, Object> ext;
@Setter
private Long maxAllowElapsedMillis;
private Boolean logEnabled;
private transient Predicate<Event> logFilter;

View File

@ -7,7 +7,7 @@
<parent>
<artifactId>axzo-framework-commons</artifactId>
<groupId>cn.axzo.framework</groupId>
<version>1.0.0-SNAPSHOT</version>
<version>${revision}</version>
</parent>
<artifactId>axzo-common-validator</artifactId>

View File

@ -7,7 +7,7 @@
<parent>
<artifactId>axzo-framework-commons</artifactId>
<groupId>cn.axzo.framework</groupId>
<version>1.0.0-SNAPSHOT</version>
<version>${revision}</version>
</parent>
<artifactId>axzo-common-web</artifactId>
@ -43,5 +43,18 @@
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-core</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-core</artifactId>
<scope>compile</scope>
<optional>true</optional>
</dependency>
</dependencies>
</project>
</project>

View File

@ -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;
}
}
}

View File

@ -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);
}
}

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>axzo-framework-commons</artifactId>
<groupId>cn.axzo.framework</groupId>
<version>1.0.0-SNAPSHOT</version>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -73,4 +73,4 @@
<scope>provided</scope>
</dependency>
</dependencies>
</project>
</project>

View File

@ -13,7 +13,7 @@
<groupId>cn.axzo.framework</groupId>
<artifactId>axzo-framework-commons</artifactId>
<version>1.0.0-SNAPSHOT</version>
<version>${revision}</version>
<packaging>pom</packaging>
<name>Axzo Framework Commons</name>
@ -41,7 +41,8 @@
<properties>
<axzo-dependencies.version>2.0.0-SNAPSHOT</axzo-dependencies.version>
<axzo-commons.version>1.0.0-SNAPSHOT</axzo-commons.version>
<revision>1.0.0-SNAPSHOT</revision>
<axzo-commons.version>${revision}</axzo-commons.version>
</properties>
<dependencies>