完成单机TEST统一入口与可靠投递回归收尾
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
| W3C Trace Context | `src/main/java/com/baiyue/common/web/TraceParent.java`;严格接收 version 00,响应 `traceparent`、`X-Trace-Id` 与 MDC `traceId` 使用同一 trace ID |
|
||||
| Feign Trace 传播 | `FeignTraceRequestInterceptor.java`、`FeignTracePropagationAutoConfiguration.java`;仅 Feign 存在时装配,optional 依赖不向无 Feign 服务传递 |
|
||||
| 可靠事件 HTTP 客户端配置 | `EventDeliveryFeignConfiguration.java`;由业务事件 Feign 客户端显式选择,在最终构建阶段固定连接/读取超时、禁止重定向和客户端内部重试,不作为全局自动配置 |
|
||||
| 投递审计和指标装配 | `EventDeliveryObservationAutoConfiguration.java`、`EventDeliveryMetrics.java`、`EventDeliveryObservation.java`;仅EventDeliveryProducer标记的业务Relay装配必需Journal/Metrics,其他JDBC服务不装配;定时采样固定事件白名单,Prometheus仅读内存快照,采样失败保留旧值并暴露失败/时间,错误标签不含载荷或令牌 |
|
||||
| RestClient Trace 传播 | `RestClientTraceInterceptor.java`、`RestClientTracePropagationAutoConfiguration.java`;Spring 管理的 Builder 自动装配,原始 Builder 可显式复用拦截器 |
|
||||
| 自动配置入口 | `src/main/java/com/baiyue/common/web/CommonWebAutoConfiguration.java`;为只扫描自身包的服务注册异常处理器和 Correlation 过滤器 |
|
||||
| 全局异常映射与业务失败指标 | `src/main/java/com/baiyue/common/web/GlobalExceptionHandler.java`;指标 `baiyue.api.business.failures` 按有限 `code` 标记 |
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
<name>baiyue-common-web</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><optional>true</optional></dependency>
|
||||
<dependency><groupId>com.baiyue.shared</groupId><artifactId>common-event-contract</artifactId><version>${project.version}</version></dependency>
|
||||
<dependency><groupId>com.h2database</groupId><artifactId>h2</artifactId><scope>test</scope></dependency>
|
||||
<dependency>
|
||||
|
||||
@@ -42,7 +42,8 @@ public class EventDeliveryFailure extends IllegalStateException {
|
||||
&& ("NoFallbackAvailableException".equals(current.getClass().getSimpleName())
|
||||
|| "No fallback available.".equals(current.getMessage()))) current = current.getCause();
|
||||
String message;
|
||||
if (current instanceof FeignException http) message = "HTTP " + http.status();
|
||||
if (current instanceof feign.codec.DecodeException) message = "Invalid consumer response contract";
|
||||
else if (current instanceof FeignException http) message = "HTTP " + http.status();
|
||||
else if (current instanceof JsonProcessingException) message = "Invalid event JSON";
|
||||
else message = current.getMessage() == null ? "unknown" : current.getMessage();
|
||||
String text = current.getClass().getSimpleName() + ": " + message;
|
||||
|
||||
@@ -19,6 +19,7 @@ public final class EventDeliveryObservation {
|
||||
public static String errorCode(Throwable error, boolean permanent, boolean requestStarted) {
|
||||
Set<Throwable> seen = Collections.newSetFromMap(new IdentityHashMap<>());
|
||||
for (Throwable cause = error; cause != null && seen.add(cause); cause = cause.getCause()) {
|
||||
if (cause instanceof feign.codec.DecodeException) return "INVALID_RESPONSE";
|
||||
if (cause instanceof FeignException http && http.status() >= 100 && http.status() <= 599) {
|
||||
return "HTTP_" + http.status();
|
||||
}
|
||||
|
||||
+2
@@ -5,6 +5,7 @@ import com.baiyue.common.core.outbox.EventDeliveryProducer;
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -13,6 +14,7 @@ import org.springframework.transaction.PlatformTransactionManager;
|
||||
|
||||
@AutoConfiguration(after = {JdbcTemplateAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class})
|
||||
@ConditionalOnBean(EventDeliveryProducer.class)
|
||||
@ConditionalOnClass(name = "org.springframework.jdbc.core.JdbcTemplate")
|
||||
public class EventDeliveryObservationAutoConfiguration {
|
||||
@Bean
|
||||
EventDeliveryJournal eventDeliveryJournal(JdbcTemplate jdbc, PlatformTransactionManager manager) {
|
||||
|
||||
@@ -5,6 +5,15 @@ import org.junit.jupiter.api.Test;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
class EventDeliveryObservationTest {
|
||||
@Test void malformedSuccessResponseIsNotReportedAsAnHttpSuccess() {
|
||||
var request = feign.Request.create(feign.Request.HttpMethod.POST, "http://consumer.invalid/events",
|
||||
java.util.Map.of(), new byte[0], java.nio.charset.StandardCharsets.UTF_8, new feign.RequestTemplate());
|
||||
var failure = new feign.codec.DecodeException(200, "secret remote payload", request);
|
||||
assertThat(EventDeliveryObservation.errorCode(failure, false, true)).isEqualTo("INVALID_RESPONSE");
|
||||
assertThat(EventDeliveryFailure.diagnostic(failure, "token"))
|
||||
.contains("Invalid consumer response contract").doesNotContain("secret", "HTTP 200");
|
||||
assertThat(EventDeliveryFailure.isPermanent(failure)).isFalse();
|
||||
}
|
||||
@Test void auditIdentitySupportsTheFormalUuidAndUlidContractWithoutSavingMalformedPayload() {
|
||||
var mapper = new ObjectMapper();
|
||||
for (String id : new String[]{"00000000-0000-4000-8000-000000000019", "01ARZ3NDEKTSV4RRFFQ69G5FAV"}) {
|
||||
|
||||
Reference in New Issue
Block a user