diff --git a/micrometer-support/pom.xml b/micrometer-support/pom.xml new file mode 100644 index 0000000000..f38349c232 --- /dev/null +++ b/micrometer-support/pom.xml @@ -0,0 +1,31 @@ + + + + java-operator-sdk + io.javaoperatorsdk + 1.9.8-SNAPSHOT + + 4.0.0 + + micrometer-support + Operator SDK - Micrometer Support + + + 11 + 11 + + + + + io.micrometer + micrometer-core + + + io.javaoperatorsdk + operator-framework-core + + + + \ No newline at end of file diff --git a/micrometer-support/src/main/java/io/javaoperatorsdk/operator/micrometer/MicrometerMetrics.java b/micrometer-support/src/main/java/io/javaoperatorsdk/operator/micrometer/MicrometerMetrics.java new file mode 100644 index 0000000000..7dd5515173 --- /dev/null +++ b/micrometer-support/src/main/java/io/javaoperatorsdk/operator/micrometer/MicrometerMetrics.java @@ -0,0 +1,83 @@ +package io.javaoperatorsdk.operator.micrometer; + +import java.util.Collections; +import java.util.Map; + +import io.javaoperatorsdk.operator.Metrics; +import io.javaoperatorsdk.operator.processing.DefaultEventHandler.EventMonitor; +import io.javaoperatorsdk.operator.processing.event.Event; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Timer; + +public class MicrometerMetrics implements Metrics { + + public static final String PREFIX = "operator.sdk."; + private final MeterRegistry registry; + private final EventMonitor monitor = new EventMonitor() { + @Override + public void processedEvent(String uid, Event event) { + incrementProcessedEventsNumber(); + } + + @Override + public void failedEvent(String uid, Event event) { + incrementControllerRetriesNumber(); + } + }; + + public MicrometerMetrics(MeterRegistry registry) { + this.registry = registry; + } + + public T timeControllerExecution(ControllerExecution execution) { + final var name = execution.controllerName(); + final var execName = PREFIX + "controllers.execution." + execution.name(); + final var timer = + Timer.builder(execName) + .tags("controller", name) + .publishPercentiles(0.3, 0.5, 0.95) + .publishPercentileHistogram() + .register(registry); + try { + final var result = timer.record(execution::execute); + final var successType = execution.successTypeName(result); + registry + .counter(execName + ".success", "controller", name, "type", successType) + .increment(); + return result; + } catch (Exception e) { + final var exception = e.getClass().getSimpleName(); + registry + .counter(execName + ".failure", "controller", name, "exception", exception) + .increment(); + throw e; + } + } + + public void incrementControllerRetriesNumber() { + registry + .counter( + PREFIX + "retry.on.exception", "retry", "retryCounter", "type", + "retryException") + .increment(); + + } + + public void incrementProcessedEventsNumber() { + registry + .counter( + PREFIX + "total.events.received", "events", "totalEvents", "type", + "eventsReceived") + .increment(); + + } + + public > T monitorSizeOf(T map, String name) { + return registry.gaugeMapSize(PREFIX + name + ".size", Collections.emptyList(), map); + } + + @Override + public EventMonitor getEventMonitor() { + return monitor; + } +} diff --git a/operator-framework-core/pom.xml b/operator-framework-core/pom.xml index 33ba79521f..0ed9219c18 100644 --- a/operator-framework-core/pom.xml +++ b/operator-framework-core/pom.xml @@ -62,10 +62,6 @@ org.slf4j slf4j-api - - io.micrometer - micrometer-core - org.junit.jupiter diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/Metrics.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/Metrics.java index 31269b5d41..a3f3ddccb5 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/Metrics.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/Metrics.java @@ -1,41 +1,15 @@ package io.javaoperatorsdk.operator; -import java.util.Collections; import java.util.Map; -import java.util.concurrent.TimeUnit; -import java.util.function.ToDoubleFunction; -import java.util.function.ToLongFunction; -import io.micrometer.core.instrument.Clock; -import io.micrometer.core.instrument.Counter; -import io.micrometer.core.instrument.DistributionSummary; -import io.micrometer.core.instrument.FunctionCounter; -import io.micrometer.core.instrument.FunctionTimer; -import io.micrometer.core.instrument.Gauge; -import io.micrometer.core.instrument.Measurement; -import io.micrometer.core.instrument.Meter; -import io.micrometer.core.instrument.MeterRegistry; -import io.micrometer.core.instrument.Timer; -import io.micrometer.core.instrument.distribution.DistributionStatisticConfig; -import io.micrometer.core.instrument.distribution.pause.PauseDetector; -import io.micrometer.core.instrument.noop.NoopCounter; -import io.micrometer.core.instrument.noop.NoopDistributionSummary; -import io.micrometer.core.instrument.noop.NoopFunctionCounter; -import io.micrometer.core.instrument.noop.NoopFunctionTimer; -import io.micrometer.core.instrument.noop.NoopGauge; -import io.micrometer.core.instrument.noop.NoopMeter; -import io.micrometer.core.instrument.noop.NoopTimer; +import io.javaoperatorsdk.operator.processing.DefaultEventHandler; +import io.javaoperatorsdk.operator.processing.DefaultEventHandler.EventMonitor; -public class Metrics { - public static final Metrics NOOP = new Metrics(new NoopMeterRegistry(Clock.SYSTEM)); - public static final String PREFIX = "operator.sdk."; - private final MeterRegistry registry; +public interface Metrics { + Metrics NOOP = new Metrics() {}; - public Metrics(MeterRegistry registry) { - this.registry = registry; - } - public interface ControllerExecution { + interface ControllerExecution { String name(); String controllerName(); @@ -45,111 +19,19 @@ public interface ControllerExecution { T execute(); } - public T timeControllerExecution(ControllerExecution execution) { - final var name = execution.controllerName(); - final var execName = PREFIX + "controllers.execution." + execution.name(); - final var timer = - Timer.builder(execName) - .tags("controller", name) - .publishPercentiles(0.3, 0.5, 0.95) - .publishPercentileHistogram() - .register(registry); - try { - final var result = timer.record(execution::execute); - final var successType = execution.successTypeName(result); - registry - .counter(execName + ".success", "controller", name, "type", successType) - .increment(); - return result; - } catch (Exception e) { - final var exception = e.getClass().getSimpleName(); - registry - .counter(execName + ".failure", "controller", name, "exception", exception) - .increment(); - throw e; - } + default T timeControllerExecution(ControllerExecution execution) { + return execution.execute(); } - public void incrementControllerRetriesNumber() { - registry - .counter( - PREFIX + "retry.on.exception", "retry", "retryCounter", "type", - "retryException") - .increment(); - - } + default void incrementControllerRetriesNumber() {} - public void incrementProcessedEventsNumber() { - registry - .counter( - PREFIX + "total.events.received", "events", "totalEvents", "type", - "eventsReceived") - .increment(); + default void incrementProcessedEventsNumber() {} + default > T monitorSizeOf(T map, String name) { + return map; } - public > T monitorSizeOf(T map, String name) { - return registry.gaugeMapSize(PREFIX + name + ".size", Collections.emptyList(), map); - } - - public static class NoopMeterRegistry extends MeterRegistry { - public NoopMeterRegistry(Clock clock) { - super(clock); - } - - @Override - protected Gauge newGauge(Meter.Id id, T t, ToDoubleFunction toDoubleFunction) { - return new NoopGauge(id); - } - - @Override - protected Counter newCounter(Meter.Id id) { - return new NoopCounter(id); - } - - @Override - protected Timer newTimer( - Meter.Id id, - DistributionStatisticConfig distributionStatisticConfig, - PauseDetector pauseDetector) { - return new NoopTimer(id); - } - - @Override - protected DistributionSummary newDistributionSummary( - Meter.Id id, DistributionStatisticConfig distributionStatisticConfig, double v) { - return new NoopDistributionSummary(id); - } - - @Override - protected Meter newMeter(Meter.Id id, Meter.Type type, Iterable iterable) { - return new NoopMeter(id); - } - - @Override - protected FunctionTimer newFunctionTimer( - Meter.Id id, - T t, - ToLongFunction toLongFunction, - ToDoubleFunction toDoubleFunction, - TimeUnit timeUnit) { - return new NoopFunctionTimer(id); - } - - @Override - protected FunctionCounter newFunctionCounter( - Meter.Id id, T t, ToDoubleFunction toDoubleFunction) { - return new NoopFunctionCounter(id); - } - - @Override - protected TimeUnit getBaseTimeUnit() { - return TimeUnit.SECONDS; - } - - @Override - protected DistributionStatisticConfig defaultHistogramConfig() { - return DistributionStatisticConfig.NONE; - } + default DefaultEventHandler.EventMonitor getEventMonitor() { + return EventMonitor.NOOP; } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/Operator.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/Operator.java index 834c516c68..c61bcb04b3 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/Operator.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/Operator.java @@ -18,9 +18,6 @@ import io.javaoperatorsdk.operator.api.config.ControllerConfiguration; import io.javaoperatorsdk.operator.api.config.ExecutorServiceManager; import io.javaoperatorsdk.operator.processing.ConfiguredController; -import io.javaoperatorsdk.operator.processing.DefaultEventHandler; -import io.javaoperatorsdk.operator.processing.DefaultEventHandler.EventMonitor; -import io.javaoperatorsdk.operator.processing.event.Event; @SuppressWarnings("rawtypes") public class Operator implements AutoCloseable { @@ -32,17 +29,6 @@ public class Operator implements AutoCloseable { public Operator(KubernetesClient k8sClient, ConfigurationService configurationService) { this.k8sClient = k8sClient; this.configurationService = configurationService; - DefaultEventHandler.setEventMonitor(new EventMonitor() { - @Override - public void processedEvent(String uid, Event event) { - configurationService.getMetrics().incrementProcessedEventsNumber(); - } - - @Override - public void failedEvent(String uid, Event event) { - configurationService.getMetrics().incrementControllerRetriesNumber(); - } - }); } /** Adds a shutdown hook that automatically calls {@link #close()} when the app shuts down. */ diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/DefaultEventHandler.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/DefaultEventHandler.java index b44994a018..34201ae2df 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/DefaultEventHandler.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/DefaultEventHandler.java @@ -14,6 +14,7 @@ import org.slf4j.LoggerFactory; import io.fabric8.kubernetes.client.CustomResource; +import io.javaoperatorsdk.operator.Metrics; import io.javaoperatorsdk.operator.api.RetryInfo; import io.javaoperatorsdk.operator.api.config.ConfigurationService; import io.javaoperatorsdk.operator.api.config.ExecutorServiceManager; @@ -35,13 +36,9 @@ public class DefaultEventHandler> implements EventHandler { private static final Logger log = LoggerFactory.getLogger(DefaultEventHandler.class); - private static EventMonitor monitor = new EventMonitor() { - @Override - public void processedEvent(String uid, Event event) {} - @Override - public void failedEvent(String uid, Event event) {} - }; + @Deprecated + private static EventMonitor monitor = EventMonitor.NOOP; private final EventBuffer eventBuffer; private final Set underProcessing = new HashSet<>(); @@ -51,6 +48,7 @@ public void failedEvent(String uid, Event event) {} private final ExecutorService executor; private final String controllerName; private final ReentrantLock lock = new ReentrantLock(); + private final EventMonitor eventMonitor; private volatile boolean running; private DefaultEventSourceManager eventSourceManager; @@ -58,16 +56,17 @@ public DefaultEventHandler(ConfiguredController controller) { this(ExecutorServiceManager.instance().executorService(), controller.getConfiguration().getName(), new EventDispatcher<>(controller), - GenericRetry.fromConfiguration(controller.getConfiguration().getRetryConfiguration())); + GenericRetry.fromConfiguration(controller.getConfiguration().getRetryConfiguration()), + controller.getConfiguration().getConfigurationService().getMetrics().getEventMonitor()); } DefaultEventHandler(EventDispatcher eventDispatcher, String relatedControllerName, Retry retry) { - this(null, relatedControllerName, eventDispatcher, retry); + this(null, relatedControllerName, eventDispatcher, retry, null); } private DefaultEventHandler(ExecutorService executor, String relatedControllerName, - EventDispatcher eventDispatcher, Retry retry) { + EventDispatcher eventDispatcher, Retry retry, EventMonitor monitor) { this.running = true; this.executor = executor == null @@ -78,14 +77,44 @@ private DefaultEventHandler(ExecutorService executor, String relatedControllerNa this.eventDispatcher = eventDispatcher; this.retry = retry; this.eventBuffer = new EventBuffer(); + this.eventMonitor = monitor != null ? monitor : EventMonitor.NOOP; + } + + public void setEventSourceManager(DefaultEventSourceManager eventSourceManager) { + this.eventSourceManager = eventSourceManager; } + /** + * @deprecated the EventMonitor to be used should now be retrieved from + * {@link Metrics#getEventMonitor()} + * @param monitor + */ + @Deprecated public static void setEventMonitor(EventMonitor monitor) { DefaultEventHandler.monitor = monitor; } - public void setEventSourceManager(DefaultEventSourceManager eventSourceManager) { - this.eventSourceManager = eventSourceManager; + /* + * TODO: promote this interface to top-level, probably create a `monitoring` package? + */ + public interface EventMonitor { + EventMonitor NOOP = new EventMonitor() { + @Override + public void processedEvent(String uid, Event event) {} + + @Override + public void failedEvent(String uid, Event event) {} + }; + + void processedEvent(String uid, Event event); + + void failedEvent(String uid, Event event); + } + + private EventMonitor monitor() { + // todo: remove us of static monitor, only here for backwards compatibility + return DefaultEventHandler.monitor != EventMonitor.NOOP ? DefaultEventHandler.monitor + : eventMonitor; } @Override @@ -102,6 +131,7 @@ public void handleEvent(Event event) { log.debug("Received event: {}", event); final Predicate selector = event.getCustomResourcesSelector(); + final var monitor = monitor(); for (String uid : eventSourceManager.getLatestResourceUids(selector)) { eventBuffer.addEvent(uid, event); monitor.processedEvent(uid, event); @@ -168,6 +198,7 @@ void eventProcessingFinished( if (retry != null && postExecutionControl.exceptionDuringExecution()) { handleRetryOnException(executionScope); + final var monitor = monitor(); executionScope.getEvents() .forEach(e -> monitor.failedEvent(executionScope.getCustomResourceUid(), e)); return; @@ -296,11 +327,6 @@ private void unsetUnderExecution(String customResourceUid) { underProcessing.remove(customResourceUid); } - public interface EventMonitor { - void processedEvent(String uid, Event event); - - void failedEvent(String uid, Event event); - } private class ControllerExecution implements Runnable { private final ExecutionScope executionScope; diff --git a/pom.xml b/pom.xml index baa171c9fd..cbc7448686 100644 --- a/pom.xml +++ b/pom.xml @@ -78,6 +78,7 @@ operator-framework-junit5 operator-framework samples + micrometer-support