Skip to content

Commit 738ad3f

Browse files
marcingrzejszczakodrotbohm
authored andcommitted
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.
1 parent 163673a commit 738ad3f

20 files changed

+566
-151
lines changed

pom.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@
4848
<spring-cloud-aws-bom.version>3.1.1</spring-cloud-aws-bom.version>
4949
<testcontainers.version>1.17.6</testcontainers.version>
5050
<structurizr.version>3.1.0</structurizr.version>
51+
<!-- TODO: For snapshots -->
52+
<micrometer-tracing.version>1.5.0-SNAPSHOT</micrometer-tracing.version>
5153

5254
</properties>
5355

@@ -97,6 +99,14 @@ limitations under the License.
9799
<type>pom</type>
98100
<scope>import</scope>
99101
</dependency>
102+
<!-- TODO: For snapshots -->
103+
<dependency>
104+
<groupId>io.micrometer</groupId>
105+
<artifactId>micrometer-tracing-bom</artifactId>
106+
<version>${micrometer-tracing.version}</version>
107+
<type>pom</type>
108+
<scope>import</scope>
109+
</dependency>
100110
<dependency>
101111
<groupId>org.springframework.boot</groupId>
102112
<artifactId>spring-boot-dependencies</artifactId>

spring-modulith-observability/pom.xml

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@
1313

1414
<properties>
1515
<module.name>org.springframework.modulith.observability</module.name>
16-
<spring-cloud.version>2021.0.0</spring-cloud.version>
16+
17+
<micrometer-docs-generator.version>1.0.4</micrometer-docs-generator.version>
18+
<micrometer-docs-generator.inputPath>${project.basedir}/src/main/java/</micrometer-docs-generator.inputPath>
19+
<micrometer-docs-generator.inclusionPattern>.*</micrometer-docs-generator.inclusionPattern>
20+
<micrometer-docs-generator.outputPath>${project.build.directory}/observation-documentation/</micrometer-docs-generator.outputPath>
1721
</properties>
1822

1923
<dependencies>
@@ -73,12 +77,24 @@
7377
<optional>true</optional>
7478
</dependency>
7579

80+
<dependency>
81+
<groupId>io.micrometer</groupId>
82+
<artifactId>micrometer-core</artifactId>
83+
<optional>true</optional>
84+
</dependency>
85+
7686
<dependency>
7787
<groupId>io.zipkin.brave</groupId>
7888
<artifactId>brave</artifactId>
7989
<optional>true</optional>
8090
</dependency>
8191

92+
<dependency>
93+
<groupId>io.opentelemetry</groupId>
94+
<artifactId>opentelemetry-api</artifactId>
95+
<optional>true</optional>
96+
</dependency>
97+
8298
<!-- Testing -->
8399

84100
<dependency>
@@ -107,4 +123,39 @@
107123

108124
</dependencies>
109125

126+
<build>
127+
<plugins>
128+
<plugin>
129+
<groupId>org.codehaus.mojo</groupId>
130+
<artifactId>exec-maven-plugin</artifactId>
131+
<executions>
132+
<execution>
133+
<id>generate-docs</id>
134+
<phase>prepare-package</phase>
135+
<goals>
136+
<goal>java</goal>
137+
</goals>
138+
<configuration>
139+
<mainClass>io.micrometer.docs.DocsGeneratorCommand</mainClass>
140+
<includePluginDependencies>true</includePluginDependencies>
141+
<arguments>
142+
<argument>${micrometer-docs-generator.inputPath}</argument>
143+
<argument>${micrometer-docs-generator.inclusionPattern}</argument>
144+
<argument>${micrometer-docs-generator.outputPath}</argument>
145+
</arguments>
146+
</configuration>
147+
</execution>
148+
</executions>
149+
<dependencies>
150+
<dependency>
151+
<groupId>io.micrometer</groupId>
152+
<artifactId>micrometer-docs-generator</artifactId>
153+
<version>${micrometer-docs-generator.version}</version>
154+
<type>jar</type>
155+
</dependency>
156+
</dependencies>
157+
</plugin>
158+
</plugins>
159+
</build>
160+
110161
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package org.springframework.modulith.observability;
2+
3+
import java.lang.reflect.Method;
4+
5+
import io.micrometer.common.KeyValues;
6+
7+
import org.springframework.modulith.observability.ModulithObservations.HighKeys;
8+
import org.springframework.modulith.observability.ModulithObservations.LowKeys;
9+
10+
/**
11+
* Default implementation of {@link ModulithObservationConvention}.
12+
*
13+
* @author Marcin Grzejszczak
14+
* @since 1.4
15+
*/
16+
public class DefaultModulithObservationConvention implements ModulithObservationConvention {
17+
18+
@Override
19+
public KeyValues getLowCardinalityKeyValues(ModulithContext context) {
20+
KeyValues keyValues = KeyValues.of(LowKeys.MODULE_KEY.withValue(context.getModule().getIdentifier().toString()));
21+
if (isEventListener(context)) {
22+
return keyValues.and(LowKeys.INVOCATION_TYPE.withValue("event-listener"));
23+
}
24+
return keyValues;
25+
}
26+
27+
private boolean isEventListener(ModulithContext context) {
28+
try {
29+
return context.getModule().isEventListenerInvocation(context.getInvocation());
30+
} catch (Exception e) {
31+
return false;
32+
}
33+
}
34+
35+
@Override
36+
public KeyValues getHighCardinalityKeyValues(ModulithContext context) {
37+
Method method = context.getInvocation().getMethod();
38+
return KeyValues.of(HighKeys.MODULE_METHOD.withValue(method.getName()));
39+
}
40+
41+
@Override
42+
public String getName() {
43+
return "module.requests";
44+
}
45+
46+
@Override
47+
public String getContextualName(ModulithContext context) {
48+
return "[" + context.getApplicationName() + "] " + context.getModule().getDisplayName();
49+
}
50+
}

