GH-1068 - Automatically create counters for cross-module application events.

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.
This commit is contained in:
Oliver Drotbohm
2025-02-21 22:51:02 +01:00
parent 417e1c1e8c
commit dd37a283da
27 changed files with 389 additions and 37 deletions

View File

@@ -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 <T> 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)
*/
<T> ModulithEventMetrics customize(Class<T> type, BiConsumer<T, Builder> 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 <T>
* @param type must not be {@literal null}.
* @param factory must not be {@literal null}.
* @return will never be {@literal null}.
*/
<T> ModulithEventMetrics customize(Class<T> type, Function<T, Builder> factory);
}

View File

@@ -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);
}

View File

@@ -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> observationRegistry, ConfigurableListableBeanFactory factory, Environment environment) {
ObjectProvider<ObservationRegistry> observationRegistry, ConfigurableListableBeanFactory factory,
Environment environment) {
return new ModuleObservabilityBeanPostProcessor(runtime, observationRegistry::getObject, factory, environment);
}
@Bean
static ModuleEventListener tracingModuleEventListener(ApplicationModulesRuntime runtime,
ObjectProvider<ObservationRegistry> observationRegistry, ObjectProvider<MeterRegistry> meterRegistry) {
return new ModuleEventListener(runtime, observationRegistry::getObject, meterRegistry::getObject);
ObjectProvider<ObservationRegistry> observationRegistry, ObjectProvider<MeterRegistry> 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<ModulithEventMetricsCustomizer> customizer) {
var factory = new CrossModuleEventCounterFactory();
customizer.stream().forEach(it -> it.customize(factory));
return factory;
}
}

View File

@@ -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;
/**

View File

@@ -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<ModulithMetricsCustomizer> customizers = new TreeSet<>();
private final SortedSet<ModulithMetricsCustomizer> 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 <T> ModulithEventMetrics customize(Class<T> type, Function<T, Builder> factory) {
creators.add(new ModulithMetricsCustomizer(type, (Function<Object, Builder>) factory));
return this;
}
/*
* (non-Javadoc)
* @see org.springframework.modulith.observability.api.ModulithEventMetricsCustomizer#customize(java.lang.Class, java.util.function.BiConsumer)
*/
@Override
@SuppressWarnings("unchecked")
public <T> CrossModuleEventCounterFactory customize(Class<T> type, BiConsumer<T, Builder> consumer) {
customizers.add(new ModulithMetricsCustomizer(type, (BiConsumer<Object, Builder>) consumer));
return this;
}
private static class ModulithMetricsCustomizer implements Comparable<ModulithMetricsCustomizer> {
private static final BiConsumer<Object, Builder> NO_OP = (event, builder) -> {};
private static final Function<Object, Builder> 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<Object, Builder> creator;
private final BiFunction<Object, Builder, Builder> customizer;
public ModulithMetricsCustomizer(Class<?> type, Function<Object, Builder> creator) {
this.type = type;
this.creator = creator;
this.customizer = (event, builder) -> builder;
}
public ModulithMetricsCustomizer(Class<?> type, BiConsumer<Object, Builder> 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());
}
}
}

View File

@@ -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}.

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
/**

View File

@@ -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<ApplicationEvent
private final ApplicationModulesRuntime runtime;
private final Supplier<ObservationRegistry> observationRegistry;
private final Supplier<MeterRegistry> 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<ApplicationEvent
* @param runtime must not be {@literal null}.
* @param observationRegistrySupplier must not be {@literal null}.
* @param meterRegistrySupplier must not be {@literal null}.
* @param configurer must not be {@literal null}.
*/
public ModuleEventListener(ApplicationModulesRuntime runtime,
Supplier<ObservationRegistry> observationRegistrySupplier, Supplier<MeterRegistry> meterRegistrySupplier) {
Supplier<ObservationRegistry> observationRegistrySupplier, Supplier<MeterRegistry> 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<ApplicationEvent
if (registry != null) {
Counter.builder(ModulithMetrics.EVENTS.getName()) //
.tags(ModulithMetrics.LowKeys.EVENT_TYPE.name().toLowerCase(), event.getClass().getSimpleName()) //
.tags(ModulithMetrics.LowKeys.EVENT_TYPE.name().toLowerCase(), payloadType.getSimpleName()) //
.tags(ModulithMetrics.LowKeys.MODULE_NAME.name().toLowerCase(), moduleByType.getDisplayName()) //
.register(registry).increment();
factory.createCounterBuilder(event).register(registry).increment();
}
var observation = observationRegistry.get().getCurrentObservation();

View File

@@ -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.ObservationRegistry;

View File

@@ -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.util.function.Consumer;

View File

@@ -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.ContextView;

View File

@@ -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.Context;

View File

@@ -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.common.docs.KeyName;
import io.micrometer.core.instrument.Meter;

View File

@@ -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.Context;
import io.micrometer.observation.ObservationConvention;

View File

@@ -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.common.docs.KeyName;
import io.micrometer.observation.Observation;
@@ -133,7 +133,7 @@ enum ModulithObservations implements ObservationDocumentation {
*/
@Override
public String getContextualName() {
return getName();
return "event.publication.success";
}
},
@@ -157,7 +157,7 @@ enum ModulithObservations implements ObservationDocumentation {
*/
@Override
public String getContextualName() {
return getName();
return "event.publication.failure";
}
}
}

View File

@@ -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;

View File

@@ -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.lang.reflect.Modifier;

View File

@@ -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.ObservationRegistry;

View File

@@ -0,0 +1,5 @@
/**
* Support for application module observability.
*/
@org.springframework.lang.NonNullApi
package org.springframework.modulith.observability.support;

View File

@@ -22,7 +22,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.actuate.observability.AutoConfigureObservability;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.modulith.observability.ModuleEventListener;
import org.springframework.modulith.observability.support.ModuleEventListener;
/**
* @author Oliver Drotbohm

View File

@@ -0,0 +1,80 @@
/*
* 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 static org.assertj.core.api.Assertions.*;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.Meter.Id;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import java.util.function.Consumer;
import org.junit.jupiter.api.Test;
import org.springframework.modulith.observability.support.CrossModuleEventCounterFactory;
/**
* Unit tests for {@link CrossModuleEventCounterFactory}.
*
* @author Oliver Drotbohm
*/
class CrossModuleEventCounterFactoryUnitTests {
MeterRegistry registry = new SimpleMeterRegistry();
CrossModuleEventCounterFactory factory = new CrossModuleEventCounterFactory();
@Test // GH-1068
void appliesCustomization() {
factory.customize(SampleEvent.class, (__, builder) -> 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<Id> assertions) {
assertions.accept(factory.createCounterBuilder(event).register(registry).getId());
}
static class SampleEvent {}
static class OtherSampleEvent {}
}

View File

@@ -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;
/**

View File

@@ -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;

View File

@@ -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;
/**

View File

@@ -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;