Skip to content

Commit b0ce659

Browse files
WIP on Micrometer Observation
- adds a VaadinFilter mechanism to mimic an around-aspect - adds a Micrometer Observation implementation - configures VaadinServlet to automatically update the VaadinService with filters - configures Spring to automatically update the VaadinServlet with filters
1 parent 6a7486c commit b0ce659

File tree

5 files changed

+118
-7
lines changed

5 files changed

+118
-7
lines changed

flow-server/pom.xml

+14
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,20 @@
135135
<artifactId>commons-compress</artifactId>
136136
<version>1.23.0</version>
137137
</dependency>
138+
139+
<dependency>
140+
<groupId>io.micrometer</groupId>
141+
<artifactId>micrometer-observation</artifactId>
142+
<version>${micrometer.version}</version>
143+
<optional>true</optional>
144+
</dependency>
145+
<dependency>
146+
<groupId>io.micrometer</groupId>
147+
<artifactId>micrometer-jakarta</artifactId>
148+
<version>${micrometer.version}</version>
149+
<optional>true</optional>
150+
</dependency>
151+
138152
<!-- TESTING DEPENDENCIES -->
139153
<dependency>
140154
<groupId>com.vaadin</groupId>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package com.vaadin.flow.server;
2+
3+
import io.micrometer.common.lang.Nullable;
4+
import io.micrometer.jakarta.instrument.binder.http.DefaultHttpJakartaServerServletRequestObservationConvention;
5+
import io.micrometer.jakarta.instrument.binder.http.HttpJakartaServerServletRequestObservationContext;
6+
import io.micrometer.jakarta.instrument.binder.http.HttpJakartaServerServletRequestObservationConvention;
7+
import io.micrometer.jakarta.instrument.binder.http.JakartaHttpObservationDocumentation;
8+
import io.micrometer.observation.Observation;
9+
import io.micrometer.observation.ObservationRegistry;
10+
import jakarta.servlet.http.HttpServletResponse;
11+
12+
/**
13+
* Micrometer Observation {@link VaadinFilter} that will start
14+
* observations around processing of a request.
15+
*
16+
* @author Marcin Grzejszczak
17+
* @since 24.2
18+
*/
19+
public class ObservedVaadinFilter implements VaadinFilter {
20+
21+
private static final String SCOPE_ATTRIBUTE = ObservedVaadinFilter.class.getName() + ".scope";
22+
23+
private final ObservationRegistry observationRegistry;
24+
25+
private final HttpJakartaServerServletRequestObservationConvention convention;
26+
27+
public ObservedVaadinFilter(ObservationRegistry observationRegistry,
28+
@Nullable HttpJakartaServerServletRequestObservationConvention convention) {
29+
this.observationRegistry = observationRegistry;
30+
this.convention = convention;
31+
}
32+
33+
@Override
34+
public void requestStart(VaadinRequest request, VaadinResponse response) {
35+
if (request instanceof VaadinServletRequest servletRequest && response instanceof VaadinServletResponse servletResponse) {
36+
HttpJakartaServerServletRequestObservationContext context = new HttpJakartaServerServletRequestObservationContext(servletRequest, servletResponse);
37+
Observation observation = JakartaHttpObservationDocumentation.JAKARTA_SERVLET_SERVER_OBSERVATION.start(this.convention, DefaultHttpJakartaServerServletRequestObservationConvention.INSTANCE, () -> context, observationRegistry);
38+
request.setAttribute(SCOPE_ATTRIBUTE, observation.openScope());
39+
}
40+
}
41+
42+
@Override
43+
public void handleException(VaadinRequest request, VaadinResponse response, VaadinSession vaadinSession, Exception t) {
44+
Observation.Scope scope = (Observation.Scope) request.getAttribute(SCOPE_ATTRIBUTE);
45+
if (scope == null) {
46+
return;
47+
}
48+
scope.getCurrentObservation().error(t);
49+
}
50+
51+
@Override
52+
public void requestEnd(VaadinRequest request, VaadinResponse response, VaadinSession session) {
53+
Observation.Scope scope = (Observation.Scope) request.getAttribute(SCOPE_ATTRIBUTE);
54+
if (scope == null) {
55+
return;
56+
}
57+
scope.close();
58+
Observation observation = scope.getCurrentObservation();
59+
if (!observation.isNoop() && response instanceof HttpServletResponse httpServletResponse) {
60+
HttpJakartaServerServletRequestObservationContext ctx = (HttpJakartaServerServletRequestObservationContext) observation.getContext();
61+
ctx.setResponse(httpServletResponse);
62+
}
63+
observation.stop();
64+
}
65+
}

