AnalyticsObservability.java
package com.ecommerce.analytics.infrastructure.observability;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
@Component
public class AnalyticsObservability {
private static final String SERVICE = "analytics-service";
private final MeterRegistry registry;
private final Map<String, Counter> eventCounters = new ConcurrentHashMap<>();
private final Map<String, Counter> rebuildCounters = new ConcurrentHashMap<>();
private final AtomicLong reconciliationIssues = new AtomicLong();
public AnalyticsObservability(MeterRegistry registry) {
this.registry = registry;
Gauge.builder(
"ecommerce.analytics.reconciliation.issues",
reconciliationIssues,
AtomicLong::doubleValue)
.description("Issues found by the latest bounded analytics reconciliation")
.baseUnit("issues")
.tag("service", SERVICE)
.register(registry);
}
public void eventAccepted(String eventType) {
eventCounter(eventType, "accepted").increment();
}
public void eventDuplicate(String eventType) {
eventCounter(eventType, "duplicate").increment();
}
public void reconciliationCompleted(long issueCount) {
reconciliationIssues.set(Math.max(0, issueCount));
}
public void rebuildCompleted(boolean success) {
String outcome = success ? "success" : "failure";
rebuildCounters.computeIfAbsent(outcome, key -> Counter.builder("ecommerce.analytics.rebuilds")
.description("Analytics projection rebuild commands by outcome")
.tag("service", SERVICE)
.tag("outcome", key)
.register(registry))
.increment();
}
private Counter eventCounter(String eventType, String outcome) {
String key = eventType + ':' + outcome;
return eventCounters.computeIfAbsent(key, ignored -> Counter.builder("ecommerce.analytics.events")
.description("Analytics source events by bounded type and processing outcome")
.tag("service", SERVICE)
.tag("event_type", eventType)
.tag("outcome", outcome)
.register(registry));
}
}