From dd37a283daa89288d39bbae7b0c87784106777ac Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Fri, 21 Feb 2025 22:51:02 +0100 Subject: [PATCH] GH-1068 - Automatically create counters for cross-module application events. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We now create counters for each cross-module application event published. The counters can be customized through ModulithEventMetricsCustomizer beans registered in the ApplicationContext. Refactored the packages to let ….modulith.observability become the API package and moved all implementation components into ….modulith.observability.support. --- .../observability/ModulithEventMetrics.java | 57 +++++++ .../ModulithEventMetricsCustomizer.java | 32 ++++ .../ModuleObservabilityAutoConfiguration.java | 27 +++- ...tModuleObservabilityAutoConfiguration.java | 2 +- .../CrossModuleEventCounterFactory.java | 152 ++++++++++++++++++ .../DefaultModulithObservationConvention.java | 6 +- .../{ => support}/DefaultObservedModule.java | 2 +- .../LocalServiceRenamingSpanFilter.java | 2 +- .../{ => support}/ModuleEntryInterceptor.java | 4 +- .../{ => support}/ModuleEventListener.java | 15 +- .../ModuleObservabilityBeanPostProcessor.java | 2 +- .../ModuleObservabilitySupport.java | 2 +- .../ModulePassingObservationFilter.java | 2 +- .../{ => support}/ModulithContext.java | 2 +- .../{ => support}/ModulithMetrics.java | 2 +- .../ModulithObservationConvention.java | 2 +- .../{ => support}/ModulithObservations.java | 6 +- .../{ => support}/ObservedModule.java | 2 +- .../{ => support}/ObservedModuleType.java | 2 +- ...tModuleObservabilityBeanPostProcessor.java | 2 +- .../observability/support/package-info.java | 5 + .../ExampleApplicationIntegrationTests.java | 2 +- ...ossModuleEventCounterFactoryUnitTests.java | 80 +++++++++ .../DefaultObservedModuleUnitTests.java | 4 +- ...lityBeanPostProcessorIntegrationTests.java | 5 +- ...servabilityBeanPostProcessorUnitTests.java | 3 +- .../ObservedModuleTypeUnitTests.java | 4 +- 27 files changed, 389 insertions(+), 37 deletions(-) create mode 100644 spring-modulith-observability/src/main/java/org/springframework/modulith/observability/ModulithEventMetrics.java create mode 100644 spring-modulith-observability/src/main/java/org/springframework/modulith/observability/ModulithEventMetricsCustomizer.java create mode 100644 spring-modulith-observability/src/main/java/org/springframework/modulith/observability/support/CrossModuleEventCounterFactory.java rename spring-modulith-observability/src/main/java/org/springframework/modulith/observability/{ => support}/DefaultModulithObservationConvention.java (91%) rename spring-modulith-observability/src/main/java/org/springframework/modulith/observability/{ => support}/DefaultObservedModule.java (98%) rename spring-modulith-observability/src/main/java/org/springframework/modulith/observability/{ => support}/LocalServiceRenamingSpanFilter.java (95%) rename spring-modulith-observability/src/main/java/org/springframework/modulith/observability/{ => support}/ModuleEntryInterceptor.java (97%) rename spring-modulith-observability/src/main/java/org/springframework/modulith/observability/{ => support}/ModuleEventListener.java (85%) rename spring-modulith-observability/src/main/java/org/springframework/modulith/observability/{ => support}/ModuleObservabilityBeanPostProcessor.java (99%) rename spring-modulith-observability/src/main/java/org/springframework/modulith/observability/{ => support}/ModuleObservabilitySupport.java (97%) rename spring-modulith-observability/src/main/java/org/springframework/modulith/observability/{ => support}/ModulePassingObservationFilter.java (97%) rename spring-modulith-observability/src/main/java/org/springframework/modulith/observability/{ => support}/ModulithContext.java (97%) rename spring-modulith-observability/src/main/java/org/springframework/modulith/observability/{ => support}/ModulithMetrics.java (97%) rename spring-modulith-observability/src/main/java/org/springframework/modulith/observability/{ => support}/ModulithObservationConvention.java (95%) rename spring-modulith-observability/src/main/java/org/springframework/modulith/observability/{ => support}/ModulithObservations.java (96%) rename spring-modulith-observability/src/main/java/org/springframework/modulith/observability/{ => support}/ObservedModule.java (97%) rename spring-modulith-observability/src/main/java/org/springframework/modulith/observability/{ => support}/ObservedModuleType.java (98%) rename spring-modulith-observability/src/main/java/org/springframework/modulith/observability/{ => support}/SpringDataRestModuleObservabilityBeanPostProcessor.java (98%) create mode 100644 spring-modulith-observability/src/main/java/org/springframework/modulith/observability/support/package-info.java create mode 100644 spring-modulith-observability/src/test/java/org/springframework/modulith/observability/support/CrossModuleEventCounterFactoryUnitTests.java rename spring-modulith-observability/src/test/java/org/springframework/modulith/observability/{ => support}/DefaultObservedModuleUnitTests.java (91%) rename spring-modulith-observability/src/test/java/org/springframework/modulith/observability/{ => support}/ModuleObservabilityBeanPostProcessorIntegrationTests.java (91%) rename spring-modulith-observability/src/test/java/org/springframework/modulith/observability/{ => support}/ModuleObservabilityBeanPostProcessorUnitTests.java (94%) rename spring-modulith-observability/src/test/java/org/springframework/modulith/observability/{ => support}/ObservedModuleTypeUnitTests.java (93%) diff --git a/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/ModulithEventMetrics.java b/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/ModulithEventMetrics.java new file mode 100644 index 00000000..fa8baea7 --- /dev/null +++ b/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/ModulithEventMetrics.java @@ -0,0 +1,57 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.modulith.observability; + +import io.micrometer.core.instrument.Counter.Builder; + +import java.util.function.BiConsumer; +import java.util.function.Function; + +/** + * SPI to customize the {@link io.micrometer.core.instrument.Counter} instances created for cross-module application + * events. + * + * @author Oliver Drotbohm + * @author Marcin Grzejszczak + * @since 1.4 + */ +public interface ModulithEventMetrics { + + /** + * Customizes a {@link io.micrometer.core.instrument.Counter.Builder} to eventually produce a + * {@link io.micrometer.core.instrument.Counter} for the event of the given type. The {@link Builder} will have been + * set up named after the fully-qualified type name. To customize the creation, also call + * {@link #customize(Class, Function)}. + * + * @param the type of the event. + * @param type must not be {@literal null}. + * @param consumer must not be {@literal null}. + * @return will never be {@literal null}. + * @see #customize(Class, Function) + */ + ModulithEventMetrics customize(Class type, BiConsumer consumer); + + /** + * Customizes the creation of a {@link Builder} for events of the given type. The instances created will still be + * subject to customizations registered via {@link #customize(Class, BiConsumer)}. + * + * @param + * @param type must not be {@literal null}. + * @param factory must not be {@literal null}. + * @return will never be {@literal null}. + */ + ModulithEventMetrics customize(Class type, Function factory); +} diff --git a/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/ModulithEventMetricsCustomizer.java b/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/ModulithEventMetricsCustomizer.java new file mode 100644 index 00000000..969f4123 --- /dev/null +++ b/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/ModulithEventMetricsCustomizer.java @@ -0,0 +1,32 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.modulith.observability; + +/** + * Allows customizing the metrics creation, in particular the counters created for cross-application-module events. + * + * @author Oliver Drotbohm + * @since 1.4 + */ +public interface ModulithEventMetricsCustomizer { + + /** + * Customize the given {@link ModulithEventMetrics}. + * + * @param metrics will never be {@literal null}. + */ + void customize(ModulithEventMetrics metrics); +} diff --git a/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/autoconfigure/ModuleObservabilityAutoConfiguration.java b/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/autoconfigure/ModuleObservabilityAutoConfiguration.java index 53c15930..766b990a 100644 --- a/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/autoconfigure/ModuleObservabilityAutoConfiguration.java +++ b/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/autoconfigure/ModuleObservabilityAutoConfiguration.java @@ -30,10 +30,12 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import org.springframework.core.task.support.ContextPropagatingTaskDecorator; -import org.springframework.modulith.observability.LocalServiceRenamingSpanFilter; -import org.springframework.modulith.observability.ModuleEventListener; -import org.springframework.modulith.observability.ModuleObservabilityBeanPostProcessor; -import org.springframework.modulith.observability.ModulePassingObservationFilter; +import org.springframework.modulith.observability.ModulithEventMetricsCustomizer; +import org.springframework.modulith.observability.support.CrossModuleEventCounterFactory; +import org.springframework.modulith.observability.support.LocalServiceRenamingSpanFilter; +import org.springframework.modulith.observability.support.ModuleEventListener; +import org.springframework.modulith.observability.support.ModuleObservabilityBeanPostProcessor; +import org.springframework.modulith.observability.support.ModulePassingObservationFilter; import org.springframework.modulith.runtime.ApplicationModulesRuntime; /** @@ -45,14 +47,16 @@ class ModuleObservabilityAutoConfiguration { @Bean static ModuleObservabilityBeanPostProcessor moduleTracingBeanPostProcessor(ApplicationModulesRuntime runtime, - ObjectProvider observationRegistry, ConfigurableListableBeanFactory factory, Environment environment) { + ObjectProvider observationRegistry, ConfigurableListableBeanFactory factory, + Environment environment) { return new ModuleObservabilityBeanPostProcessor(runtime, observationRegistry::getObject, factory, environment); } @Bean static ModuleEventListener tracingModuleEventListener(ApplicationModulesRuntime runtime, - ObjectProvider observationRegistry, ObjectProvider meterRegistry) { - return new ModuleEventListener(runtime, observationRegistry::getObject, meterRegistry::getObject); + ObjectProvider observationRegistry, ObjectProvider meterRegistry, + CrossModuleEventCounterFactory configurer) { + return new ModuleEventListener(runtime, observationRegistry::getObject, meterRegistry::getObject, configurer); } // TODO: Have a custom thread pool for modulith @@ -78,4 +82,13 @@ class ModuleObservabilityAutoConfiguration { return new LocalServiceRenamingSpanFilter(); } + @Bean + CrossModuleEventCounterFactory modulithEventCounterFactory(ObjectProvider customizer) { + + var factory = new CrossModuleEventCounterFactory(); + + customizer.stream().forEach(it -> it.customize(factory)); + + return factory; + } } diff --git a/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/autoconfigure/SpringDataRestModuleObservabilityAutoConfiguration.java b/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/autoconfigure/SpringDataRestModuleObservabilityAutoConfiguration.java index bd675ac7..7c15d81d 100644 --- a/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/autoconfigure/SpringDataRestModuleObservabilityAutoConfiguration.java +++ b/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/autoconfigure/SpringDataRestModuleObservabilityAutoConfiguration.java @@ -23,7 +23,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import org.springframework.data.rest.webmvc.RepositoryController; -import org.springframework.modulith.observability.SpringDataRestModuleObservabilityBeanPostProcessor; +import org.springframework.modulith.observability.support.SpringDataRestModuleObservabilityBeanPostProcessor; import org.springframework.modulith.runtime.ApplicationModulesRuntime; /** diff --git a/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/support/CrossModuleEventCounterFactory.java b/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/support/CrossModuleEventCounterFactory.java new file mode 100644 index 00000000..f190815e --- /dev/null +++ b/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/support/CrossModuleEventCounterFactory.java @@ -0,0 +1,152 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.modulith.observability.support; + +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.Counter.Builder; + +import java.util.Comparator; +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Function; + +import org.springframework.modulith.observability.ModulithEventMetrics; +import org.springframework.util.Assert; + +/** + * A factory to create {@link Builder} instances for {@link Counter}s eventually. Target for dependency injection via + * the {@link ModulithEventMetricsCustomizer} interface to allow users to augment the counters with additional + * information. + * + * @author Oliver Drotbohm + * @author Marcin Grzejszczak + * @since 1.4 + */ +public class CrossModuleEventCounterFactory implements ModulithEventMetrics { + + private final SortedSet customizers = new TreeSet<>(); + private final SortedSet creators = new TreeSet<>(); + + /** + * Creates a {@link Builder} instance for the given event applying registered customizers. + * + * @param event must not be {@literal null}. + * @return will never be {@literal null}. + */ + Builder createCounterBuilder(Object event) { + + Assert.notNull(event, "Event must not be null!"); + + // Use most specific creator (default order as defined in ModulithMetricsCustomizer) + var creator = creators.stream() + .filter(it -> it.supports(event)) + .findFirst() + .orElse(ModulithMetricsCustomizer.DEFAULT); + + var builder = creator.createBuilder(event); + + return customizers.stream() + .sorted(Comparator.reverseOrder()) // Inverted order (most specific last) + .filter(it -> it.supports(event)) + .reduce(builder, (it, customizer) -> customizer.augment(event, it), (l, r) -> r); + } + + /* + * (non-Javadoc) + * @see org.springframework.modulith.observability.api.ModulithEventMetricsCustomizer#customize(java.lang.Class, java.util.function.Function) + */ + @SuppressWarnings("unchecked") + @Override + public ModulithEventMetrics customize(Class type, Function factory) { + + creators.add(new ModulithMetricsCustomizer(type, (Function) factory)); + return this; + } + + /* + * (non-Javadoc) + * @see org.springframework.modulith.observability.api.ModulithEventMetricsCustomizer#customize(java.lang.Class, java.util.function.BiConsumer) + */ + @Override + @SuppressWarnings("unchecked") + public CrossModuleEventCounterFactory customize(Class type, BiConsumer consumer) { + + customizers.add(new ModulithMetricsCustomizer(type, (BiConsumer) consumer)); + return this; + } + + private static class ModulithMetricsCustomizer implements Comparable { + + private static final BiConsumer NO_OP = (event, builder) -> {}; + private static final Function DEFAULT_FACTORY = event -> Counter + .builder(event.getClass().getName()); + + public static final ModulithMetricsCustomizer DEFAULT = new ModulithMetricsCustomizer(Object.class, NO_OP); + + private final Class type; + private final Function creator; + private final BiFunction customizer; + + public ModulithMetricsCustomizer(Class type, Function creator) { + + this.type = type; + this.creator = creator; + this.customizer = (event, builder) -> builder; + } + + public ModulithMetricsCustomizer(Class type, BiConsumer creator) { + + this.type = type; + this.creator = DEFAULT_FACTORY; + this.customizer = (event, builder) -> { + creator.accept(event, builder); + return builder; + }; + } + + public Builder createBuilder(Object event) { + return creator.apply(event); + } + + public boolean supports(Object event) { + return type.isInstance(event); + } + + public Builder augment(Object event, Builder builder) { + return customizer.apply(event, builder); + } + + /* + * (non-Javadoc) + * @see java.lang.Comparable#compareTo(java.lang.Object) + */ + @Override + public int compareTo(ModulithMetricsCustomizer that) { + + if (this.type.isAssignableFrom(that.type)) { + return 1; + } + if (that.type.isAssignableFrom(this.type)) { + return -1; + } + + // If classes are not in the same hierarchy, sort by name for consistency + return this.type.getName().compareTo(that.type.getName()); + } + } +} diff --git a/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/DefaultModulithObservationConvention.java b/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/support/DefaultModulithObservationConvention.java similarity index 91% rename from spring-modulith-observability/src/main/java/org/springframework/modulith/observability/DefaultModulithObservationConvention.java rename to spring-modulith-observability/src/main/java/org/springframework/modulith/observability/support/DefaultModulithObservationConvention.java index 031da501..ea39d9dd 100644 --- a/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/DefaultModulithObservationConvention.java +++ b/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/support/DefaultModulithObservationConvention.java @@ -13,12 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.modulith.observability; +package org.springframework.modulith.observability.support; import io.micrometer.common.KeyValues; -import org.springframework.modulith.observability.ModulithObservations.HighKeys; -import org.springframework.modulith.observability.ModulithObservations.LowKeys; +import org.springframework.modulith.observability.support.ModulithObservations.HighKeys; +import org.springframework.modulith.observability.support.ModulithObservations.LowKeys; /** * Default implementation of {@link ModulithObservationConvention}. diff --git a/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/DefaultObservedModule.java b/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/support/DefaultObservedModule.java similarity index 98% rename from spring-modulith-observability/src/main/java/org/springframework/modulith/observability/DefaultObservedModule.java rename to spring-modulith-observability/src/main/java/org/springframework/modulith/observability/support/DefaultObservedModule.java index cea0ddaa..261acc33 100644 --- a/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/DefaultObservedModule.java +++ b/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/support/DefaultObservedModule.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.modulith.observability; +package org.springframework.modulith.observability.support; import java.lang.reflect.Method; import java.util.Arrays; diff --git a/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/LocalServiceRenamingSpanFilter.java b/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/support/LocalServiceRenamingSpanFilter.java similarity index 95% rename from spring-modulith-observability/src/main/java/org/springframework/modulith/observability/LocalServiceRenamingSpanFilter.java rename to spring-modulith-observability/src/main/java/org/springframework/modulith/observability/support/LocalServiceRenamingSpanFilter.java index 40cc0cf0..40a2709a 100644 --- a/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/LocalServiceRenamingSpanFilter.java +++ b/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/support/LocalServiceRenamingSpanFilter.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.modulith.observability; +package org.springframework.modulith.observability.support; import io.micrometer.tracing.exporter.FinishedSpan; import io.micrometer.tracing.exporter.SpanFilter; diff --git a/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/ModuleEntryInterceptor.java b/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/support/ModuleEntryInterceptor.java similarity index 97% rename from spring-modulith-observability/src/main/java/org/springframework/modulith/observability/ModuleEntryInterceptor.java rename to spring-modulith-observability/src/main/java/org/springframework/modulith/observability/support/ModuleEntryInterceptor.java index 9c5ac10c..1a03d6c1 100644 --- a/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/ModuleEntryInterceptor.java +++ b/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/support/ModuleEntryInterceptor.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.modulith.observability; +package org.springframework.modulith.observability.support; import io.micrometer.observation.Observation; import io.micrometer.observation.Observation.Scope; @@ -30,7 +30,7 @@ import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; import org.springframework.lang.Nullable; import org.springframework.modulith.core.ApplicationModuleIdentifier; -import org.springframework.modulith.observability.ModulithObservations.LowKeys; +import org.springframework.modulith.observability.support.ModulithObservations.LowKeys; import org.springframework.util.Assert; /** diff --git a/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/ModuleEventListener.java b/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/support/ModuleEventListener.java similarity index 85% rename from spring-modulith-observability/src/main/java/org/springframework/modulith/observability/ModuleEventListener.java rename to spring-modulith-observability/src/main/java/org/springframework/modulith/observability/support/ModuleEventListener.java index ad188110..c406ed57 100644 --- a/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/ModuleEventListener.java +++ b/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/support/ModuleEventListener.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.modulith.observability; +package org.springframework.modulith.observability.support; import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.MeterRegistry; @@ -25,7 +25,7 @@ import java.util.function.Supplier; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; import org.springframework.context.PayloadApplicationEvent; -import org.springframework.modulith.observability.ModulithObservations.Events; +import org.springframework.modulith.observability.support.ModulithObservations.Events; import org.springframework.modulith.runtime.ApplicationModulesRuntime; import org.springframework.util.Assert; @@ -37,6 +37,7 @@ public class ModuleEventListener implements ApplicationListener observationRegistry; private final Supplier meterRegistry; + private final CrossModuleEventCounterFactory factory; /** * Creates a new {@link ModuleEventListener} for the given {@link ApplicationModulesRuntime} and @@ -45,17 +46,21 @@ public class ModuleEventListener implements ApplicationListener observationRegistrySupplier, Supplier meterRegistrySupplier) { + Supplier observationRegistrySupplier, Supplier meterRegistrySupplier, + CrossModuleEventCounterFactory counterFactory) { Assert.notNull(runtime, "ApplicationModulesRuntime must not be null!"); Assert.notNull(observationRegistrySupplier, "ObservationRegistry must not be null!"); Assert.notNull(meterRegistrySupplier, "MeterRegistry must not be null!"); + Assert.notNull(counterFactory, "ModulithEventCounterFactory must not be null!"); this.runtime = runtime; this.observationRegistry = observationRegistrySupplier; this.meterRegistry = meterRegistrySupplier; + this.factory = counterFactory; } /* @@ -89,9 +94,11 @@ public class ModuleEventListener implements ApplicationListener builder.tag("key", "value")); + + assertCounter(new SampleEvent(), it -> { + assertThat(it.getName()).isEqualTo(SampleEvent.class.getName()); + assertThat(it.getTag("key")).isNotNull(); + }); + } + + @Test // GH-1068 + void usesCreatorAndCustomizer() { + + factory.customize(Object.class, __ -> Counter.builder("Object")); + factory.customize(SampleEvent.class, __ -> Counter.builder("name")); + factory.customize(SampleEvent.class, (__, builder) -> builder.tag("key", "value")); + factory.customize(Object.class, (__, builder) -> builder.tag("key", "value2")); + factory.customize(OtherSampleEvent.class, (__, builder) -> builder.tag("key2", "value3")); + + assertCounter(new SampleEvent(), it -> { + assertThat(it.getName()).isEqualTo("name"); + assertThat(it.getTag("key")).isEqualTo("value"); + assertThat(it.getTag("key2")).isNull(); + }); + + assertCounter(new OtherSampleEvent(), it -> { + assertThat(it.getName()).isEqualTo("Object"); + assertThat(it.getTag("key")).isEqualTo("value2"); + assertThat(it.getTag("key2")).isEqualTo("value3"); + }); + } + + private void assertCounter(Object event, Consumer assertions) { + assertions.accept(factory.createCounterBuilder(event).register(registry).getId()); + } + + static class SampleEvent {} + + static class OtherSampleEvent {} +} diff --git a/spring-modulith-observability/src/test/java/org/springframework/modulith/observability/DefaultObservedModuleUnitTests.java b/spring-modulith-observability/src/test/java/org/springframework/modulith/observability/support/DefaultObservedModuleUnitTests.java similarity index 91% rename from spring-modulith-observability/src/test/java/org/springframework/modulith/observability/DefaultObservedModuleUnitTests.java rename to spring-modulith-observability/src/test/java/org/springframework/modulith/observability/support/DefaultObservedModuleUnitTests.java index 5534befe..ee46fe6d 100644 --- a/spring-modulith-observability/src/test/java/org/springframework/modulith/observability/DefaultObservedModuleUnitTests.java +++ b/spring-modulith-observability/src/test/java/org/springframework/modulith/observability/support/DefaultObservedModuleUnitTests.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.modulith.observability; +package org.springframework.modulith.observability.support; import static org.assertj.core.api.Assertions.*; @@ -27,6 +27,8 @@ import org.junit.jupiter.api.Test; import org.springframework.modulith.core.ApplicationModule; import org.springframework.modulith.core.ApplicationModules; import org.springframework.modulith.core.ArchitecturallyEvidentType; +import org.springframework.modulith.observability.support.DefaultObservedModule; +import org.springframework.modulith.observability.support.ObservedModule; import org.springframework.modulith.test.TestApplicationModules; /** diff --git a/spring-modulith-observability/src/test/java/org/springframework/modulith/observability/ModuleObservabilityBeanPostProcessorIntegrationTests.java b/spring-modulith-observability/src/test/java/org/springframework/modulith/observability/support/ModuleObservabilityBeanPostProcessorIntegrationTests.java similarity index 91% rename from spring-modulith-observability/src/test/java/org/springframework/modulith/observability/ModuleObservabilityBeanPostProcessorIntegrationTests.java rename to spring-modulith-observability/src/test/java/org/springframework/modulith/observability/support/ModuleObservabilityBeanPostProcessorIntegrationTests.java index d9dec6cb..43f2494a 100644 --- a/spring-modulith-observability/src/test/java/org/springframework/modulith/observability/ModuleObservabilityBeanPostProcessorIntegrationTests.java +++ b/spring-modulith-observability/src/test/java/org/springframework/modulith/observability/support/ModuleObservabilityBeanPostProcessorIntegrationTests.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.modulith.observability; +package org.springframework.modulith.observability.support; import static org.assertj.core.api.Assertions.*; @@ -28,7 +28,8 @@ import org.springframework.boot.SpringApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.modulith.observability.ModuleObservabilityBeanPostProcessor.ApplicationModuleObservingAdvisor; +import org.springframework.modulith.observability.support.ModuleObservabilityBeanPostProcessor; +import org.springframework.modulith.observability.support.ModuleObservabilityBeanPostProcessor.ApplicationModuleObservingAdvisor; import org.springframework.modulith.runtime.ApplicationModulesRuntime; import org.springframework.modulith.runtime.ApplicationRuntime; import org.springframework.modulith.test.TestApplicationModules; diff --git a/spring-modulith-observability/src/test/java/org/springframework/modulith/observability/ModuleObservabilityBeanPostProcessorUnitTests.java b/spring-modulith-observability/src/test/java/org/springframework/modulith/observability/support/ModuleObservabilityBeanPostProcessorUnitTests.java similarity index 94% rename from spring-modulith-observability/src/test/java/org/springframework/modulith/observability/ModuleObservabilityBeanPostProcessorUnitTests.java rename to spring-modulith-observability/src/test/java/org/springframework/modulith/observability/support/ModuleObservabilityBeanPostProcessorUnitTests.java index cfe15e40..7e8eaf25 100644 --- a/spring-modulith-observability/src/test/java/org/springframework/modulith/observability/ModuleObservabilityBeanPostProcessorUnitTests.java +++ b/spring-modulith-observability/src/test/java/org/springframework/modulith/observability/support/ModuleObservabilityBeanPostProcessorUnitTests.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.modulith.observability; +package org.springframework.modulith.observability.support; import static org.assertj.core.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; @@ -26,6 +26,7 @@ import org.springframework.amqp.rabbit.annotation.RabbitListenerAnnotationBeanPo import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.mock.env.MockEnvironment; +import org.springframework.modulith.observability.support.ModuleObservabilityBeanPostProcessor; import org.springframework.modulith.runtime.ApplicationModulesRuntime; /** diff --git a/spring-modulith-observability/src/test/java/org/springframework/modulith/observability/ObservedModuleTypeUnitTests.java b/spring-modulith-observability/src/test/java/org/springframework/modulith/observability/support/ObservedModuleTypeUnitTests.java similarity index 93% rename from spring-modulith-observability/src/test/java/org/springframework/modulith/observability/ObservedModuleTypeUnitTests.java rename to spring-modulith-observability/src/test/java/org/springframework/modulith/observability/support/ObservedModuleTypeUnitTests.java index 91437c39..c7cbaa4c 100644 --- a/spring-modulith-observability/src/test/java/org/springframework/modulith/observability/ObservedModuleTypeUnitTests.java +++ b/spring-modulith-observability/src/test/java/org/springframework/modulith/observability/support/ObservedModuleTypeUnitTests.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.modulith.observability; +package org.springframework.modulith.observability.support; import static org.assertj.core.api.Assertions.*; @@ -26,6 +26,8 @@ import org.springframework.modulith.core.ApplicationModule; import org.springframework.modulith.core.ApplicationModules; import org.springframework.modulith.core.ArchitecturallyEvidentType; import org.springframework.modulith.core.Types; +import org.springframework.modulith.observability.support.DefaultObservedModule; +import org.springframework.modulith.observability.support.ObservedModuleType; import org.springframework.modulith.test.TestApplicationModules; import org.springframework.util.ReflectionUtils;