Skip to content

Commit de59ad1

Browse files
authored
Add detailed response code meters for Jetty 10.x (#3174)
Refs #3133
1 parent 3d97e17 commit de59ad1

File tree

5 files changed

+147
-49
lines changed

5 files changed

+147
-49
lines changed

metrics-jetty10/pom.xml

+4-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,10 @@
5858
<dependency>
5959
<groupId>io.dropwizard.metrics</groupId>
6060
<artifactId>metrics-core</artifactId>
61-
<version>${project.version}</version>
61+
</dependency>
62+
<dependency>
63+
<groupId>io.dropwizard.metrics</groupId>
64+
<artifactId>metrics-annotation</artifactId>
6265
</dependency>
6366
<dependency>
6467
<groupId>org.eclipse.jetty</groupId>

metrics-jetty10/src/main/java/io/dropwizard/metrics/jetty10/InstrumentedHandler.java

+67-21
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.codahale.metrics.MetricRegistry;
66
import com.codahale.metrics.RatioGauge;
77
import com.codahale.metrics.Timer;
8+
import com.codahale.metrics.annotation.ResponseMeteredLevel;
89
import org.eclipse.jetty.http.HttpMethod;
910
import org.eclipse.jetty.server.AsyncContextState;
1011
import org.eclipse.jetty.server.Handler;
@@ -18,9 +19,19 @@
1819
import javax.servlet.http.HttpServletRequest;
1920
import javax.servlet.http.HttpServletResponse;
2021
import java.io.IOException;
22+
import java.util.Arrays;
23+
import java.util.Collections;
24+
import java.util.EnumSet;
25+
import java.util.List;
26+
import java.util.Map;
27+
import java.util.Set;
28+
import java.util.concurrent.ConcurrentHashMap;
2129
import java.util.concurrent.TimeUnit;
2230

2331
import static com.codahale.metrics.MetricRegistry.name;
32+
import static com.codahale.metrics.annotation.ResponseMeteredLevel.ALL;
33+
import static com.codahale.metrics.annotation.ResponseMeteredLevel.COARSE;
34+
import static com.codahale.metrics.annotation.ResponseMeteredLevel.DETAILED;
2435

2536
/**
2637
* A Jetty {@link Handler} which records various metrics about an underlying {@link Handler}
@@ -55,6 +66,8 @@ public class InstrumentedHandler extends HandlerWrapper {
5566
private static final String NAME_PERCENT_5XX_1M = "percent-5xx-1m";
5667
private static final String NAME_PERCENT_5XX_5M = "percent-5xx-5m";
5768
private static final String NAME_PERCENT_5XX_15M = "percent-5xx-15m";
69+
private static final Set<ResponseMeteredLevel> COARSE_METER_LEVELS = EnumSet.of(COARSE, ALL);
70+
private static final Set<ResponseMeteredLevel> DETAILED_METER_LEVELS = EnumSet.of(DETAILED, ALL);
5871

5972
private final MetricRegistry metricRegistry;
6073

@@ -82,7 +95,9 @@ public class InstrumentedHandler extends HandlerWrapper {
8295
// the number of requests that expired while suspended
8396
private Meter asyncTimeouts;
8497

85-
private Meter[] responses;
98+
private final ResponseMeteredLevel responseMeteredLevel;
99+
private List<Meter> responses;
100+
private Map<Integer, Meter> responseCodeMeters;
86101

87102
private Timer getRequests;
88103
private Timer postRequests;
@@ -113,8 +128,20 @@ public InstrumentedHandler(MetricRegistry registry) {
113128
* @param prefix the prefix to use for the metrics names
114129
*/
115130
public InstrumentedHandler(MetricRegistry registry, String prefix) {
131+
this(registry, prefix, COARSE);
132+
}
133+
134+
/**
135+
* Create a new instrumented handler using a given metrics registry.
136+
*
137+
* @param registry the registry for the metrics
138+
* @param prefix the prefix to use for the metrics names
139+
* @param responseMeteredLevel the level to determine individual/aggregate response codes that are instrumented
140+
*/
141+
public InstrumentedHandler(MetricRegistry registry, String prefix, ResponseMeteredLevel responseMeteredLevel) {
116142
this.metricRegistry = registry;
117143
this.prefix = prefix;
144+
this.responseMeteredLevel = responseMeteredLevel;
118145
}
119146

120147
public String getName() {
@@ -141,13 +168,15 @@ protected void doStart() throws Exception {
141168
this.asyncDispatches = metricRegistry.meter(name(prefix, NAME_ASYNC_DISPATCHES));
142169
this.asyncTimeouts = metricRegistry.meter(name(prefix, NAME_ASYNC_TIMEOUTS));
143170

144-
this.responses = new Meter[]{
145-
metricRegistry.meter(name(prefix, NAME_1XX_RESPONSES)), // 1xx
146-
metricRegistry.meter(name(prefix, NAME_2XX_RESPONSES)), // 2xx
147-
metricRegistry.meter(name(prefix, NAME_3XX_RESPONSES)), // 3xx
148-
metricRegistry.meter(name(prefix, NAME_4XX_RESPONSES)), // 4xx
149-
metricRegistry.meter(name(prefix, NAME_5XX_RESPONSES)) // 5xx
150-
};
171+
this.responseCodeMeters = DETAILED_METER_LEVELS.contains(responseMeteredLevel) ? new ConcurrentHashMap<>() : Collections.emptyMap();
172+
this.responses = COARSE_METER_LEVELS.contains(responseMeteredLevel) ?
173+
Collections.unmodifiableList(Arrays.asList(
174+
metricRegistry.meter(name(prefix, NAME_1XX_RESPONSES)), // 1xx
175+
metricRegistry.meter(name(prefix, NAME_2XX_RESPONSES)), // 2xx
176+
metricRegistry.meter(name(prefix, NAME_3XX_RESPONSES)), // 3xx
177+
metricRegistry.meter(name(prefix, NAME_4XX_RESPONSES)), // 4xx
178+
metricRegistry.meter(name(prefix, NAME_5XX_RESPONSES)) // 5xx
179+
)) : Collections.emptyList();
151180

152181
this.getRequests = metricRegistry.timer(name(prefix, NAME_GET_REQUESTS));
153182
this.postRequests = metricRegistry.timer(name(prefix, NAME_POST_REQUESTS));
@@ -163,47 +192,47 @@ protected void doStart() throws Exception {
163192
metricRegistry.register(name(prefix, NAME_PERCENT_4XX_1M), new RatioGauge() {
164193
@Override
165194
protected Ratio getRatio() {
166-
return Ratio.of(responses[3].getOneMinuteRate(),
195+
return Ratio.of(responses.get(3).getOneMinuteRate(),
167196
requests.getOneMinuteRate());
168197
}
169198
});
170199

171200
metricRegistry.register(name(prefix, NAME_PERCENT_4XX_5M), new RatioGauge() {
172201
@Override
173202
protected Ratio getRatio() {
174-
return Ratio.of(responses[3].getFiveMinuteRate(),
203+
return Ratio.of(responses.get(3).getFiveMinuteRate(),
175204
requests.getFiveMinuteRate());
176205
}
177206
});
178207

179208
metricRegistry.register(name(prefix, NAME_PERCENT_4XX_15M), new RatioGauge() {
180209
@Override
181210
protected Ratio getRatio() {
182-
return Ratio.of(responses[3].getFifteenMinuteRate(),
211+
return Ratio.of(responses.get(3).getFifteenMinuteRate(),
183212
requests.getFifteenMinuteRate());
184213
}
185214
});
186215

187216
metricRegistry.register(name(prefix, NAME_PERCENT_5XX_1M), new RatioGauge() {
188217
@Override
189218
protected Ratio getRatio() {
190-
return Ratio.of(responses[4].getOneMinuteRate(),
219+
return Ratio.of(responses.get(4).getOneMinuteRate(),
191220
requests.getOneMinuteRate());
192221
}
193222
});
194223

195224
metricRegistry.register(name(prefix, NAME_PERCENT_5XX_5M), new RatioGauge() {
196225
@Override
197226
protected Ratio getRatio() {
198-
return Ratio.of(responses[4].getFiveMinuteRate(),
227+
return Ratio.of(responses.get(4).getFiveMinuteRate(),
199228
requests.getFiveMinuteRate());
200229
}
201230
});
202231

203232
metricRegistry.register(name(prefix, NAME_PERCENT_5XX_15M), new RatioGauge() {
204233
@Override
205234
public Ratio getRatio() {
206-
return Ratio.of(responses[4].getFifteenMinuteRate(),
235+
return Ratio.of(responses.get(4).getFifteenMinuteRate(),
207236
requests.getFifteenMinuteRate());
208237
}
209238
});
@@ -244,7 +273,9 @@ protected void doStop() throws Exception {
244273
metricRegistry.remove(name(prefix, NAME_PERCENT_5XX_1M));
245274
metricRegistry.remove(name(prefix, NAME_PERCENT_5XX_5M));
246275
metricRegistry.remove(name(prefix, NAME_PERCENT_5XX_15M));
247-
276+
responseCodeMeters.keySet().stream()
277+
.map(sc -> name(getMetricPrefix(), String.format("%d-responses", sc)))
278+
.forEach(m -> metricRegistry.remove(m));
248279
super.doStop();
249280
}
250281

@@ -321,21 +352,36 @@ private Timer requestTimer(String method) {
321352
}
322353

323354
private void updateResponses(HttpServletRequest request, HttpServletResponse response, long start, boolean isHandled) {
324-
final int responseStatus;
325355
if (isHandled) {
326-
responseStatus = response.getStatus() / 100;
356+
mark(response.getStatus());
327357
} else {
328-
responseStatus = 4; // will end up with a 404 response sent by HttpChannel.handle
329-
}
330-
if (responseStatus >= 1 && responseStatus <= 5) {
331-
responses[responseStatus - 1].mark();
358+
mark(404);; // will end up with a 404 response sent by HttpChannel.handle
332359
}
333360
activeRequests.dec();
334361
final long elapsedTime = System.currentTimeMillis() - start;
335362
requests.update(elapsedTime, TimeUnit.MILLISECONDS);
336363
requestTimer(request.getMethod()).update(elapsedTime, TimeUnit.MILLISECONDS);
337364
}
338365

366+
private void mark(int statusCode) {
367+
if (DETAILED_METER_LEVELS.contains(responseMeteredLevel)) {
368+
getResponseCodeMeter(statusCode).mark();
369+
}
370+
371+
if (COARSE_METER_LEVELS.contains(responseMeteredLevel)) {
372+
final int responseStatus = statusCode / 100;
373+
if (responseStatus >= 1 && responseStatus <= 5) {
374+
responses.get(responseStatus - 1).mark();
375+
}
376+
}
377+
}
378+
379+
private Meter getResponseCodeMeter(int statusCode) {
380+
return responseCodeMeters
381+
.computeIfAbsent(statusCode, sc -> metricRegistry
382+
.meter(name(getMetricPrefix(), String.format("%d-responses", sc))));
383+
}
384+
339385
private String getMetricPrefix() {
340386
return this.prefix == null ? name(getHandler().getClass(), name) : name(this.prefix, name);
341387
}

0 commit comments

Comments
 (0)