spring-modulith-observability/src/main/java/org/springframework/modulith/observability/DefaultObservedModule.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.springframework.modulith.core.ApplicationModule;
2626
import org.springframework.modulith.core.ApplicationModuleIdentifier;
2727
import org.springframework.modulith.core.ApplicationModules;
28+
import org.springframework.modulith.core.ArchitecturallyEvidentType;
2829
import org.springframework.modulith.core.ArchitecturallyEvidentType.ReferenceMethod;
2930
import org.springframework.modulith.core.FormattableType;
3031
import org.springframework.modulith.core.SpringBean;

spring-modulith-observability/src/main/java/org/springframework/modulith/observability/ModuleEntryInterceptor.java

Lines changed: 68 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -15,47 +15,79 @@
1515
*/
1616
package org.springframework.modulith.observability;
1717

18-
import io.micrometer.tracing.BaggageInScope;
19-
import io.micrometer.tracing.Tracer;
20-
import io.micrometer.tracing.Tracer.SpanInScope;
18+
import io.micrometer.common.KeyValue;
19+
import io.micrometer.observation.Observation;
20+
import io.micrometer.observation.ObservationRegistry;
2121

2222
import java.util.HashMap;
2323
import java.util.Map;
24+
import java.util.Objects;
2425

2526
import org.aopalliance.intercept.MethodInterceptor;
2627
import org.aopalliance.intercept.MethodInvocation;
2728
import org.slf4j.Logger;
2829
import org.slf4j.LoggerFactory;
30+
import org.springframework.core.env.Environment;
31+
import org.springframework.lang.Nullable;
32+
import org.springframework.modulith.core.ApplicationModuleIdentifier;
33+
import org.springframework.modulith.observability.ModulithObservations.LowKeys;
2934
import org.springframework.util.Assert;
3035

