Skip to content

Commit b1bbe0c

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 fb92e69 commit b1bbe0c

File tree

9 files changed

+240
-89
lines changed

9 files changed

+240
-89
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+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright 2000-2023 Vaadin Ltd.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+
* use this file except in compliance with the License. You may obtain a copy of
6+
* the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations under
14+
* the License.
15+
*/
16+
17+
package com.vaadin.flow.server;
18+
19+
/**
20+
* Used to provide an around-like aspect option around request processing.
21+
*
22+
* @author Marcin Grzejszczak
23+
* @since 24.2
24+
*/
25+
public interface VaadinFilter {
26+
27+
/**
28+
* Called when request is about to be processed.
29+
* @param request request
30+
* @param response response
31+
*/
32+
void requestStart(VaadinRequest request, VaadinResponse response);
33+
34+
/**
35+
* Called when an exception occurred
36+
* @param request request
37+
* @param response response
38+
* @param vaadinSession session
39+
* @param t exception
40+
*/
41+
void handleException(VaadinRequest request,
42+
VaadinResponse response, VaadinSession vaadinSession, Exception t);
43+
44+
/**
45+
* Called in the finally block of processing a request. Will be called
46+
* regardless of whether there was an exception or not.
47+
* @param request request
48+
* @param response response
49+
* @param session session
50+
*/
51+
void requestEnd(VaadinRequest request, VaadinResponse response,
52+
VaadinSession session);
53+
}

flow-server/src/main/java/com/vaadin/flow/server/VaadinService.java

+29-47
Original file line numberDiff line numberDiff line change
@@ -16,41 +16,6 @@
1616

1717
package com.vaadin.flow.server;
1818

19-
import java.io.BufferedWriter;
20-
import java.io.IOException;
21-
import java.io.InputStream;
22-
import java.io.OutputStream;
23-
import java.io.OutputStreamWriter;
24-
import java.io.PrintWriter;
25-
import java.io.Serializable;
26-
import java.lang.reflect.Constructor;
27-
import java.net.URL;
28-
import java.nio.charset.StandardCharsets;
29-
import java.security.MessageDigest;
30-
import java.util.ArrayList;
31-
import java.util.Collection;
32-
import java.util.Collections;
33-
import java.util.HashMap;
34-
import java.util.List;
35-
import java.util.Locale;
36-
import java.util.Map;
37-
import java.util.Map.Entry;
38-
import java.util.Optional;
39-
import java.util.ServiceLoader;
40-
import java.util.Set;
41-
import java.util.concurrent.CancellationException;
42-
import java.util.concurrent.ConcurrentHashMap;
43-
import java.util.concurrent.CopyOnWriteArrayList;
44-
import java.util.concurrent.Future;
45-
import java.util.concurrent.TimeUnit;
46-
import java.util.concurrent.locks.Lock;
47-
import java.util.concurrent.locks.ReentrantLock;
48-
import java.util.stream.Collectors;
49-
import java.util.stream.StreamSupport;
50-
51-
import org.slf4j.Logger;
52-
import org.slf4j.LoggerFactory;
53-
5419
import com.vaadin.flow.component.UI;
5520
import com.vaadin.flow.di.DefaultInstantiator;
5621
import com.vaadin.flow.di.Instantiator;
@@ -64,26 +29,30 @@
6429
import com.vaadin.flow.router.RouteData;
6530
import com.vaadin.flow.router.Router;
6631
import com.vaadin.flow.server.HandlerHelper.RequestType;
67-
import com.vaadin.flow.server.communication.AtmospherePushConnection;
68-
import com.vaadin.flow.server.communication.HeartbeatHandler;
69-
import com.vaadin.flow.server.communication.IndexHtmlRequestListener;
70-
import com.vaadin.flow.server.communication.IndexHtmlResponse;
71-
import com.vaadin.flow.server.communication.JavaScriptBootstrapHandler;
72-
import com.vaadin.flow.server.communication.PwaHandler;
73-
import com.vaadin.flow.server.communication.SessionRequestHandler;
74-
import com.vaadin.flow.server.communication.StreamRequestHandler;
75-
import com.vaadin.flow.server.communication.UidlRequestHandler;
76-
import com.vaadin.flow.server.communication.WebComponentBootstrapHandler;
77-
import com.vaadin.flow.server.communication.WebComponentProvider;
32+
import com.vaadin.flow.server.communication.*;
7833
import com.vaadin.flow.shared.ApplicationConstants;
7934
import com.vaadin.flow.shared.JsonConstants;
8035
import com.vaadin.flow.shared.Registration;
8136
import com.vaadin.flow.shared.communication.PushMode;
82-
8337
import elemental.json.Json;
8438
import elemental.json.JsonException;
8539
import elemental.json.JsonObject;
8640
import elemental.json.impl.JsonUtil;
41+
import org.slf4j.Logger;
42+
import org.slf4j.LoggerFactory;
43+
44+
import java.io.*;
45+
import java.lang.reflect.Constructor;
46+
import java.net.URL;
47+
import java.nio.charset.StandardCharsets;
48+
import java.security.MessageDigest;
49+
import java.util.*;
50+
import java.util.Map.Entry;
51+
import java.util.concurrent.*;
52+
import java.util.concurrent.locks.Lock;
53+
import java.util.concurrent.locks.ReentrantLock;
54+
import java.util.stream.Collectors;
55+
import java.util.stream.StreamSupport;
8756

