WIP
This commit is contained in:
@@ -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>
|
||||
@@ -79,6 +83,12 @@
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.opentelemetry</groupId>
|
||||
<artifactId>opentelemetry-api</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Testing -->
|
||||
|
||||
<dependency>
|
||||
@@ -95,4 +105,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,37 @@
|
||||
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;
|
||||
|
||||
public class DefaultModulithObservationConvention implements ModulithObservationConvention {
|
||||
|
||||
@Override
|
||||
public KeyValues getLowCardinalityKeyValues(ModulithContext context) {
|
||||
ObservedModule currentModule = context.getModule();
|
||||
if (currentModule != null) {
|
||||
return KeyValues.of(LowKeys.MODULE_KEY.withValue(currentModule.getName()));
|
||||
}
|
||||
return KeyValues.empty();
|
||||
}
|
||||
|
||||
@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) {
|
||||
// TODO: Change this to application name
|
||||
return "[" + context.getModule().getDisplayName() + "] " + 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,17 +15,21 @@
|
||||
*/
|
||||
package org.springframework.modulith.observability;
|
||||
|
||||
import io.micrometer.tracing.BaggageInScope;
|
||||
import io.micrometer.tracing.Tracer;
|
||||
import io.micrometer.tracing.Tracer.SpanInScope;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import io.micrometer.common.KeyValue;
|
||||
import io.micrometer.observation.Observation;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.modulith.observability.ModulithObservations.LowKeys;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
class ModuleEntryInterceptor implements MethodInterceptor {
|
||||
@@ -34,28 +38,49 @@ class ModuleEntryInterceptor implements MethodInterceptor {
|
||||
private static Map<String, ModuleEntryInterceptor> CACHE = new HashMap<>();
|
||||
private static final String MODULE_KEY = ModuleTracingBeanPostProcessor.MODULE_BAGGAGE_KEY;
|
||||
|
||||
private static final ModulithObservationConvention DEFAULT = new DefaultModulithObservationConvention();
|
||||
|
||||
private final ObservedModule module;
|
||||
private final Tracer tracer;
|
||||
private final ObservationRegistry observationRegistry;
|
||||
@Nullable private final ModulithObservationConvention customModulithObservationConvention;
|
||||
|
||||
/**
|
||||
* 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 module must not be {@literal null}.
|
||||
* @param observationRegistry 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) {
|
||||
this(module, observationRegistry, null);
|
||||
}
|
||||
|
||||
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}.
|
||||
*/
|
||||
private ModuleEntryInterceptor(ObservedModule module, ObservationRegistry observationRegistry,
|
||||
ModulithObservationConvention custom) {
|
||||
|
||||
Assert.notNull(module, "ObservedModule must not be null!");
|
||||
Assert.notNull(observationRegistry, "Tracer must not be null!");
|
||||
|
||||
this.module = module; this.observationRegistry = observationRegistry;
|
||||
this.customModulithObservationConvention = custom;
|
||||
}
|
||||
|
||||
public static ModuleEntryInterceptor of(ObservedModule module, ObservationRegistry observationRegistry) {
|
||||
return of(module, observationRegistry, null);
|
||||
}
|
||||
|
||||
public static ModuleEntryInterceptor of(ObservedModule module, ObservationRegistry observationRegistry,
|
||||
ModulithObservationConvention custom) {
|
||||
|
||||
return CACHE.computeIfAbsent(module.getName(), __ -> {
|
||||
return new ModuleEntryInterceptor(module, tracer);
|
||||
return new ModuleEntryInterceptor(module, observationRegistry, custom);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -63,15 +88,19 @@ class ModuleEntryInterceptor implements MethodInterceptor {
|
||||
* (non-Javadoc)
|
||||
* @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation)
|
||||
*/
|
||||
@Override
|
||||
public Object invoke(MethodInvocation invocation) throws Throwable {
|
||||
@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 currentObservation = observationRegistry.getCurrentObservation();
|
||||
String currentModule = null;
|
||||
|
||||
if (currentSpan != null && moduleName.equals(currentModule)) {
|
||||
if (currentObservation != null) {
|
||||
KeyValue moduleKey = currentObservation.getContextView().getLowCardinalityKeyValue(MODULE_KEY);
|
||||
currentModule = moduleKey != null ? moduleKey.getValue() : null;
|
||||
}
|
||||
|
||||
if (currentObservation != null && moduleName.equals(currentModule)) {
|
||||
// Same module
|
||||
return invocation.proceed();
|
||||
}
|
||||
|
||||
@@ -79,21 +108,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();
|
||||
boolean isEventListener = module.isEventListenerInvocation(invocation);
|
||||
|
||||
// TODO: Good name for metrics
|
||||
ModulithContext modulithContext = new ModulithContext(module, invocation);
|
||||
var observation = Observation.createNotStarted(customModulithObservationConvention, DEFAULT,
|
||||
() -> modulithContext, observationRegistry); if (isEventListener) {
|
||||
observation.lowCardinalityKeyValue(LowKeys.INVOCATION_TYPE.withValue("event-listener"));
|
||||
} try (Observation.Scope scope = observation.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();
|
||||
LOGGER.trace("Leaving {}", module.getDisplayName()); observation.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.modulith.observability;
|
||||
|
||||
import io.micrometer.observation.Observation.Event;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import io.micrometer.tracing.Tracer;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
@@ -22,6 +24,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.runtime.ApplicationModulesRuntime;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -31,21 +34,21 @@ 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;
|
||||
|
||||
/**
|
||||
* Creates a new {@link ModuleEventListener} for the given {@link ApplicationModulesRuntime} and {@link Tracer}.
|
||||
*
|
||||
* @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) {
|
||||
|
||||
Assert.notNull(runtime, "ApplicationModulesRuntime must not be null!");
|
||||
Assert.notNull(tracer, "Tracer must not be null!");
|
||||
Assert.notNull(observationRegistrySupplier, "Tracer must not be null!");
|
||||
|
||||
this.runtime = runtime;
|
||||
this.tracer = tracer;
|
||||
this.observationRegistry = observationRegistrySupplier;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -74,12 +77,12 @@ public class ModuleEventListener implements ApplicationListener<ApplicationEvent
|
||||
return;
|
||||
}
|
||||
|
||||
var span = tracer.get().currentSpan();
|
||||
var observation = observationRegistry.get().getCurrentObservation();
|
||||
|
||||
if (span == null) {
|
||||
if (observation == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
span.event("Published " + payloadType.getName());
|
||||
observation.event(Event.of(Events.EVENT_PUBLICATION_SUCCESS.getName(), "Published " + payloadType.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.modulith.observability;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import io.micrometer.tracing.Tracer;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
@@ -47,7 +48,7 @@ public class ModuleTracingBeanPostProcessor extends ModuleTracingSupport impleme
|
||||
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;
|
||||
|
||||
@@ -56,16 +57,16 @@ public class ModuleTracingBeanPostProcessor extends ModuleTracingSupport impleme
|
||||
* {@link Tracer}.
|
||||
*
|
||||
* @param runtime must not be {@literal null}.
|
||||
* @param tracer must not be {@literal null}.
|
||||
* @param observationRegistry must not be {@literal null}.
|
||||
*/
|
||||
public ModuleTracingBeanPostProcessor(ApplicationModulesRuntime runtime, Supplier<Tracer> tracer,
|
||||
public ModuleTracingBeanPostProcessor(ApplicationModulesRuntime runtime, Supplier<ObservationRegistry> observationRegistry,
|
||||
ConfigurableListableBeanFactory factory) {
|
||||
|
||||
Assert.notNull(runtime, "ApplicationModulesRuntime must not be null!");
|
||||
Assert.notNull(tracer, "Tracer must not be null!");
|
||||
Assert.notNull(observationRegistry, "Tracer must not be null!");
|
||||
|
||||
this.runtime = runtime;
|
||||
this.tracer = tracer;
|
||||
this.observationRegistry = observationRegistry;
|
||||
this.advisors = new HashMap<>();
|
||||
this.factory = factory;
|
||||
}
|
||||
@@ -120,7 +121,7 @@ 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 new ApplicationModuleObservingAdvisor(type, ModuleEntryInterceptor.of(module, observationRegistry.get()));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package org.springframework.modulith.observability;
|
||||
|
||||
import io.micrometer.observation.Observation;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
/**
|
||||
* A {@link Observation.Context} for Modulithic applications.
|
||||
*
|
||||
* @author Marcin Grzejsczak
|
||||
* @since 1.3
|
||||
*/
|
||||
public class ModulithContext extends Observation.Context {
|
||||
|
||||
private final ObservedModule module;
|
||||
|
||||
private final MethodInvocation invocation;
|
||||
|
||||
public ModulithContext(ObservedModule module, MethodInvocation invocation) {
|
||||
this.module = module;
|
||||
this.invocation = invocation;
|
||||
}
|
||||
|
||||
public ObservedModule getModule() {
|
||||
return module;
|
||||
}
|
||||
|
||||
public MethodInvocation getInvocation() {
|
||||
return invocation;
|
||||
}
|
||||
}
|
||||
@@ -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.3
|
||||
*/
|
||||
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.3
|
||||
*/
|
||||
interface ObservedModule {
|
||||
public interface ObservedModule {
|
||||
|
||||
/**
|
||||
* Returns the name of the application module.
|
||||
|
||||
@@ -35,8 +35,9 @@ import org.springframework.util.ReflectionUtils;
|
||||
* Represents a type in an {@link ObservedModule}.
|
||||
*
|
||||
* @author Oliver Drotbohm
|
||||
* @since 1.3
|
||||
*/
|
||||
class ObservedModuleType {
|
||||
public class ObservedModuleType {
|
||||
|
||||
private static Collection<Class<?>> IGNORED_TYPES = List.of(Advised.class, TargetClassAware.class);
|
||||
private static Predicate<Method> IS_USER_METHOD = it -> !Modifier.isPrivate(it.getModifiers())
|
||||
@@ -115,4 +116,5 @@ class ObservedModuleType {
|
||||
.map(it -> !module.isObservedModule(it))
|
||||
.orElse(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.modulith.observability;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import io.micrometer.tracing.Tracer;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
@@ -40,22 +41,22 @@ import org.springframework.util.Assert;
|
||||
public class SpringDataRestModuleTracingBeanPostProcessor extends ModuleTracingSupport implements BeanPostProcessor {
|
||||
|
||||
private final ApplicationModulesRuntime runtime;
|
||||
private final Supplier<Tracer> tracer;
|
||||
private final Supplier<ObservationRegistry> observationRegistry;
|
||||
|
||||
/**
|
||||
* Creates a new {@link SpringDataRestModuleTracingBeanPostProcessor} for the given {@link ApplicationModulesRuntime}
|
||||
* and {@link Tracer}.
|
||||
*
|
||||
* @param runtime must not be {@literal null}.
|
||||
* @param tracer must not be {@literal null}.
|
||||
* @param observationRegistry must not be {@literal null}.
|
||||
*/
|
||||
public SpringDataRestModuleTracingBeanPostProcessor(ApplicationModulesRuntime runtime, Supplier<Tracer> tracer) {
|
||||
public SpringDataRestModuleTracingBeanPostProcessor(ApplicationModulesRuntime runtime, Supplier<ObservationRegistry> observationRegistry) {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -71,7 +72,7 @@ public class SpringDataRestModuleTracingBeanPostProcessor extends ModuleTracingS
|
||||
return bean;
|
||||
}
|
||||
|
||||
Advice interceptor = new DataRestControllerInterceptor(runtime, tracer);
|
||||
Advice interceptor = new DataRestControllerInterceptor(runtime, observationRegistry);
|
||||
Advisor advisor = new DefaultPointcutAdvisor(interceptor);
|
||||
|
||||
return addAdvisor(bean, advisor, it -> it.setProxyTargetClass(true));
|
||||
@@ -80,21 +81,21 @@ 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;
|
||||
|
||||
/**
|
||||
* 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 observationRegistry must not be {@literal null}.
|
||||
*/
|
||||
private DataRestControllerInterceptor(Supplier<ApplicationModules> modules, Supplier<Tracer> tracer) {
|
||||
private DataRestControllerInterceptor(Supplier<ApplicationModules> modules, Supplier<ObservationRegistry> observationRegistry) {
|
||||
|
||||
Assert.notNull(modules, "ApplicationModules must not be null!");
|
||||
Assert.notNull(tracer, "Tracer must not be null!");
|
||||
Assert.notNull(observationRegistry, "Tracer must not be null!");
|
||||
|
||||
this.modules = modules;
|
||||
this.tracer = tracer;
|
||||
this.observationRegistry = observationRegistry;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -112,7 +113,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()).invoke(invocation);
|
||||
}
|
||||
|
||||
private ApplicationModule getModuleFrom(Object[] arguments) {
|
||||
|
||||
@@ -15,18 +15,10 @@
|
||||
*/
|
||||
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.observation.ObservationRegistry;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnThreading;
|
||||
import org.springframework.boot.autoconfigure.thread.Threading;
|
||||
@@ -48,16 +40,17 @@ class ModuleObservabilityAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
static ModuleTracingBeanPostProcessor moduleTracingBeanPostProcessor(ApplicationModulesRuntime runtime,
|
||||
ObjectProvider<Tracer> tracer, ConfigurableListableBeanFactory factory) {
|
||||
return new ModuleTracingBeanPostProcessor(runtime, () -> tracer.getObject(), factory);
|
||||
ObjectProvider<ObservationRegistry> observationRegistry, ConfigurableListableBeanFactory factory) {
|
||||
return new ModuleTracingBeanPostProcessor(runtime, observationRegistry::getObject, factory);
|
||||
}
|
||||
|
||||
@Bean
|
||||
static ModuleEventListener tracingModuleEventListener(ApplicationModulesRuntime runtime,
|
||||
ObjectProvider<Tracer> tracer) {
|
||||
return new ModuleEventListener(runtime, () -> tracer.getObject());
|
||||
ObjectProvider<ObservationRegistry> observationRegistry) {
|
||||
return new ModuleEventListener(runtime, observationRegistry::getObject);
|
||||
}
|
||||
|
||||
// TODO: Have a custom thread pool for modulith
|
||||
@Bean
|
||||
@ConditionalOnThreading(Threading.VIRTUAL)
|
||||
SimpleAsyncTaskExecutorCustomizer simpleAsyncTaskExecutorCustomizer() {
|
||||
@@ -69,52 +62,4 @@ class ModuleObservabilityAutoConfiguration {
|
||||
ThreadPoolTaskExecutorCustomizer threadPoolTaskExecutorCustomizer() {
|
||||
return executor -> executor.setTaskDecorator(new ContextPropagatingTaskDecorator());
|
||||
}
|
||||
|
||||
/**
|
||||
* Brave-specific auto configuration.
|
||||
*
|
||||
* @author Oliver Drotbohm
|
||||
*/
|
||||
@ConditionalOnClass(TracingCustomizer.class)
|
||||
static class ModulithsBraveIntegrationAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
BaggagePropagationCustomizer moduleBaggagePropagationCustomizer() {
|
||||
|
||||
return builder -> builder
|
||||
.add(BaggagePropagationConfig.SingleBaggageField
|
||||
.local(BaggageField.create(ModuleTracingBeanPostProcessor.MODULE_BAGGAGE_KEY)));
|
||||
}
|
||||
|
||||
@Bean
|
||||
SpanHandler spanHandler() {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
BaggageField field = BaggageField.getByName(context, ModuleTracingBeanPostProcessor.MODULE_BAGGAGE_KEY);
|
||||
value = field.getValue();
|
||||
|
||||
if (value != null) {
|
||||
span.localServiceName(value);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.modulith.observability.autoconfigure;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import io.micrometer.tracing.Tracer;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
@@ -34,8 +35,8 @@ class SpringDataRestModuleObservabilityAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
static SpringDataRestModuleTracingBeanPostProcessor springDataRestModuleTracingBeanPostProcessor(
|
||||
ApplicationModulesRuntime runtime, ObjectProvider<Tracer> tracer) {
|
||||
ApplicationModulesRuntime runtime, ObjectProvider<ObservationRegistry> observationRegistry) {
|
||||
|
||||
return new SpringDataRestModuleTracingBeanPostProcessor(runtime, () -> tracer.getObject());
|
||||
return new SpringDataRestModuleTracingBeanPostProcessor(runtime, () -> observationRegistry.getObject());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import static org.mockito.Mockito.*;
|
||||
|
||||
import example.ExampleApplication;
|
||||
import example.sample.SampleComponent;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import io.micrometer.tracing.Tracer;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -70,7 +71,7 @@ class ModuleTracingBeanPostProcessorIntegrationTests {
|
||||
var modulesRuntime = new ApplicationModulesRuntime(() -> TestApplicationModules.of(ExampleApplication.class),
|
||||
runtime);
|
||||
|
||||
return new ModuleTracingBeanPostProcessor(modulesRuntime, () -> mock(Tracer.class), context.getBeanFactory());
|
||||
return new ModuleTracingBeanPostProcessor(modulesRuntime, () -> ObservationRegistry.NOOP, context.getBeanFactory());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user