5
5
import com .codahale .metrics .MetricRegistry ;
6
6
import com .codahale .metrics .RatioGauge ;
7
7
import com .codahale .metrics .Timer ;
8
+ import com .codahale .metrics .annotation .ResponseMeteredLevel ;
8
9
import org .eclipse .jetty .http .HttpMethod ;
9
10
import org .eclipse .jetty .server .AsyncContextState ;
10
11
import org .eclipse .jetty .server .Handler ;
18
19
import javax .servlet .http .HttpServletRequest ;
19
20
import javax .servlet .http .HttpServletResponse ;
20
21
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 ;
21
29
import java .util .concurrent .TimeUnit ;
22
30
23
31
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 ;
24
35
25
36
/**
26
37
* A Jetty {@link Handler} which records various metrics about an underlying {@link Handler}
@@ -55,6 +66,8 @@ public class InstrumentedHandler extends HandlerWrapper {
55
66
private static final String NAME_PERCENT_5XX_1M = "percent-5xx-1m" ;
56
67
private static final String NAME_PERCENT_5XX_5M = "percent-5xx-5m" ;
57
68
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 );
58
71
59
72
private final MetricRegistry metricRegistry ;
60
73
@@ -82,7 +95,9 @@ public class InstrumentedHandler extends HandlerWrapper {
82
95
// the number of requests that expired while suspended
83
96
private Meter asyncTimeouts ;
84
97
85
- private Meter [] responses ;
98
+ private final ResponseMeteredLevel responseMeteredLevel ;
99
+ private List <Meter > responses ;
100
+ private Map <Integer , Meter > responseCodeMeters ;
86
101
87
102
private Timer getRequests ;
88
103
private Timer postRequests ;
@@ -113,8 +128,20 @@ public InstrumentedHandler(MetricRegistry registry) {
113
128
* @param prefix the prefix to use for the metrics names
114
129
*/
115
130
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 ) {
116
142
this .metricRegistry = registry ;
117
143
this .prefix = prefix ;
144
+ this .responseMeteredLevel = responseMeteredLevel ;
118
145
}
119
146
120
147
public String getName () {
@@ -141,13 +168,15 @@ protected void doStart() throws Exception {
141
168
this .asyncDispatches = metricRegistry .meter (name (prefix , NAME_ASYNC_DISPATCHES ));
142
169
this .asyncTimeouts = metricRegistry .meter (name (prefix , NAME_ASYNC_TIMEOUTS ));
143
170
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 ();
151
180
152
181
this .getRequests = metricRegistry .timer (name (prefix , NAME_GET_REQUESTS ));
153
182
this .postRequests = metricRegistry .timer (name (prefix , NAME_POST_REQUESTS ));
@@ -163,47 +192,47 @@ protected void doStart() throws Exception {
163
192
metricRegistry .register (name (prefix , NAME_PERCENT_4XX_1M ), new RatioGauge () {
164
193
@ Override
165
194
protected Ratio getRatio () {
166
- return Ratio .of (responses [ 3 ] .getOneMinuteRate (),
195
+ return Ratio .of (responses . get ( 3 ) .getOneMinuteRate (),
167
196
requests .getOneMinuteRate ());
168
197
}
169
198
});
170
199
171
200
metricRegistry .register (name (prefix , NAME_PERCENT_4XX_5M ), new RatioGauge () {
172
201
@ Override
173
202
protected Ratio getRatio () {
174
- return Ratio .of (responses [ 3 ] .getFiveMinuteRate (),
203
+ return Ratio .of (responses . get ( 3 ) .getFiveMinuteRate (),
175
204
requests .getFiveMinuteRate ());
176
205
}
177
206
});
178
207
179
208
metricRegistry .register (name (prefix , NAME_PERCENT_4XX_15M ), new RatioGauge () {
180
209
@ Override
181
210
protected Ratio getRatio () {
182
- return Ratio .of (responses [ 3 ] .getFifteenMinuteRate (),
211
+ return Ratio .of (responses . get ( 3 ) .getFifteenMinuteRate (),
183
212
requests .getFifteenMinuteRate ());
184
213
}
185
214
});
186
215
187
216
metricRegistry .register (name (prefix , NAME_PERCENT_5XX_1M ), new RatioGauge () {
188
217
@ Override
189
218
protected Ratio getRatio () {
190
- return Ratio .of (responses [ 4 ] .getOneMinuteRate (),
219
+ return Ratio .of (responses . get ( 4 ) .getOneMinuteRate (),
191
220
requests .getOneMinuteRate ());
192
221
}
193
222
});
194
223
195
224
metricRegistry .register (name (prefix , NAME_PERCENT_5XX_5M ), new RatioGauge () {
196
225
@ Override
197
226
protected Ratio getRatio () {
198
- return Ratio .of (responses [ 4 ] .getFiveMinuteRate (),
227
+ return Ratio .of (responses . get ( 4 ) .getFiveMinuteRate (),
199
228
requests .getFiveMinuteRate ());
200
229
}
201
230
});
202
231
203
232
metricRegistry .register (name (prefix , NAME_PERCENT_5XX_15M ), new RatioGauge () {
204
233
@ Override
205
234
public Ratio getRatio () {
206
- return Ratio .of (responses [ 4 ] .getFifteenMinuteRate (),
235
+ return Ratio .of (responses . get ( 4 ) .getFifteenMinuteRate (),
207
236
requests .getFifteenMinuteRate ());
208
237
}
209
238
});
@@ -244,7 +273,9 @@ protected void doStop() throws Exception {
244
273
metricRegistry .remove (name (prefix , NAME_PERCENT_5XX_1M ));
245
274
metricRegistry .remove (name (prefix , NAME_PERCENT_5XX_5M ));
246
275
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 ));
248
279
super .doStop ();
249
280
}
250
281
@@ -321,21 +352,36 @@ private Timer requestTimer(String method) {
321
352
}
322
353
323
354
private void updateResponses (HttpServletRequest request , HttpServletResponse response , long start , boolean isHandled ) {
324
- final int responseStatus ;
325
355
if (isHandled ) {
326
- responseStatus = response .getStatus () / 100 ;
356
+ mark ( response .getStatus ()) ;
327
357
} 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
332
359
}
333
360
activeRequests .dec ();
334
361
final long elapsedTime = System .currentTimeMillis () - start ;
335
362
requests .update (elapsedTime , TimeUnit .MILLISECONDS );
336
363
requestTimer (request .getMethod ()).update (elapsedTime , TimeUnit .MILLISECONDS );
337
364
}
338
365
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
+
339
385
private String getMetricPrefix () {
340
386
return this .prefix == null ? name (getHandler ().getClass (), name ) : name (this .prefix , name );
341
387
}
0 commit comments