8857
import static java.nio.charset.StandardCharsets.UTF_8;
8958

@@ -114,6 +83,8 @@ public abstract class VaadinService implements Serializable {
11483
+ PushMode.class.getSimpleName() + "." + PushMode.DISABLED.name()
11584
+ "." + SEPARATOR;
11685

86+
private List<VaadinFilter> vaadinFilters = new ArrayList<>();
87+
11788
/**
11889
* Attribute name for telling
11990
* {@link VaadinSession#valueUnbound(jakarta.servlet.http.HttpSessionBindingEvent)}
@@ -1433,6 +1404,7 @@ public void requestStart(VaadinRequest request, VaadinResponse response) {
14331404
}
14341405
setCurrentInstances(request, response);
14351406
request.setAttribute(REQUEST_START_TIME_ATTRIBUTE, System.nanoTime());
1407+
vaadinFilters.forEach(vaadinFilter -> vaadinFilter.requestStart(request, response));
14361408
}
14371409

14381410
/**
@@ -1449,6 +1421,7 @@ public void requestStart(VaadinRequest request, VaadinResponse response) {
14491421
*/
14501422
public void requestEnd(VaadinRequest request, VaadinResponse response,
14511423
VaadinSession session) {
1424+
vaadinFilters.forEach(vaadinFilter -> vaadinFilter.requestEnd(request, response, session));
14521425
if (session != null) {
14531426
assert VaadinSession.getCurrent() == session;
14541427
session.lock();
@@ -1544,6 +1517,7 @@ private void handleExceptionDuringRequest(VaadinRequest request,
15441517
vaadinSession.lock();
15451518
}
15461519
try {
1520+
vaadinFilters.forEach(vaadinFilter -> vaadinFilter.handleException(request, response, vaadinSession, t));
15471521
if (vaadinSession != null) {
15481522
vaadinSession.getErrorHandler().error(new ErrorEvent(t));
15491523
}
@@ -2374,6 +2348,14 @@ public static String getCsrfTokenAttributeName() {
23742348
+ ApplicationConstants.CSRF_TOKEN;
23752349
}
23762350

2351+
public List<VaadinFilter> getVaadinFilters() {
2352+
return vaadinFilters;
2353+
}
2354+
2355+
public void setVaadinFilters(List<VaadinFilter> vaadinFilters) {
2356+
this.vaadinFilters = vaadinFilters;
2357+
}
2358+
23772359
private void doSetClassLoader() {
23782360
final String classLoaderName = getDeploymentConfiguration() == null
23792361
? null

flow-server/src/main/java/com/vaadin/flow/server/VaadinServlet.java

+17-14
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,6 @@
1515
*/
1616
package com.vaadin.flow.server;
1717

18-
import java.io.IOException;
19-
import java.net.MalformedURLException;
20-
import java.net.URL;
21-
import java.util.ArrayList;
22-
import java.util.Collection;
23-
import java.util.Collections;
24-
import java.util.List;
25-
import java.util.Map;
26-
import java.util.Properties;
27-
28-
import org.slf4j.Logger;
29-
import org.slf4j.LoggerFactory;
30-
3118
import com.vaadin.flow.component.UI;
3219
import com.vaadin.flow.di.Lookup;
3320
import com.vaadin.flow.function.DeploymentConfiguration;
@@ -37,14 +24,20 @@
3724
import com.vaadin.flow.server.HandlerHelper.RequestType;
3825
import com.vaadin.flow.server.startup.ApplicationConfiguration;
3926
import com.vaadin.flow.shared.JsonConstants;
40-
4127
import jakarta.servlet.ServletConfig;
4228
import jakarta.servlet.ServletContext;
4329
import jakarta.servlet.ServletException;
4430
import jakarta.servlet.ServletRegistration;
4531
import jakarta.servlet.http.HttpServlet;
4632
import jakarta.servlet.http.HttpServletRequest;
4733
import jakarta.servlet.http.HttpServletResponse;
34+
import org.slf4j.Logger;
35+
import org.slf4j.LoggerFactory;
36+
37+
import java.io.IOException;
38+
import java.net.MalformedURLException;
39+
import java.net.URL;
40+
import java.util.*;
4841

4942
/**
5043
* The main servlet, which handles all incoming requests to the application.
@@ -72,6 +65,8 @@ public class VaadinServlet extends HttpServlet {
7265

7366
private static List<Runnable> whenFrontendMappingAvailable = new ArrayList<>();
7467

68+
private List<VaadinFilter> vaadinFilters = new ArrayList<>();
69+
7570
/**
7671
* Called by the servlet container to indicate to a servlet that the servlet
7772
* is being placed into service.
@@ -647,6 +642,14 @@ private VaadinServletContext initializeContext() {
647642
return vaadinServletContext;
648643
}
649644

645+
public List<VaadinFilter> getVaadinFilters() {
646+
return vaadinFilters;
647+
}
648+
649+
public void setVaadinFilters(List<VaadinFilter> vaadinFilters) {
650+
this.vaadinFilters = vaadinFilters;
651+
}
652+
650653
/**
651654
* For internal use only.
652655
*

flow-server/src/main/java/com/vaadin/flow/server/VaadinServletService.java

+11-12
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,19 @@
1616

1717
package com.vaadin.flow.server;
1818

19+
import com.vaadin.flow.function.DeploymentConfiguration;
20+
import com.vaadin.flow.internal.DevModeHandler;
21+
import com.vaadin.flow.internal.DevModeHandlerManager;
22+
import com.vaadin.flow.server.communication.FaviconHandler;
23+
import com.vaadin.flow.server.communication.IndexHtmlRequestHandler;
24+
import com.vaadin.flow.server.communication.PushRequestHandler;
25+
import com.vaadin.flow.server.startup.ApplicationRouteRegistry;
26+
import com.vaadin.flow.shared.ApplicationConstants;
1927
import jakarta.servlet.GenericServlet;
2028
import jakarta.servlet.ServletContext;
2129
import jakarta.servlet.http.HttpServletRequest;
30+
import org.slf4j.Logger;
31+
import org.slf4j.LoggerFactory;
2232

2333
import java.io.InputStream;
2434
import java.net.MalformedURLException;
@@ -27,18 +37,6 @@
2737
import java.util.Objects;
2838
import java.util.Optional;
2939

30-
import org.slf4j.Logger;
31-
import org.slf4j.LoggerFactory;
32-
33-
import com.vaadin.flow.function.DeploymentConfiguration;
34-
import com.vaadin.flow.internal.DevModeHandler;
35-
import com.vaadin.flow.internal.DevModeHandlerManager;
36-
import com.vaadin.flow.server.communication.FaviconHandler;
37-
import com.vaadin.flow.server.communication.IndexHtmlRequestHandler;
38-
import com.vaadin.flow.server.communication.PushRequestHandler;
39-
import com.vaadin.flow.server.startup.ApplicationRouteRegistry;
40-
import com.vaadin.flow.shared.ApplicationConstants;
41-
4240
/**
4341
* A service implementation connected to a {@link VaadinServlet}.
4442
*
@@ -66,6 +64,7 @@ public VaadinServletService(VaadinServlet servlet,
6664
DeploymentConfiguration deploymentConfiguration) {
6765
super(deploymentConfiguration);
6866
this.servlet = servlet;
67+
setVaadinFilters(servlet.getVaadinFilters());
6968
}
7069

7170
/**

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>

0 commit comments

Comments
 (0)