3136
class ModuleEntryInterceptor implements MethodInterceptor {
3237

3338
private static Logger LOGGER = LoggerFactory.getLogger(ModuleEntryInterceptor.class);
34-
private static Map<String, ModuleEntryInterceptor> CACHE = new HashMap<>();
35-
private static final String MODULE_KEY = ModuleTracingBeanPostProcessor.MODULE_BAGGAGE_KEY;
39+
private static Map<ApplicationModuleIdentifier, ModuleEntryInterceptor> CACHE = new HashMap<>();
40+
41+
private static final ModulithObservationConvention DEFAULT = new DefaultModulithObservationConvention();
3642

3743
private final ObservedModule module;
38-
private final Tracer tracer;
44+
private final ObservationRegistry observationRegistry;
45+
@Nullable private final ModulithObservationConvention customModulithObservationConvention;
46+
private final Environment environment;
47+
48+
/**
49+
* Creates a new {@link ModuleEntryInterceptor} for the given {@link ObservedModule} and {@link ObservationRegistry}.
50+
*
51+
* @param module must not be {@literal null}.
52+
* @param observationRegistry must not be {@literal null}.
53+
* @param environment must not be {@literal null}.
54+
*/
55+
private ModuleEntryInterceptor(ObservedModule module, ObservationRegistry observationRegistry,
56+
Environment environment) {
57+
this(module, observationRegistry, null, environment);
58+
}
3959

4060
/**
41-
* Creates a new {@link ModuleEntryInterceptor} for the given {@link ObservedModule} and {@link Tracer}.
61+
* Creates a new {@link ModuleEntryInterceptor} for the given {@link ObservedModule}, {@link ObservationRegistry} and
62+
* {@link ModulithObservationConvention}.
4263
*
4364
* @param module must not be {@literal null}.
44-
* @param tracer must not be {@literal null}.
65+
* @param observationRegistry must not be {@literal null}.
66+
* @param custom must not be {@literal null}.
67+
* @param environment must not be {@literal null}.
4568
*/
46-
private ModuleEntryInterceptor(ObservedModule module, Tracer tracer) {
69+
private ModuleEntryInterceptor(ObservedModule module, ObservationRegistry observationRegistry,
70+
ModulithObservationConvention custom, Environment environment) {
4771

4872
Assert.notNull(module, "ObservedModule must not be null!");
49-
Assert.notNull(tracer, "Tracer must not be null!");
73+
Assert.notNull(observationRegistry, "ObservationRegistry must not be null!");
5074

5175
this.module = module;
52-
this.tracer = tracer;
76+
this.observationRegistry = observationRegistry;
77+
this.customModulithObservationConvention = custom;
78+
this.environment = environment;
79+
}
80+
81+
public static ModuleEntryInterceptor of(ObservedModule module, ObservationRegistry observationRegistry,
82+
Environment environment) {
83+
return of(module, observationRegistry, null, environment);
5384
}
5485

55-
public static ModuleEntryInterceptor of(ObservedModule module, Tracer tracer) {
86+
public static ModuleEntryInterceptor of(ObservedModule module, ObservationRegistry observationRegistry,
87+
ModulithObservationConvention custom, Environment environment) {
5688

57-
return CACHE.computeIfAbsent(module.getName(), __ -> {
58-
return new ModuleEntryInterceptor(module, tracer);
89+
return CACHE.computeIfAbsent(module.getIdentifier(), __ -> {
90+
return new ModuleEntryInterceptor(module, observationRegistry, custom, environment);
5991
});
6092
}
6193

@@ -66,34 +98,38 @@ public static ModuleEntryInterceptor of(ObservedModule module, Tracer tracer) {
6698
@Override
6799
public Object invoke(MethodInvocation invocation) throws Throwable {
68100

69-
var moduleName = module.getName();
70-
var currentSpan = tracer.currentSpan();
71-
var currentBaggage = tracer.getBaggage(MODULE_KEY);
72-
var currentModule = currentBaggage != null ? currentBaggage.get() : null;
101+
var moduleIdentifier = module.getIdentifier();
102+
var currentObservation = observationRegistry.getCurrentObservation();
103+
String currentModule = null;
104+
105+
if (currentObservation != null) {
106+
KeyValue moduleKey = currentObservation.getContextView().getLowCardinalityKeyValue(LowKeys.MODULE_KEY.asString());
107+
currentModule = moduleKey != null ? moduleKey.getValue() : null;
108+
}
73109

74-
if (currentSpan != null && moduleName.equals(currentModule)) {
110+
if (currentObservation != null && Objects.equals(moduleIdentifier.toString(), currentModule)) {
111+
// Same module
75112
return invocation.proceed();
76113
}
77114

78115
var invokedMethod = module.getInvokedMethod(invocation);
79116

80117
LOGGER.trace("Entering {} via {}.", module.getDisplayName(), invokedMethod);
81118

82-
var span = tracer.spanBuilder()
83-
.name(moduleName)
84-
.tag("module.method", invokedMethod)
85-
.tag(MODULE_KEY, moduleName)
86-
.start();
87-
88-
try (SpanInScope ws = tracer.withSpan(span);
89-
BaggageInScope baggage = tracer.createBaggageInScope(MODULE_KEY, moduleName)) {
90-
91-
return invocation.proceed();
92-
119+
ModulithContext modulithContext = new ModulithContext(module, invocation, environment);
120+
var observation = Observation.createNotStarted(customModulithObservationConvention, DEFAULT,
121+
() -> modulithContext, observationRegistry);
122+
try (Observation.Scope scope = observation.start().openScope()) {
123+
Object proceed = invocation.proceed();
124+
observation.event(ModulithObservations.Events.EVENT_PUBLICATION_SUCCESS);
125+
return proceed;
126+
} catch (Exception ex) {
127+
observation.error(ex);
128+
observation.event(ModulithObservations.Events.EVENT_PUBLICATION_FAILURE);
129+
throw ex;
93130
} finally {
94-
95131
LOGGER.trace("Leaving {}", module.getDisplayName());
96-
span.end();
132+
observation.stop();
97133
}
98134
}
99135
}

spring-modulith-observability/src/main/java/org/springframework/modulith/observability/ModuleEventListener.java

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,17 @@
1515
*/
1616
package org.springframework.modulith.observability;
1717

18-
import io.micrometer.tracing.Tracer;
18+
import io.micrometer.core.instrument.Counter;
19+
import io.micrometer.core.instrument.MeterRegistry;
20+
import io.micrometer.observation.Observation.Event;
21+
import io.micrometer.observation.ObservationRegistry;
1922

2023
import java.util.function.Supplier;
2124

2225
import org.springframework.context.ApplicationEvent;
2326
import org.springframework.context.ApplicationListener;
2427
import org.springframework.context.PayloadApplicationEvent;
28+
import org.springframework.modulith.observability.ModulithObservations.Events;
2529
import org.springframework.modulith.runtime.ApplicationModulesRuntime;
2630
import org.springframework.util.Assert;
2731

@@ -31,21 +35,25 @@
3135
public class ModuleEventListener implements ApplicationListener<ApplicationEvent> {
3236

3337
private final ApplicationModulesRuntime runtime;
34-
private final Supplier<Tracer> tracer;
38+
private final Supplier<ObservationRegistry> observationRegistry;
39+
private final Supplier<MeterRegistry> meterRegistry;
3540

3641
/**
37-
* Creates a new {@link ModuleEventListener} for the given {@link ApplicationModulesRuntime} and {@link Tracer}.
42+
* Creates a new {@link ModuleEventListener} for the given {@link ApplicationModulesRuntime} and {@link ObservationRegistry} and {@link MeterRegistry}.
3843
*
3944
* @param runtime must not be {@literal null}.
40-
* @param tracer must not be {@literal null}.
45+
* @param observationRegistrySupplier must not be {@literal null}.
4146
*/
42-
public ModuleEventListener(ApplicationModulesRuntime runtime, Supplier<Tracer> tracer) {
47+
public ModuleEventListener(ApplicationModulesRuntime runtime, Supplier<ObservationRegistry> observationRegistrySupplier,
48+
Supplier<MeterRegistry> meterRegistrySupplier) {
4349

4450
Assert.notNull(runtime, "ApplicationModulesRuntime must not be null!");
45-
Assert.notNull(tracer, "Tracer must not be null!");
51+
Assert.notNull(observationRegistrySupplier, "ObservationRegistry must not be null!");
52+
Assert.notNull(meterRegistrySupplier, "MeterRegistry must not be null!");
4653

4754
this.runtime = runtime;
48-
this.tracer = tracer;
55+
this.observationRegistry = observationRegistrySupplier;
56+
this.meterRegistry = meterRegistrySupplier;
4957
}
5058

5159
/*
@@ -74,12 +82,20 @@ public void onApplicationEvent(ApplicationEvent event) {
7482
return;
7583
}
7684

77-
var span = tracer.get().currentSpan();
85+
MeterRegistry registry = meterRegistry.get();
86+
if (registry != null) {
87+
Counter.builder(ModulithMetrics.EVENTS.getName()) //
88+
.tags(ModulithMetrics.LowKeys.EVENT_TYPE.name().toLowerCase(), event.getClass().getSimpleName()) //
89+
.tags(ModulithMetrics.LowKeys.MODULE_NAME.name().toLowerCase(), moduleByType.getDisplayName()) //
90+
.register(registry).increment();
91+
}
92+
93+
var observation = observationRegistry.get().getCurrentObservation();
7894

79-
if (span == null) {
95+
if (observation == null) {
8096
return;
8197
}
8298

83-
span.event("Published " + payloadType.getName());
99+
observation.event(Event.of(Events.EVENT_PUBLICATION_SUCCESS.getName(), "Published " + payloadType.getName()));
84100
}
85101
}

0 commit comments

Comments
 (0)