entry : meterNamesByStatusCode.entrySet()) {
+ metersByStatusCode.put(entry.getKey(),
+ metricsRegistry.meter(name(metricName, entry.getValue())));
+ }
+ this.otherMeter = metricsRegistry.meter(name(metricName, otherMetricName));
+ this.timeoutsMeter = metricsRegistry.meter(name(metricName, "timeouts"));
+ this.errorsMeter = metricsRegistry.meter(name(metricName, "errors"));
+ this.activeRequests = metricsRegistry.counter(name(metricName, "activeRequests"));
+ this.requestTimer = metricsRegistry.timer(name(metricName, "requests"));
+
+ }
+
+ private MetricRegistry getMetricsFactory(FilterConfig filterConfig) {
+ final MetricRegistry metricsRegistry;
+
+ final Object o = filterConfig.getServletContext().getAttribute(this.registryAttribute);
+ if (o instanceof MetricRegistry) {
+ metricsRegistry = (MetricRegistry) o;
+ } else {
+ metricsRegistry = new MetricRegistry();
+ }
+ return metricsRegistry;
+ }
+
+ @Override
+ public void destroy() {
+
+ }
+
+ @Override
+ public void doFilter(ServletRequest request,
+ ServletResponse response,
+ FilterChain chain) throws IOException, ServletException {
+ final StatusExposingServletResponse wrappedResponse =
+ new StatusExposingServletResponse((HttpServletResponse) response);
+ activeRequests.inc();
+ final Timer.Context context = requestTimer.time();
+ boolean error = false;
+ try {
+ chain.doFilter(request, wrappedResponse);
+ } catch (IOException | RuntimeException | ServletException e) {
+ error = true;
+ throw e;
+ } finally {
+ if (!error && request.isAsyncStarted()) {
+ request.getAsyncContext().addListener(new AsyncResultListener(context));
+ } else {
+ context.stop();
+ activeRequests.dec();
+ if (error) {
+ errorsMeter.mark();
+ } else {
+ markMeterForStatusCode(wrappedResponse.getStatus());
+ }
+ }
+ }
+ }
+
+ private void markMeterForStatusCode(int status) {
+ final Meter metric = metersByStatusCode.get(status);
+ if (metric != null) {
+ metric.mark();
+ } else {
+ otherMeter.mark();
+ }
+ }
+
+ private static class StatusExposingServletResponse extends HttpServletResponseWrapper {
+ // The Servlet spec says: calling setStatus is optional, if no status is set, the default is 200.
+ private int httpStatus = 200;
+
+ public StatusExposingServletResponse(HttpServletResponse response) {
+ super(response);
+ }
+
+ @Override
+ public void sendError(int sc) throws IOException {
+ httpStatus = sc;
+ super.sendError(sc);
+ }
+
+ @Override
+ public void sendError(int sc, String msg) throws IOException {
+ httpStatus = sc;
+ super.sendError(sc, msg);
+ }
+
+ @Override
+ public void setStatus(int sc) {
+ httpStatus = sc;
+ super.setStatus(sc);
+ }
+
+ @Override
+ public int getStatus() {
+ return httpStatus;
+ }
+ }
+
+ private class AsyncResultListener implements AsyncListener {
+ private final Timer.Context context;
+ private boolean done = false;
+
+ public AsyncResultListener(Timer.Context context) {
+ this.context = context;
+ }
+
+ @Override
+ public void onComplete(AsyncEvent event) throws IOException {
+ if (!done) {
+ HttpServletResponse suppliedResponse = (HttpServletResponse) event.getSuppliedResponse();
+ context.stop();
+ activeRequests.dec();
+ markMeterForStatusCode(suppliedResponse.getStatus());
+ }
+ }
+
+ @Override
+ public void onTimeout(AsyncEvent event) throws IOException {
+ context.stop();
+ activeRequests.dec();
+ timeoutsMeter.mark();
+ done = true;
+ }
+
+ @Override
+ public void onError(AsyncEvent event) throws IOException {
+ context.stop();
+ activeRequests.dec();
+ errorsMeter.mark();
+ done = true;
+ }
+
+ @Override
+ public void onStartAsync(AsyncEvent event) throws IOException {
+
+ }
+ }
+}
diff --git a/metrics-jakarta-servlet6/src/main/java/io/dropwizard/metrics/servlet6/InstrumentedFilter.java b/metrics-jakarta-servlet6/src/main/java/io/dropwizard/metrics/servlet6/InstrumentedFilter.java
new file mode 100644
index 0000000000..e4b37fdc79
--- /dev/null
+++ b/metrics-jakarta-servlet6/src/main/java/io/dropwizard/metrics/servlet6/InstrumentedFilter.java
@@ -0,0 +1,48 @@
+package io.dropwizard.metrics.servlet6;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Implementation of the {@link AbstractInstrumentedFilter} which provides a default set of response codes
+ * to capture information about. Use it in your servlet.xml like this:
+ *
{@code
+ *
+ * instrumentedFilter
+ * io.dropwizard.metrics.servlet.InstrumentedFilter
+ *
+ *
+ * instrumentedFilter
+ * /*
+ *
+ * }
+ */
+public class InstrumentedFilter extends AbstractInstrumentedFilter {
+ public static final String REGISTRY_ATTRIBUTE = InstrumentedFilter.class.getName() + ".registry";
+
+ private static final String NAME_PREFIX = "responseCodes.";
+ private static final int OK = 200;
+ private static final int CREATED = 201;
+ private static final int NO_CONTENT = 204;
+ private static final int BAD_REQUEST = 400;
+ private static final int NOT_FOUND = 404;
+ private static final int SERVER_ERROR = 500;
+
+ /**
+ * Creates a new instance of the filter.
+ */
+ public InstrumentedFilter() {
+ super(REGISTRY_ATTRIBUTE, createMeterNamesByStatusCode(), NAME_PREFIX + "other");
+ }
+
+ private static Map createMeterNamesByStatusCode() {
+ final Map meterNamesByStatusCode = new HashMap<>(6);
+ meterNamesByStatusCode.put(OK, NAME_PREFIX + "ok");
+ meterNamesByStatusCode.put(CREATED, NAME_PREFIX + "created");
+ meterNamesByStatusCode.put(NO_CONTENT, NAME_PREFIX + "noContent");
+ meterNamesByStatusCode.put(BAD_REQUEST, NAME_PREFIX + "badRequest");
+ meterNamesByStatusCode.put(NOT_FOUND, NAME_PREFIX + "notFound");
+ meterNamesByStatusCode.put(SERVER_ERROR, NAME_PREFIX + "serverError");
+ return meterNamesByStatusCode;
+ }
+}
diff --git a/metrics-jakarta-servlet6/src/main/java/io/dropwizard/metrics/servlet6/InstrumentedFilterContextListener.java b/metrics-jakarta-servlet6/src/main/java/io/dropwizard/metrics/servlet6/InstrumentedFilterContextListener.java
new file mode 100644
index 0000000000..b9315847fe
--- /dev/null
+++ b/metrics-jakarta-servlet6/src/main/java/io/dropwizard/metrics/servlet6/InstrumentedFilterContextListener.java
@@ -0,0 +1,26 @@
+package io.dropwizard.metrics.servlet6;
+
+import com.codahale.metrics.MetricRegistry;
+import jakarta.servlet.ServletContextEvent;
+import jakarta.servlet.ServletContextListener;
+
+/**
+ * A listener implementation which injects a {@link MetricRegistry} instance into the servlet
+ * context. Implement {@link #getMetricRegistry()} to return the {@link MetricRegistry} for your
+ * application.
+ */
+public abstract class InstrumentedFilterContextListener implements ServletContextListener {
+ /**
+ * @return the {@link MetricRegistry} to inject into the servlet context.
+ */
+ protected abstract MetricRegistry getMetricRegistry();
+
+ @Override
+ public void contextInitialized(ServletContextEvent sce) {
+ sce.getServletContext().setAttribute(InstrumentedFilter.REGISTRY_ATTRIBUTE, getMetricRegistry());
+ }
+
+ @Override
+ public void contextDestroyed(ServletContextEvent sce) {
+ }
+}
diff --git a/metrics-jakarta-servlet6/src/test/java/io/dropwizard/metrics/servlet6/InstrumentedFilterContextListenerTest.java b/metrics-jakarta-servlet6/src/test/java/io/dropwizard/metrics/servlet6/InstrumentedFilterContextListenerTest.java
new file mode 100644
index 0000000000..74062ef7cc
--- /dev/null
+++ b/metrics-jakarta-servlet6/src/test/java/io/dropwizard/metrics/servlet6/InstrumentedFilterContextListenerTest.java
@@ -0,0 +1,32 @@
+package io.dropwizard.metrics.servlet6;
+
+import com.codahale.metrics.MetricRegistry;
+import jakarta.servlet.ServletContext;
+import jakarta.servlet.ServletContextEvent;
+import org.junit.Test;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class InstrumentedFilterContextListenerTest {
+ private final MetricRegistry registry = mock(MetricRegistry.class);
+ private final InstrumentedFilterContextListener listener = new InstrumentedFilterContextListener() {
+ @Override
+ protected MetricRegistry getMetricRegistry() {
+ return registry;
+ }
+ };
+
+ @Test
+ public void injectsTheMetricRegistryIntoTheServletContext() {
+ final ServletContext context = mock(ServletContext.class);
+
+ final ServletContextEvent event = mock(ServletContextEvent.class);
+ when(event.getServletContext()).thenReturn(context);
+
+ listener.contextInitialized(event);
+
+ verify(context).setAttribute("io.dropwizard.metrics.servlet6.InstrumentedFilter.registry", registry);
+ }
+}
diff --git a/metrics-jakarta-servlets/pom.xml b/metrics-jakarta-servlets/pom.xml
index fd6f8ab211..678a34053d 100644
--- a/metrics-jakarta-servlets/pom.xml
+++ b/metrics-jakarta-servlets/pom.xml
@@ -19,7 +19,7 @@
io.dropwizard.metrics.servlets
1.1.1
- 5.0.0
+ 6.0.0
2.12.7.1
2.0.7
diff --git a/metrics-servlet/pom.xml b/metrics-servlet/pom.xml
index 644f5208d8..66155d237b 100644
--- a/metrics-servlet/pom.xml
+++ b/metrics-servlet/pom.xml
@@ -17,7 +17,7 @@
com.codahale.metrics.servlet
- 3.1.0
+ 4.0.1
diff --git a/metrics-servlets/pom.xml b/metrics-servlets/pom.xml
index 46449a6a64..adbabefca6 100644
--- a/metrics-servlets/pom.xml
+++ b/metrics-servlets/pom.xml
@@ -19,7 +19,7 @@
com.codahale.metrics.servlets
1.1.1
- 3.1.0
+ 4.0.1
2.12.7.1
diff --git a/pom.xml b/pom.xml
index 10a70540e9..4628525be6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -28,6 +28,7 @@
metrics-httpclient5
metrics-httpasyncclient
metrics-jakarta-servlet
+ metrics-jakarta-servlet6
metrics-jakarta-servlets
metrics-jcache
metrics-jcstress