package com.baiyue.eventbridge.metrics; import com.baiyue.common.event.EventEnvelope; import com.baiyue.common.event.EventTypes; import com.baiyue.eventbridge.domain.BridgeInbox; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Timer; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.time.Clock; import java.time.Duration; import java.time.LocalDateTime; import java.util.Locale; import java.util.Set; /** Low-cardinality transport metrics for the cross-region event bridge. */ @Component @Slf4j public class EventBridgeMetrics { public static final String RECEIVE_METRIC = "baiyue.event.bridge.receive"; public static final String TRANSPORT_METRIC = "baiyue.event.bridge.transport"; public static final String DELIVERY_LATENCY_METRIC = "baiyue.event.bridge.delivery.latency"; private static final Set KNOWN_EVENT_TYPES = Set.of( EventTypes.ORDER_APPROVED, EventTypes.ORDER_CANCELLED, EventTypes.FIRST_LEG_SHIPMENT_DEPARTED, EventTypes.FIRST_LEG_LOADING_PLAN_COMPLETED, EventTypes.FIRST_LEG_CONTAINER_SHIPPING_NODE_RECORDED, EventTypes.OVERSEAS_CONTAINER_UNLOADED, EventTypes.OVERSEAS_INBOUND_ORDER_DETAINED_CHANGED, EventTypes.OVERSEAS_DELIVERY_COMPLETED, EventTypes.BUSINESS_FEE_SOURCE_CHANGED, EventTypes.WALLET_COMMAND_REQUESTED, EventTypes.WALLET_COMMAND_COMPLETED, EventTypes.IAM_USER_STATUS_CHANGED, EventTypes.IAM_TENANT_STATUS_CHANGED, EventTypes.IAM_PERMISSION_VERSION_CHANGED, EventTypes.LAST_MILE_HANDOFF_ACCEPTED, EventTypes.LAST_MILE_HANDOFF_REJECTED, EventTypes.LAST_MILE_MILESTONE_CHANGED, EventTypes.LAST_MILE_COMPLETED, EventTypes.TRACKING_MILESTONE_PROJECTED); private final MeterRegistry registry; private final Clock clock; @Autowired public EventBridgeMetrics(MeterRegistry registry) { this(registry, Clock.systemDefaultZone()); } EventBridgeMetrics(MeterRegistry registry, Clock clock) { this.registry = registry; this.clock = clock; } public void recordReceive(EventEnvelope envelope, ReceiveResult result) { try { registry.counter(RECEIVE_METRIC, "target_region", targetRegion(envelope.targetRegion().name()), "event_type", eventType(envelope.eventType()), "result", result.name()).increment(); } catch (RuntimeException exception) { log.warn("Event Bridge 接收指标记录失败,不影响事件事务", exception); } } public void recordTransport(BridgeInbox event, TransportResult result) { try { String targetRegion = targetRegion(event.getTargetRegion()); String eventType = eventType(event.getEventType()); registry.counter(TRANSPORT_METRIC, "target_region", targetRegion, "event_type", eventType, "result", result.name()).increment(); if (result.acknowledged() && event.getReceivedAt() != null) { Duration latency = Duration.between(event.getReceivedAt(), LocalDateTime.now(clock)); if (latency.isNegative()) { latency = Duration.ZERO; } Timer.builder(DELIVERY_LATENCY_METRIC) .description("Time from Event Bridge receipt to downstream acknowledgement") .serviceLevelObjectives(Duration.ofSeconds(60), Duration.ofMinutes(15)) .tags("target_region", targetRegion, "event_type", eventType) .register(registry).record(latency); } } catch (RuntimeException exception) { log.warn("Event Bridge 传输指标记录失败,不影响可靠性状态机", exception); } } public static String targetRegion(String value) { String normalized = value == null ? "" : value.trim().toUpperCase(Locale.ROOT); return "CN".equals(normalized) || "OVERSEAS".equals(normalized) ? normalized : "OTHER"; } public static String eventType(String value) { return value != null && KNOWN_EVENT_TYPES.contains(value) ? value : "OTHER"; } public enum ReceiveResult { RECEIVED, DUPLICATE, CONFLICT } public enum TransportResult { DELIVERED(true), RETRY(false), DEAD(false), ACKNOWLEDGED_LEASE_LOST(true), FAILED_LEASE_LOST(false), MANUAL_REPLAY(false); private final boolean acknowledged; TransportResult(boolean acknowledged) { this.acknowledged = acknowledged; } boolean acknowledged() { return acknowledged; } } }