ObservabilityConfig.java
package com.licensis.notaire.config;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.binder.jvm.ClassLoaderMetrics;
import io.micrometer.core.instrument.binder.jvm.JvmGcMetrics;
import io.micrometer.core.instrument.binder.jvm.JvmMemoryMetrics;
import io.micrometer.core.instrument.binder.jvm.JvmThreadMetrics;
import io.micrometer.core.instrument.binder.system.ProcessorMetrics;
import io.micrometer.core.instrument.binder.system.UptimeMetrics;
import io.micrometer.core.instrument.binder.logging.LogbackMetrics;
import jakarta.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
/**
* Observability configuration for Micrometer metrics.
* Registers JVM, system, and application-specific metrics
* for Prometheus scraping via the /actuator/prometheus endpoint.
*/
@Configuration
public class ObservabilityConfig {
private static final Logger LOG = LoggerFactory.getLogger(ObservabilityConfig.class);
private final MeterRegistry meterRegistry;
public ObservabilityConfig(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}
@PostConstruct
public void init() {
LOG.info("Initializing observability metrics for Notaire Backend");
}
/**
* JVM Memory metrics - heap, non-heap, buffer pools
*/
@Bean
@ConditionalOnClass(name = "io.micrometer.core.instrument.binder.jvm.JvmMemoryMetrics")
public JvmMemoryMetrics jvmMemoryMetrics() {
JvmMemoryMetrics metrics = new JvmMemoryMetrics();
metrics.bindTo(meterRegistry);
LOG.debug("JVM Memory metrics registered");
return metrics;
}
/**
* JVM GC metrics - garbage collection pauses and counts
*/
@Bean
@ConditionalOnClass(name = "io.micrometer.core.instrument.binder.jvm.JvmGcMetrics")
public JvmGcMetrics jvmGcMetrics() {
JvmGcMetrics metrics = new JvmGcMetrics();
metrics.bindTo(meterRegistry);
LOG.debug("JVM GC metrics registered");
return metrics;
}
/**
* JVM Thread metrics - live, daemon, peak thread counts
*/
@Bean
@ConditionalOnClass(name = "io.micrometer.core.instrument.binder.jvm.JvmThreadMetrics")
public JvmThreadMetrics jvmThreadMetrics() {
JvmThreadMetrics metrics = new JvmThreadMetrics();
metrics.bindTo(meterRegistry);
LOG.debug("JVM Thread metrics registered");
return metrics;
}
/**
* Processor metrics - CPU usage, load average
*/
@Bean
@ConditionalOnClass(name = "io.micrometer.core.instrument.binder.system.ProcessorMetrics")
public ProcessorMetrics processorMetrics() {
ProcessorMetrics metrics = new ProcessorMetrics();
metrics.bindTo(meterRegistry);
LOG.debug("Processor metrics registered");
return metrics;
}
/**
* Uptime metrics - application uptime tracking
*/
@Bean
@ConditionalOnClass(name = "io.micrometer.core.instrument.binder.system.UptimeMetrics")
public UptimeMetrics uptimeMetrics() {
UptimeMetrics metrics = new UptimeMetrics();
metrics.bindTo(meterRegistry);
LOG.debug("Uptime metrics registered");
return metrics;
}
/**
* Logback metrics - log event counting by level
*/
@Bean
@ConditionalOnClass(name = "io.micrometer.core.instrument.binder.logging.LogbackMetrics")
public LogbackMetrics logbackMetrics() {
LogbackMetrics metrics = new LogbackMetrics();
metrics.bindTo(meterRegistry);
LOG.debug("Logback metrics registered");
return metrics;
}
/**
* ClassLoader metrics - loaded/unloaded classes
*/
@Bean
@ConditionalOnClass(name = "io.micrometer.core.instrument.binder.jvm.ClassLoaderMetrics")
public ClassLoaderMetrics classLoaderMetrics() {
ClassLoaderMetrics metrics = new ClassLoaderMetrics();
metrics.bindTo(meterRegistry);
LOG.debug("ClassLoader metrics registered");
return metrics;
}
/**
* Register a custom Gauge for notaire-shared module version
* This exposes the shared module version as a metric for tracking
*/
@Bean
public Gauge notaireSharedVersion() {
return Gauge.builder("notaire_shared_version", () -> {
try {
String version = getClass().getPackage().getImplementationVersion();
return version != null ? Double.parseDouble(version.replaceAll("[^0-9.]", "").substring(0, 1)) : 1.0;
} catch (Exception e) {
return 1.0;
}
})
.description("Notaire shared module version indicator")
.tag("module", "notaire-shared")
.register(meterRegistry);
}
}