GH-928 - Move to observations API.
Reworked observability integration to create Observations rather than traces directly. Additional metrics and counters and an Observation.Context to potentially customize the metrics exposed.
This commit is contained in:
committed by
Oliver Drotbohm
parent
163673ad75
commit
738ad3f14b
10
pom.xml
10
pom.xml
@@ -48,6 +48,8 @@
|
||||
<spring-cloud-aws-bom.version>3.1.1</spring-cloud-aws-bom.version>
|
||||
<testcontainers.version>1.17.6</testcontainers.version>
|
||||
<structurizr.version>3.1.0</structurizr.version>
|
||||
<!-- TODO: For snapshots -->
|
||||
<micrometer-tracing.version>1.5.0-SNAPSHOT</micrometer-tracing.version>
|
||||
|
||||
</properties>
|
||||
|
||||
@@ -97,6 +99,14 @@ limitations under the License.
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<!-- TODO: For snapshots -->
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-tracing-bom</artifactId>
|
||||
<version>${micrometer-tracing.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-dependencies</artifactId>
|
||||
|
||||
@@ -13,7 +13,11 @@
|
||||
|
||||
<properties>
|
||||
<module.name>org.springframework.modulith.observability</module.name>
|
||||
<spring-cloud.version>2021.0.0</spring-cloud.version>
|
||||
|
||||
<micrometer-docs-generator.version>1.0.4</micrometer-docs-generator.version>
|
||||
<micrometer-docs-generator.inputPath>${project.basedir}/src/main/java/</micrometer-docs-generator.inputPath>
|
||||
<micrometer-docs-generator.inclusionPattern>.*</micrometer-docs-generator.inclusionPattern>
|
||||
<micrometer-docs-generator.outputPath>${project.build.directory}/observation-documentation/</micrometer-docs-generator.outputPath>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@@ -73,12 +77,24 @@
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-core</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.zipkin.brave</groupId>
|
||||
<artifactId>brave</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.opentelemetry</groupId>
|
||||
<artifactId>opentelemetry-api</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Testing -->
|
||||
|
||||
<dependency>
|
||||
@@ -107,4 +123,39 @@
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>generate-docs</id>
|
||||
<phase>prepare-package</phase>
|
||||
<goals>
|
||||
<goal>java</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<mainClass>io.micrometer.docs.DocsGeneratorCommand</mainClass>
|
||||
<includePluginDependencies>true</includePluginDependencies>
|
||||
<arguments>
|
||||
<argument>${micrometer-docs-generator.inputPath}</argument>
|
||||
<argument>${micrometer-docs-generator.inclusionPattern}</argument>
|
||||
<argument>${micrometer-docs-generator.outputPath}</argument>
|
||||
</arguments>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-docs-generator</artifactId>
|
||||
<version>${micrometer-docs-generator.version}</version>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package org.springframework.modulith.observability;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import io.micrometer.common.KeyValues;
|
||||
|
||||
import org.springframework.modulith.observability.ModulithObservations.HighKeys;
|
||||
import org.springframework.modulith.observability.ModulithObservations.LowKeys;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link ModulithObservationConvention}.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.4
|
||||
*/
|
||||
public class DefaultModulithObservationConvention implements ModulithObservationConvention {
|
||||
|
||||
@Override
|
||||
public KeyValues getLowCardinalityKeyValues(ModulithContext context) {
|
||||
KeyValues keyValues = KeyValues.of(LowKeys.MODULE_KEY.withValue(context.getModule().getIdentifier().toString()));
|
||||
if (isEventListener(context)) {
|
||||
return keyValues.and(LowKeys.INVOCATION_TYPE.withValue("event-listener"));
|
||||
}
|
||||
return keyValues;
|
||||
}
|
||||
|
||||
private boolean isEventListener(ModulithContext context) {
|
||||
try {
|
||||
return context.getModule().isEventListenerInvocation(context.getInvocation());
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyValues getHighCardinalityKeyValues(ModulithContext context) {
|
||||
Method method = context.getInvocation().getMethod();
|
||||
return KeyValues.of(HighKeys.MODULE_METHOD.withValue(method.getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "module.requests";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContextualName(ModulithContext context) {
|
||||
return "[" + context.getApplicationName() + "] " + context.getModule().getDisplayName();
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.modulith.core.ApplicationModule;
|
||||
import org.springframework.modulith.core.ApplicationModuleIdentifier;
|
||||
import org.springframework.modulith.core.ApplicationModules;
|
||||
import org.springframework.modulith.core.ArchitecturallyEvidentType;
|
||||
import org.springframework.modulith.core.ArchitecturallyEvidentType.ReferenceMethod;
|
||||
import org.springframework.modulith.core.FormattableType;
|
||||
import org.springframework.modulith.core.SpringBean;
|
||||
|
||||
@@ -15,47 +15,79 @@
|
||||
*/
|
||||
package org.springframework.modulith.observability;
|
||||
|
||||
import io.micrometer.tracing.BaggageInScope;
|
||||
import io.micrometer.tracing.Tracer;
|
||||
import io.micrometer.tracing.Tracer.SpanInScope;
|
||||
import io.micrometer.common.KeyValue;
|
||||
import io.micrometer.observation.Observation;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.slf4j.Logger;
|
||||
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.util.Assert;
|
||||
|
||||
class ModuleEntryInterceptor implements MethodInterceptor {
|
||||
|
||||
private static Logger LOGGER = LoggerFactory.getLogger(ModuleEntryInterceptor.class);
|
||||
private static Map<String, ModuleEntryInterceptor> CACHE = new HashMap<>();
|
||||
private static final String MODULE_KEY = ModuleTracingBeanPostProcessor.MODULE_BAGGAGE_KEY;
|
||||
private static Map<ApplicationModuleIdentifier, ModuleEntryInterceptor> CACHE = new HashMap<>();
|
||||
|
||||
private static final ModulithObservationConvention DEFAULT = new DefaultModulithObservationConvention();
|
||||
|
||||
private final ObservedModule module;
|
||||
private final Tracer tracer;
|
||||
private final ObservationRegistry observationRegistry;
|
||||
@Nullable private final ModulithObservationConvention customModulithObservationConvention;
|
||||
private final Environment environment;
|
||||
|
||||
/**
|
||||
* Creates a new {@link ModuleEntryInterceptor} for the given {@link ObservedModule} and {@link Tracer}.
|
||||
* Creates a new {@link ModuleEntryInterceptor} for the given {@link ObservedModule} and {@link ObservationRegistry}.
|
||||
*
|
||||
* @param module must not be {@literal null}.
|
||||
* @param tracer must not be {@literal null}.
|
||||
* @param observationRegistry must not be {@literal null}.
|
||||
* @param environment must not be {@literal null}.
|
||||
*/
|
||||
private ModuleEntryInterceptor(ObservedModule module, Tracer tracer) {
|
||||
|
||||
Assert.notNull(module, "ObservedModule must not be null!");
|
||||
Assert.notNull(tracer, "Tracer must not be null!");
|
||||
|
||||
this.module = module;
|
||||
this.tracer = tracer;
|
||||
private ModuleEntryInterceptor(ObservedModule module, ObservationRegistry observationRegistry,
|
||||
Environment environment) {
|
||||
this(module, observationRegistry, null, environment);
|
||||
}
|
||||
|
||||
public static ModuleEntryInterceptor of(ObservedModule module, Tracer tracer) {
|
||||
/**
|
||||
* Creates a new {@link ModuleEntryInterceptor} for the given {@link ObservedModule}, {@link ObservationRegistry} and
|
||||
* {@link ModulithObservationConvention}.
|
||||
*
|
||||
* @param module must not be {@literal null}.
|
||||
* @param observationRegistry must not be {@literal null}.
|
||||
* @param custom must not be {@literal null}.
|
||||
* @param environment must not be {@literal null}.
|
||||
*/
|
||||
private ModuleEntryInterceptor(ObservedModule module, ObservationRegistry observationRegistry,
|
||||
ModulithObservationConvention custom, Environment environment) {
|
||||
|
||||
return CACHE.computeIfAbsent(module.getName(), __ -> {
|
||||
return new ModuleEntryInterceptor(module, tracer);
|
||||
Assert.notNull(module, "ObservedModule must not be null!");
|
||||
Assert.notNull(observationRegistry, "ObservationRegistry must not be null!");
|
||||
|
||||
this.module = module;
|
||||
this.observationRegistry = observationRegistry;
|
||||
this.customModulithObservationConvention = custom;
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
public static ModuleEntryInterceptor of(ObservedModule module, ObservationRegistry observationRegistry,
|
||||
Environment environment) {
|
||||
return of(module, observationRegistry, null, environment);
|
||||
}
|
||||
|
||||
public static ModuleEntryInterceptor of(ObservedModule module, ObservationRegistry observationRegistry,
|
||||
ModulithObservationConvention custom, Environment environment) {
|
||||
|
||||
return CACHE.computeIfAbsent(module.getIdentifier(), __ -> {
|
||||
return new ModuleEntryInterceptor(module, observationRegistry, custom, environment);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -66,12 +98,17 @@ class ModuleEntryInterceptor implements MethodInterceptor {
|
||||
@Override
|
||||
public Object invoke(MethodInvocation invocation) throws Throwable {
|
||||
|
||||
var moduleName = module.getName();
|
||||
var currentSpan = tracer.currentSpan();
|
||||
var currentBaggage = tracer.getBaggage(MODULE_KEY);
|
||||
var currentModule = currentBaggage != null ? currentBaggage.get() : null;
|
||||
var moduleIdentifier = module.getIdentifier();
|
||||
var currentObservation = observationRegistry.getCurrentObservation();
|
||||
String currentModule = null;
|
||||
|
||||
if (currentSpan != null && moduleName.equals(currentModule)) {
|
||||
if (currentObservation != null) {
|
||||
KeyValue moduleKey = currentObservation.getContextView().getLowCardinalityKeyValue(LowKeys.MODULE_KEY.asString());
|
||||
currentModule = moduleKey != null ? moduleKey.getValue() : null;
|
||||
}
|
||||
|
||||
if (currentObservation != null && Objects.equals(moduleIdentifier.toString(), currentModule)) {
|
||||
// Same module
|
||||
return invocation.proceed();
|
||||
}
|
||||
|
||||
@@ -79,21 +116,20 @@ class ModuleEntryInterceptor implements MethodInterceptor {
|
||||
|
||||
LOGGER.trace("Entering {} via {}.", module.getDisplayName(), invokedMethod);
|
||||
|
||||
var span = tracer.spanBuilder()
|
||||
.name(moduleName)
|
||||
.tag("module.method", invokedMethod)
|
||||
.tag(MODULE_KEY, moduleName)
|
||||
.start();
|
||||
|
||||
try (SpanInScope ws = tracer.withSpan(span);
|
||||
BaggageInScope baggage = tracer.createBaggageInScope(MODULE_KEY, moduleName)) {
|
||||
|
||||
return invocation.proceed();
|
||||
|
||||
ModulithContext modulithContext = new ModulithContext(module, invocation, environment);
|
||||
var observation = Observation.createNotStarted(customModulithObservationConvention, DEFAULT,
|
||||
() -> modulithContext, observationRegistry);
|
||||
try (Observation.Scope scope = observation.start().openScope()) {
|
||||
Object proceed = invocation.proceed();
|
||||
observation.event(ModulithObservations.Events.EVENT_PUBLICATION_SUCCESS);
|
||||
return proceed;
|
||||
} catch (Exception ex) {
|
||||
observation.error(ex);
|
||||
observation.event(ModulithObservations.Events.EVENT_PUBLICATION_FAILURE);
|
||||
throw ex;
|
||||
} finally {
|
||||
|
||||
LOGGER.trace("Leaving {}", module.getDisplayName());
|
||||
span.end();
|
||||
observation.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,13 +15,17 @@
|
||||
*/
|
||||
package org.springframework.modulith.observability;
|
||||
|
||||
import io.micrometer.tracing.Tracer;
|
||||
import io.micrometer.core.instrument.Counter;
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
import io.micrometer.observation.Observation.Event;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
|
||||
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.runtime.ApplicationModulesRuntime;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -31,21 +35,25 @@ import org.springframework.util.Assert;
|
||||
public class ModuleEventListener implements ApplicationListener<ApplicationEvent> {
|
||||
|
||||
private final ApplicationModulesRuntime runtime;
|
||||
private final Supplier<Tracer> tracer;
|
||||
private final Supplier<ObservationRegistry> observationRegistry;
|
||||
private final Supplier<MeterRegistry> meterRegistry;
|
||||
|
||||
/**
|
||||
* Creates a new {@link ModuleEventListener} for the given {@link ApplicationModulesRuntime} and {@link Tracer}.
|
||||
* Creates a new {@link ModuleEventListener} for the given {@link ApplicationModulesRuntime} and {@link ObservationRegistry} and {@link MeterRegistry}.
|
||||
*
|
||||
* @param runtime must not be {@literal null}.
|
||||
* @param tracer must not be {@literal null}.
|
||||
* @param observationRegistrySupplier must not be {@literal null}.
|
||||
*/
|
||||
public ModuleEventListener(ApplicationModulesRuntime runtime, Supplier<Tracer> tracer) {
|
||||
public ModuleEventListener(ApplicationModulesRuntime runtime, Supplier<ObservationRegistry> observationRegistrySupplier,
|
||||
Supplier<MeterRegistry> meterRegistrySupplier) {
|
||||
|
||||
Assert.notNull(runtime, "ApplicationModulesRuntime must not be null!");
|
||||
Assert.notNull(tracer, "Tracer must not be null!");
|
||||
Assert.notNull(observationRegistrySupplier, "ObservationRegistry must not be null!");
|
||||
Assert.notNull(meterRegistrySupplier, "MeterRegistry must not be null!");
|
||||
|
||||
this.runtime = runtime;
|
||||
this.tracer = tracer;
|
||||
this.observationRegistry = observationRegistrySupplier;
|
||||
this.meterRegistry = meterRegistrySupplier;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -74,12 +82,20 @@ public class ModuleEventListener implements ApplicationListener<ApplicationEvent
|
||||
return;
|
||||
}
|
||||
|
||||
var span = tracer.get().currentSpan();
|
||||
MeterRegistry registry = meterRegistry.get();
|
||||
if (registry != null) {
|
||||
Counter.builder(ModulithMetrics.EVENTS.getName()) //
|
||||
.tags(ModulithMetrics.LowKeys.EVENT_TYPE.name().toLowerCase(), event.getClass().getSimpleName()) //
|
||||
.tags(ModulithMetrics.LowKeys.MODULE_NAME.name().toLowerCase(), moduleByType.getDisplayName()) //
|
||||
.register(registry).increment();
|
||||
}
|
||||
|
||||
if (span == null) {
|
||||
var observation = observationRegistry.get().getCurrentObservation();
|
||||
|
||||
if (observation == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
span.event("Published " + payloadType.getName());
|
||||
observation.event(Event.of(Events.EVENT_PUBLICATION_SUCCESS.getName(), "Published " + payloadType.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package org.springframework.modulith.observability;
|
||||
|
||||
import io.micrometer.tracing.Tracer;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
@@ -34,6 +34,7 @@ import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.modulith.runtime.ApplicationModulesRuntime;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -43,32 +44,40 @@ import org.springframework.util.Assert;
|
||||
*
|
||||
* @author Oliver Drotbohm
|
||||
*/
|
||||
public class ModuleTracingBeanPostProcessor extends ModuleTracingSupport implements BeanPostProcessor, Ordered {
|
||||
public class ModuleObservabilityBeanPostProcessor extends ModuleObservabilitySupport
|
||||
implements BeanPostProcessor, Ordered {
|
||||
|
||||
public static final String MODULE_BAGGAGE_KEY = "org.springframework.modulith.module";
|
||||
|
||||
private final ApplicationModulesRuntime runtime;
|
||||
private final Supplier<Tracer> tracer;
|
||||
private final Supplier<ObservationRegistry> observationRegistry;
|
||||
private final Map<String, Advisor> advisors;
|
||||
private final ConfigurableListableBeanFactory factory;
|
||||
private final Environment environment;
|
||||
|
||||
/**
|
||||
* Creates a new {@link ModuleTracingBeanPostProcessor} for the given {@link ApplicationModulesRuntime} and
|
||||
* {@link Tracer}.
|
||||
* Creates a new {@link ModuleObservabilityBeanPostProcessor} for the given {@link ApplicationModulesRuntime} and
|
||||
* {@link ObservationRegistry}.
|
||||
*
|
||||
* @param runtime must not be {@literal null}.
|
||||
* @param tracer must not be {@literal null}.
|
||||
* @param observationRegistry must not be {@literal null}.
|
||||
* @param factory must not be {@literal null}.
|
||||
* @param environment must not be {@literal null}.
|
||||
*/
|
||||
public ModuleTracingBeanPostProcessor(ApplicationModulesRuntime runtime, Supplier<Tracer> tracer,
|
||||
ConfigurableListableBeanFactory factory) {
|
||||
public ModuleObservabilityBeanPostProcessor(ApplicationModulesRuntime runtime,
|
||||
Supplier<ObservationRegistry> observationRegistry, ConfigurableListableBeanFactory factory,
|
||||
Environment environment) {
|
||||
|
||||
Assert.notNull(runtime, "ApplicationModulesRuntime must not be null!");
|
||||
Assert.notNull(tracer, "Tracer must not be null!");
|
||||
Assert.notNull(observationRegistry, "ObservationRegistry must not be null!");
|
||||
Assert.notNull(factory, "BeanFactory must not be null!");
|
||||
Assert.notNull(environment, "Environment must not be null!");
|
||||
|
||||
this.runtime = runtime;
|
||||
this.tracer = tracer;
|
||||
this.observationRegistry = observationRegistry;
|
||||
this.advisors = new HashMap<>();
|
||||
this.factory = factory;
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -99,15 +108,17 @@ public class ModuleTracingBeanPostProcessor extends ModuleTracingSupport impleme
|
||||
|
||||
var modules = runtime.get();
|
||||
|
||||
return modules.getModuleByType(type.getName()).map(DefaultObservedModule::new).map(it -> {
|
||||
return modules.getModuleByType(type.getName())
|
||||
.map(DefaultObservedModule::new)
|
||||
.map(it -> {
|
||||
|
||||
var moduleType = it.getObservedModuleType(type, modules);
|
||||
var moduleType = it.getObservedModuleType(type, modules);
|
||||
|
||||
return moduleType != null //
|
||||
? addAdvisor(bean, getOrBuildAdvisor(it, moduleType)) //
|
||||
: bean;
|
||||
return moduleType != null //
|
||||
? addAdvisor(bean, getOrBuildAdvisor(it, moduleType)) //
|
||||
: bean;
|
||||
|
||||
}).orElse(bean);
|
||||
}).orElse(bean);
|
||||
}
|
||||
|
||||
private boolean isInfrastructureBean(String beanName) {
|
||||
@@ -129,8 +140,9 @@ public class ModuleTracingBeanPostProcessor extends ModuleTracingSupport impleme
|
||||
|
||||
private Advisor getOrBuildAdvisor(ObservedModule module, ObservedModuleType type) {
|
||||
|
||||
return advisors.computeIfAbsent(module.getName(), __ -> {
|
||||
return new ApplicationModuleObservingAdvisor(type, ModuleEntryInterceptor.of(module, tracer.get()));
|
||||
return advisors.computeIfAbsent(module.getIdentifier().toString(), __ -> {
|
||||
return new ApplicationModuleObservingAdvisor(type,
|
||||
ModuleEntryInterceptor.of(module, observationRegistry.get(), environment));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ import org.springframework.scheduling.annotation.AsyncAnnotationAdvisor;
|
||||
/**
|
||||
* @author Oliver Drotbohm
|
||||
*/
|
||||
class ModuleTracingSupport implements BeanClassLoaderAware, BeanFactoryAware {
|
||||
class ModuleObservabilitySupport implements BeanClassLoaderAware, BeanFactoryAware {
|
||||
|
||||
private ClassLoader classLoader;
|
||||
private @Nullable AbstractAutoProxyCreator creator;
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2022-2024 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.observation.Observation;
|
||||
import io.micrometer.observation.ObservationFilter;
|
||||
|
||||
/**
|
||||
* Ensures that {@link ModulithObservations.LowKeys#MODULE_KEY} gets propagated from parent
|
||||
* to child.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.4
|
||||
*/
|
||||
public class ModulePassingObservationFilter implements ObservationFilter {
|
||||
|
||||
@Override
|
||||
public Observation.Context map(Observation.Context context) {
|
||||
if (isModuleKeyValueAbsentInCurrent(context) && isModuleKeyValuePresentInParent(context)) {
|
||||
return context.addLowCardinalityKeyValue(ModulithObservations.LowKeys.MODULE_KEY.withValue(context.getParentObservation().getContextView().getLowCardinalityKeyValue(ModulithObservations.LowKeys.MODULE_KEY.asString()).getValue()));
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
private static boolean isModuleKeyValueAbsentInCurrent(Observation.ContextView context) {
|
||||
return context.getLowCardinalityKeyValue(ModulithObservations.LowKeys.MODULE_KEY.asString()) == null;
|
||||
}
|
||||
|
||||
private static boolean isModuleKeyValuePresentInParent(Observation.ContextView context) {
|
||||
return context.getParentObservation() != null && context.getParentObservation().getContextView().getLowCardinalityKeyValue(ModulithObservations.LowKeys.MODULE_KEY.asString()) != null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package org.springframework.modulith.observability;
|
||||
|
||||
import io.micrometer.observation.Observation;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
/**
|
||||
* A {@link Observation.Context} for Modulithic applications.
|
||||
*
|
||||
* @author Marcin Grzejsczak
|
||||
* @since 1.4
|
||||
*/
|
||||
public class ModulithContext extends Observation.Context {
|
||||
|
||||
private final ObservedModule module;
|
||||
|
||||
private final MethodInvocation invocation;
|
||||
|
||||
private final String applicationName;
|
||||
|
||||
public ModulithContext(ObservedModule module, MethodInvocation invocation, Environment environment) {
|
||||
this.module = module;
|
||||
this.invocation = invocation;
|
||||
this.applicationName = environment.getProperty("spring.application.name");
|
||||
}
|
||||
|
||||
public ObservedModule getModule() {
|
||||
return module;
|
||||
}
|
||||
|
||||
public MethodInvocation getInvocation() {
|
||||
return invocation;
|
||||
}
|
||||
|
||||
public String getApplicationName() {
|
||||
return applicationName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package org.springframework.modulith.observability;
|
||||
|
||||
import io.micrometer.common.docs.KeyName;
|
||||
import io.micrometer.core.instrument.Meter;
|
||||
import io.micrometer.core.instrument.docs.MeterDocumentation;
|
||||
|
||||
enum ModulithMetrics implements MeterDocumentation {
|
||||
|
||||
/**
|
||||
* Counter for the events.
|
||||
*/
|
||||
EVENTS {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "modulith.events.processed";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Meter.Type getType() {
|
||||
return Meter.Type.COUNTER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyName[] getKeyNames() {
|
||||
return LowKeys.values();
|
||||
}
|
||||
};
|
||||
|
||||
enum LowKeys implements KeyName {
|
||||
/**
|
||||
* Type of the emitted event.
|
||||
*/
|
||||
EVENT_TYPE {
|
||||
@Override
|
||||
public String asString() {
|
||||
return "event.type";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Name of the module.
|
||||
*/
|
||||
MODULE_NAME {
|
||||
@Override
|
||||
public String asString() {
|
||||
return "module.name";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package org.springframework.modulith.observability;
|
||||
|
||||
import io.micrometer.observation.Observation.Context;
|
||||
import io.micrometer.observation.ObservationConvention;
|
||||
|
||||
/**
|
||||
* {@link ObservationConvention} for {@link ModulithContext}.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.4
|
||||
*/
|
||||
public interface ModulithObservationConvention extends ObservationConvention<ModulithContext> {
|
||||
|
||||
@Override
|
||||
default boolean supportsContext(Context context) {
|
||||
return context instanceof ModulithContext;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package org.springframework.modulith.observability;
|
||||
|
||||
import io.micrometer.common.docs.KeyName;
|
||||
import io.micrometer.observation.Observation;
|
||||
import io.micrometer.observation.Observation.Context;
|
||||
import io.micrometer.observation.ObservationConvention;
|
||||
import io.micrometer.observation.docs.ObservationDocumentation;
|
||||
|
||||
enum ModulithObservations implements ObservationDocumentation {
|
||||
|
||||
/**
|
||||
* Observation related to entering a module.
|
||||
*/
|
||||
MODULE_ENTRY {
|
||||
@Override public Class<? extends ObservationConvention<? extends Context>> getDefaultConvention() {
|
||||
return DefaultModulithObservationConvention.class;
|
||||
}
|
||||
|
||||
@Override public KeyName[] getLowCardinalityKeyNames() {
|
||||
return LowKeys.values();
|
||||
}
|
||||
|
||||
@Override public KeyName[] getHighCardinalityKeyNames() {
|
||||
return HighKeys.values();
|
||||
}
|
||||
|
||||
@Override public Observation.Event[] getEvents() {
|
||||
return Events.values();
|
||||
}
|
||||
};
|
||||
|
||||
enum LowKeys implements KeyName {
|
||||
|
||||
/**
|
||||
* Name of the module.
|
||||
*/
|
||||
MODULE_KEY {
|
||||
@Override public String asString() {
|
||||
return "module.key";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Type of invocation (e.g. event listener).
|
||||
*/
|
||||
INVOCATION_TYPE {
|
||||
@Override public String asString() {
|
||||
return "module.invocation-type";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum HighKeys implements KeyName {
|
||||
/**
|
||||
* Method executed on a module.
|
||||
*/
|
||||
MODULE_METHOD {
|
||||
@Override public String asString() {
|
||||
return "module.method";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum Events implements Observation.Event {
|
||||
|
||||
/**
|
||||
* Published when an event is sent successfully.
|
||||
*/
|
||||
EVENT_PUBLICATION_SUCCESS {
|
||||
@Override public String getName() {
|
||||
return "event.publication.success";
|
||||
}
|
||||
|
||||
@Override public String getContextualName() {
|
||||
return getName();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Published when an event is sent with a failure.
|
||||
*/
|
||||
EVENT_PUBLICATION_FAILURE {
|
||||
@Override public String getName() {
|
||||
return "event.publication.failure";
|
||||
}
|
||||
|
||||
@Override public String getContextualName() {
|
||||
return getName();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,11 +24,15 @@ import org.springframework.modulith.core.ApplicationModuleIdentifier;
|
||||
import org.springframework.modulith.core.ApplicationModules;
|
||||
|
||||
import com.tngtech.archunit.core.domain.JavaClass;
|
||||
import org.springframework.modulith.core.ArchitecturallyEvidentType;
|
||||
|
||||
/**
|
||||
* Information about observed module.
|
||||
*
|
||||
* @author Oliver Drotbohm
|
||||
* @since 1.4
|
||||
*/
|
||||
interface ObservedModule {
|
||||
public interface ObservedModule {
|
||||
|
||||
/**
|
||||
* Returns the name of the application module.
|
||||
@@ -86,7 +90,7 @@ interface ObservedModule {
|
||||
* method invocation on a Spring bean.
|
||||
*
|
||||
* @param invocation must not be {@literal null}.
|
||||
* @since 1.3
|
||||
* @since 1.4
|
||||
*/
|
||||
boolean isEventListenerInvocation(MethodInvocation invocation);
|
||||
}
|
||||
|
||||
@@ -37,8 +37,9 @@ import com.tngtech.archunit.core.domain.JavaClass;
|
||||
* Represents a type in an {@link ObservedModule}.
|
||||
*
|
||||
* @author Oliver Drotbohm
|
||||
* @since 1.4
|
||||
*/
|
||||
class ObservedModuleType {
|
||||
public class ObservedModuleType {
|
||||
|
||||
private static final Collection<Class<?>> IGNORED_TYPES = List.of(Advised.class, TargetClassAware.class);
|
||||
private static final Predicate<Method> IS_USER_METHOD = it -> !Modifier.isPrivate(it.getModifiers())
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package org.springframework.modulith.observability;
|
||||
|
||||
import io.micrometer.tracing.Tracer;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.springframework.aop.support.DefaultPointcutAdvisor;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.data.rest.webmvc.BasePathAwareController;
|
||||
import org.springframework.data.rest.webmvc.RootResourceInformation;
|
||||
import org.springframework.modulith.core.ApplicationModule;
|
||||
@@ -37,25 +38,28 @@ import org.springframework.util.Assert;
|
||||
/**
|
||||
* @author Oliver Drotbohm
|
||||
*/
|
||||
public class SpringDataRestModuleTracingBeanPostProcessor extends ModuleTracingSupport implements BeanPostProcessor {
|
||||
public class SpringDataRestModuleObservabilityBeanPostProcessor extends ModuleObservabilitySupport implements BeanPostProcessor {
|
||||
|
||||
private final ApplicationModulesRuntime runtime;
|
||||
private final Supplier<Tracer> tracer;
|
||||
private final Supplier<ObservationRegistry> observationRegistry;
|
||||
private final Environment environment;
|
||||
|
||||
/**
|
||||
* Creates a new {@link SpringDataRestModuleTracingBeanPostProcessor} for the given {@link ApplicationModulesRuntime}
|
||||
* and {@link Tracer}.
|
||||
* Creates a new {@link SpringDataRestModuleObservabilityBeanPostProcessor} for the given {@link ApplicationModulesRuntime}
|
||||
* and {@link ObservationRegistry}.
|
||||
*
|
||||
* @param runtime must not be {@literal null}.
|
||||
* @param tracer must not be {@literal null}.
|
||||
* @param observationRegistry must not be {@literal null}.
|
||||
* @param environment must not be {@literal null}.
|
||||
*/
|
||||
public SpringDataRestModuleTracingBeanPostProcessor(ApplicationModulesRuntime runtime, Supplier<Tracer> tracer) {
|
||||
public SpringDataRestModuleObservabilityBeanPostProcessor(ApplicationModulesRuntime runtime, Supplier<ObservationRegistry> observationRegistry, Environment environment) {
|
||||
|
||||
Assert.notNull(runtime, "ApplicationModulesRuntime must not be null!");
|
||||
Assert.notNull(tracer, "Tracer must not be null!");
|
||||
Assert.notNull(observationRegistry, "ObservationRegistry must not be null!");
|
||||
|
||||
this.runtime = runtime;
|
||||
this.tracer = tracer;
|
||||
this.observationRegistry = observationRegistry;
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -71,7 +75,7 @@ public class SpringDataRestModuleTracingBeanPostProcessor extends ModuleTracingS
|
||||
return bean;
|
||||
}
|
||||
|
||||
Advice interceptor = new DataRestControllerInterceptor(runtime, tracer);
|
||||
Advice interceptor = new DataRestControllerInterceptor(runtime, observationRegistry, environment);
|
||||
Advisor advisor = new DefaultPointcutAdvisor(interceptor);
|
||||
|
||||
return addAdvisor(bean, advisor, it -> it.setProxyTargetClass(true));
|
||||
@@ -80,21 +84,26 @@ public class SpringDataRestModuleTracingBeanPostProcessor extends ModuleTracingS
|
||||
private static class DataRestControllerInterceptor implements MethodInterceptor {
|
||||
|
||||
private final Supplier<ApplicationModules> modules;
|
||||
private final Supplier<Tracer> tracer;
|
||||
private final Supplier<ObservationRegistry> observationRegistry;
|
||||
private final Environment environment;
|
||||
|
||||
/**
|
||||
* Creates a new {@link DataRestControllerInterceptor} for the given {@link ApplicationModules} and {@link Tracer}.
|
||||
*
|
||||
* @param modules must not be {@literal null}.
|
||||
* @param tracer must not be {@literal null}.
|
||||
* @param modules must not be {@literal null}.
|
||||
* @param observationRegistry must not be {@literal null}.
|
||||
* @param environment must not be {@literal null}.
|
||||
*/
|
||||
private DataRestControllerInterceptor(Supplier<ApplicationModules> modules, Supplier<Tracer> tracer) {
|
||||
private DataRestControllerInterceptor(Supplier<ApplicationModules> modules, Supplier<ObservationRegistry> observationRegistry,
|
||||
Environment environment) {
|
||||
|
||||
Assert.notNull(modules, "ApplicationModules must not be null!");
|
||||
Assert.notNull(tracer, "Tracer must not be null!");
|
||||
Assert.notNull(observationRegistry, "ObservationRegistry must not be null!");
|
||||
Assert.notNull(environment, "Environment must not be null!");
|
||||
|
||||
this.modules = modules;
|
||||
this.tracer = tracer;
|
||||
this.observationRegistry = observationRegistry;
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -112,7 +121,7 @@ public class SpringDataRestModuleTracingBeanPostProcessor extends ModuleTracingS
|
||||
|
||||
var observed = new DefaultObservedModule(module);
|
||||
|
||||
return ModuleEntryInterceptor.of(observed, tracer.get()).invoke(invocation);
|
||||
return ModuleEntryInterceptor.of(observed, observationRegistry.get(), environment).invoke(invocation);
|
||||
}
|
||||
|
||||
private ApplicationModule getModuleFrom(Object[] arguments) {
|
||||
@@ -15,14 +15,12 @@
|
||||
*/
|
||||
package org.springframework.modulith.observability.autoconfigure;
|
||||
|
||||
import brave.TracingCustomizer;
|
||||
import brave.baggage.BaggageField;
|
||||
import brave.baggage.BaggagePropagationConfig;
|
||||
import brave.baggage.BaggagePropagationCustomizer;
|
||||
import brave.handler.MutableSpan;
|
||||
import brave.handler.SpanHandler;
|
||||
import brave.propagation.TraceContext;
|
||||
import io.micrometer.tracing.Tracer;
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
import io.micrometer.observation.ObservationFilter;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
@@ -34,9 +32,11 @@ import org.springframework.boot.task.SimpleAsyncTaskExecutorCustomizer;
|
||||
import org.springframework.boot.task.ThreadPoolTaskExecutorCustomizer;
|
||||
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.ModuleEventListener;
|
||||
import org.springframework.modulith.observability.ModuleTracingBeanPostProcessor;
|
||||
import org.springframework.modulith.observability.ModuleObservabilityBeanPostProcessor;
|
||||
import org.springframework.modulith.observability.ModulePassingObservationFilter;
|
||||
import org.springframework.modulith.runtime.ApplicationModulesRuntime;
|
||||
|
||||
/**
|
||||
@@ -47,17 +47,18 @@ import org.springframework.modulith.runtime.ApplicationModulesRuntime;
|
||||
class ModuleObservabilityAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
static ModuleTracingBeanPostProcessor moduleTracingBeanPostProcessor(ApplicationModulesRuntime runtime,
|
||||
ObjectProvider<Tracer> tracer, ConfigurableListableBeanFactory factory) {
|
||||
return new ModuleTracingBeanPostProcessor(runtime, () -> tracer.getObject(), factory);
|
||||
static ModuleObservabilityBeanPostProcessor moduleTracingBeanPostProcessor(ApplicationModulesRuntime runtime,
|
||||
ObjectProvider<ObservationRegistry> observationRegistry, ConfigurableListableBeanFactory factory, Environment environment) {
|
||||
return new ModuleObservabilityBeanPostProcessor(runtime, observationRegistry::getObject, factory, environment);
|
||||
}
|
||||
|
||||
@Bean
|
||||
static ModuleEventListener tracingModuleEventListener(ApplicationModulesRuntime runtime,
|
||||
ObjectProvider<Tracer> tracer) {
|
||||
return new ModuleEventListener(runtime, () -> tracer.getObject());
|
||||
ObjectProvider<ObservationRegistry> observationRegistry, ObjectProvider<MeterRegistry> meterRegistry) {
|
||||
return new ModuleEventListener(runtime, observationRegistry::getObject, meterRegistry::getObject);
|
||||
}
|
||||
|
||||
// TODO: Have a custom thread pool for modulith
|
||||
@Bean
|
||||
@ConditionalOnThreading(Threading.VIRTUAL)
|
||||
SimpleAsyncTaskExecutorCustomizer simpleAsyncTaskExecutorCustomizer() {
|
||||
@@ -70,51 +71,31 @@ class ModuleObservabilityAutoConfiguration {
|
||||
return executor -> executor.setTaskDecorator(new ContextPropagatingTaskDecorator());
|
||||
}
|
||||
|
||||
/**
|
||||
* Brave-specific auto configuration.
|
||||
*
|
||||
* @author Oliver Drotbohm
|
||||
*/
|
||||
@ConditionalOnClass(TracingCustomizer.class)
|
||||
static class ModulithsBraveIntegrationAutoConfiguration {
|
||||
@Bean
|
||||
ObservationFilter modulePassingObservationFilter() {
|
||||
return new ModulePassingObservationFilter();
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(SpanHandler.class)
|
||||
static class BraveConfiguration {
|
||||
/**
|
||||
* Corresponds to {@link ModulithObservations.LowKeys#MODULE_KEY}
|
||||
*/
|
||||
private static final String MODULE_KEY_TAG_NAME = "module.key";
|
||||
|
||||
@Bean
|
||||
BaggagePropagationCustomizer moduleBaggagePropagationCustomizer() {
|
||||
|
||||
return builder -> builder
|
||||
.add(BaggagePropagationConfig.SingleBaggageField
|
||||
.local(BaggageField.create(ModuleTracingBeanPostProcessor.MODULE_BAGGAGE_KEY)));
|
||||
}
|
||||
|
||||
@Bean
|
||||
SpanHandler spanHandler() {
|
||||
|
||||
SpanHandler localServiceNameRenamingSpanHandler() {
|
||||
return new SpanHandler() {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see brave.handler.SpanHandler#end(brave.propagation.TraceContext, brave.handler.MutableSpan, brave.handler.SpanHandler.Cause)
|
||||
*/
|
||||
@Override
|
||||
public boolean end(TraceContext context, MutableSpan span, Cause cause) {
|
||||
|
||||
String value = span.tag(ModuleTracingBeanPostProcessor.MODULE_BAGGAGE_KEY);
|
||||
|
||||
if (value != null) {
|
||||
span.localServiceName(value);
|
||||
return true;
|
||||
@Override public boolean end(TraceContext context, MutableSpan span, Cause cause) {
|
||||
String tag = span.tag(MODULE_KEY_TAG_NAME);
|
||||
if (tag != null) {
|
||||
span.localServiceName(tag);
|
||||
}
|
||||
|
||||
BaggageField field = BaggageField.getByName(context, ModuleTracingBeanPostProcessor.MODULE_BAGGAGE_KEY);
|
||||
value = field.getValue();
|
||||
|
||||
if (value != null) {
|
||||
span.localServiceName(value);
|
||||
}
|
||||
|
||||
return true;
|
||||
return super.end(context, span, cause);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,14 +15,15 @@
|
||||
*/
|
||||
package org.springframework.modulith.observability.autoconfigure;
|
||||
|
||||
import io.micrometer.tracing.Tracer;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
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.SpringDataRestModuleTracingBeanPostProcessor;
|
||||
import org.springframework.modulith.observability.SpringDataRestModuleObservabilityBeanPostProcessor;
|
||||
import org.springframework.modulith.runtime.ApplicationModulesRuntime;
|
||||
|
||||
/**
|
||||
@@ -33,9 +34,9 @@ import org.springframework.modulith.runtime.ApplicationModulesRuntime;
|
||||
class SpringDataRestModuleObservabilityAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
static SpringDataRestModuleTracingBeanPostProcessor springDataRestModuleTracingBeanPostProcessor(
|
||||
ApplicationModulesRuntime runtime, ObjectProvider<Tracer> tracer) {
|
||||
static SpringDataRestModuleObservabilityBeanPostProcessor springDataRestModuleTracingBeanPostProcessor(
|
||||
ApplicationModulesRuntime runtime, ObjectProvider<ObservationRegistry> observationRegistry, Environment environment) {
|
||||
|
||||
return new SpringDataRestModuleTracingBeanPostProcessor(runtime, () -> tracer.getObject());
|
||||
return new SpringDataRestModuleObservabilityBeanPostProcessor(runtime, () -> observationRegistry.getObject(), environment);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,11 +16,10 @@
|
||||
package org.springframework.modulith.observability;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import example.ExampleApplication;
|
||||
import example.sample.SampleComponent;
|
||||
import io.micrometer.tracing.Tracer;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.aop.Advisor;
|
||||
@@ -29,24 +28,24 @@ 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.ModuleTracingBeanPostProcessor.ApplicationModuleObservingAdvisor;
|
||||
import org.springframework.modulith.observability.ModuleObservabilityBeanPostProcessor.ApplicationModuleObservingAdvisor;
|
||||
import org.springframework.modulith.runtime.ApplicationModulesRuntime;
|
||||
import org.springframework.modulith.runtime.ApplicationRuntime;
|
||||
import org.springframework.modulith.test.TestApplicationModules;
|
||||
import org.springframework.scheduling.annotation.AsyncAnnotationAdvisor;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link ModuleTracingBeanPostProcessor}.
|
||||
* Integration tests for {@link ModuleObservabilityBeanPostProcessor}.
|
||||
*
|
||||
* @author Oliver Drotbohm
|
||||
*/
|
||||
class ModuleTracingBeanPostProcessorIntegrationTests {
|
||||
class ModuleObservabilityBeanPostProcessorIntegrationTests {
|
||||
|
||||
@Test
|
||||
void decoratesExposedComponentsWithTracingInterceptor() {
|
||||
|
||||
SampleComponent bean = SpringApplication
|
||||
.run(new Class<?>[] { ExampleApplication.class, ModuleTracingConfiguration.class }, new String[] {})
|
||||
.run(new Class<?>[] { ExampleApplication.class, ModuleObservabilityConfiguration.class }, new String[] {})
|
||||
.getBean(SampleComponent.class);
|
||||
|
||||
assertThat(bean).isInstanceOfSatisfying(Advised.class, it -> {
|
||||
@@ -61,16 +60,15 @@ class ModuleTracingBeanPostProcessorIntegrationTests {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ModuleTracingConfiguration {
|
||||
static class ModuleObservabilityConfiguration {
|
||||
|
||||
@Bean
|
||||
ModuleTracingBeanPostProcessor foo(ConfigurableApplicationContext context) {
|
||||
@Bean ModuleObservabilityBeanPostProcessor foo(ConfigurableApplicationContext context) {
|
||||
|
||||
var runtime = ApplicationRuntime.of(context);
|
||||
var modulesRuntime = new ApplicationModulesRuntime(() -> TestApplicationModules.of(ExampleApplication.class),
|
||||
runtime);
|
||||
|
||||
return new ModuleTracingBeanPostProcessor(modulesRuntime, () -> mock(Tracer.class), context.getBeanFactory());
|
||||
return new ModuleObservabilityBeanPostProcessor(modulesRuntime, () -> ObservationRegistry.NOOP, context.getBeanFactory(), context.getEnvironment());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,14 +25,15 @@ import org.junit.jupiter.api.Test;
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitListenerAnnotationBeanPostProcessor;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
import org.springframework.modulith.runtime.ApplicationModulesRuntime;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ModuleTracingBeanPostProcessor}.
|
||||
* Unit tests for {@link ModuleObservabilityBeanPostProcessor}.
|
||||
*
|
||||
* @author Oliver Drotbohm
|
||||
*/
|
||||
class ModuleTracingBeanPostProcessorUnitTests {
|
||||
class ModuleObservabilityBeanPostProcessorUnitTests {
|
||||
|
||||
@Test // GH-498
|
||||
void doesNotProxyConfiguationProperties() {
|
||||
@@ -44,7 +45,7 @@ class ModuleTracingBeanPostProcessorUnitTests {
|
||||
doReturn(SampleProperties.class).when(mock).getUserClass(any(), any());
|
||||
doReturn(true).when(mock).isApplicationClass(any());
|
||||
|
||||
var processor = new ModuleTracingBeanPostProcessor(mock, () -> null, beanFactory);
|
||||
var processor = new ModuleObservabilityBeanPostProcessor(mock, () -> null, beanFactory, new MockEnvironment());
|
||||
|
||||
var bean = new SampleProperties();
|
||||
var result = processor.postProcessAfterInitialization(bean, "properties");
|
||||
@@ -56,8 +57,8 @@ class ModuleTracingBeanPostProcessorUnitTests {
|
||||
void processorIsRegisteredBefore() {
|
||||
|
||||
var rabbitProcessor = new RabbitListenerAnnotationBeanPostProcessor();
|
||||
var tracingProcessor = new ModuleTracingBeanPostProcessor(mock(ApplicationModulesRuntime.class), () -> null,
|
||||
new DefaultListableBeanFactory());
|
||||
var tracingProcessor = new ModuleObservabilityBeanPostProcessor(mock(ApplicationModulesRuntime.class), () -> null,
|
||||
new DefaultListableBeanFactory(), new MockEnvironment());
|
||||
|
||||
assertThat(rabbitProcessor.getOrder()).isGreaterThan(tracingProcessor.getOrder());
|
||||
|
||||
Reference in New Issue
Block a user