pom.xml

+2
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@
9797
<jaxb.version>4.0.3</jaxb.version>
9898
<guava.version>32.1.2-jre</guava.version>
9999
<jakarta.websocket.version>2.1.1</jakarta.websocket.version>
100+
<micrometer.version>1.12.0-SNAPSHOT</micrometer.version>
101+
<micrometer-tracing.version>1.2.0-SNAPSHOT</micrometer-tracing.version>
100102

101103
<!-- Plugins -->
102104
<frontend.maven.plugin.version>1.5</frontend.maven.plugin.version>

vaadin-spring/pom.xml

+22-7
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
<description>Provides Spring integration for Vaadin applications.</description>
1616
<packaging>jar</packaging>
1717

18+
<properties>
19+
<micrometer.version>1.12.0-SNAPSHOT</micrometer.version>
20+
</properties>
21+
1822
<dependencies>
1923
<dependency>
2024
<groupId>com.vaadin</groupId>
@@ -57,6 +61,11 @@
5761
<artifactId>spring-boot-starter-web</artifactId>
5862
<scope>provided</scope>
5963
</dependency>
64+
<dependency>
65+
<groupId>org.springframework.boot</groupId>
66+
<artifactId>spring-boot-starter-actuator</artifactId>
67+
<optional>true</optional>
68+
</dependency>
6069

6170
<dependency>
6271
<groupId>org.springframework</groupId>
@@ -103,6 +112,12 @@
103112
<version>${project.version}</version>
104113
<optional>true</optional>
105114
</dependency>
115+
<dependency>
116+
<groupId>io.micrometer</groupId>
117+
<artifactId>micrometer-jakarta</artifactId>
118+
<version>${micrometer.version}</version>
119+
<optional>true</optional>
120+
</dependency>
106121

107122
<dependency>
108123
<groupId>org.springframework</groupId>
@@ -175,13 +190,13 @@
175190

176191
<dependencyManagement>
177192
<dependencies>
178-
<dependency>
179-
<groupId>org.springframework.boot</groupId>
180-
<artifactId>spring-boot-dependencies</artifactId>
181-
<version>${spring.boot.version}</version>
182-
<type>pom</type>
183-
<scope>import</scope>
184-
</dependency>
193+
<dependency>
194+
<groupId>org.springframework.boot</groupId>
195+
<artifactId>spring-boot-dependencies</artifactId>
196+
<version>${spring.boot.version}</version>
197+
<type>pom</type>
198+
<scope>import</scope>
199+
</dependency>
185200
</dependencies>
186201
</dependencyManagement>
187202
</project>

vaadin-spring/src/main/java/com/vaadin/flow/spring/SpringBootAutoConfiguration.java

+15
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,18 @@
1616
package com.vaadin.flow.spring;
1717

1818
import com.vaadin.flow.server.Constants;
19+
import com.vaadin.flow.server.ObservedVaadinFilter;
1920
import com.vaadin.flow.server.VaadinFilter;
2021
import com.vaadin.flow.server.VaadinServlet;
2122
import com.vaadin.flow.spring.springnative.VaadinBeanFactoryInitializationAotProcessor;
23+
import io.micrometer.jakarta.instrument.binder.http.HttpJakartaServerServletRequestObservationConvention;
24+
import io.micrometer.observation.ObservationRegistry;
2225
import jakarta.servlet.MultipartConfigElement;
2326
import org.atmosphere.cpr.ApplicationConfig;
2427
import org.springframework.beans.factory.ObjectProvider;
2528
import org.springframework.beans.factory.annotation.Autowired;
29+
import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration;
30+
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
2631
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
2732
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2833
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
@@ -132,4 +137,14 @@ public ServerEndpointExporter websocketEndpointDeployer() {
132137
return new VaadinWebsocketEndpointExporter();
133138
}
134139

140+
@Configuration(proxyBeanMethods = false)
141+
@AutoConfigureAfter(ObservationAutoConfiguration.class)
142+
@ConditionalOnClass(ObservationRegistry.class)
143+
static class ObservabilityConfig {
144+
145+
@Bean
146+
ObservedVaadinFilter observedVaadinFilter(ObservationRegistry observationRegistry, ObjectProvider<HttpJakartaServerServletRequestObservationConvention> convention) {
147+
return new ObservedVaadinFilter(observationRegistry, convention.getIfAvailable(() -> null));
148+
}
149+
}
135150
}

0 commit comments

Comments
 (0)