View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.server.handler;
20  
21  import java.io.IOException;
22  import java.util.concurrent.atomic.AtomicInteger;
23  import java.util.concurrent.atomic.AtomicLong;
24  
25  import javax.servlet.AsyncEvent;
26  import javax.servlet.AsyncListener;
27  import javax.servlet.ServletException;
28  import javax.servlet.http.HttpServletRequest;
29  import javax.servlet.http.HttpServletResponse;
30  
31  import org.eclipse.jetty.server.HttpChannelState;
32  import org.eclipse.jetty.server.Request;
33  import org.eclipse.jetty.server.Response;
34  import org.eclipse.jetty.util.annotation.ManagedAttribute;
35  import org.eclipse.jetty.util.annotation.ManagedObject;
36  import org.eclipse.jetty.util.annotation.ManagedOperation;
37  import org.eclipse.jetty.util.statistic.CounterStatistic;
38  import org.eclipse.jetty.util.statistic.SampleStatistic;
39  
40  @ManagedObject("Request Statistics Gathering")
41  public class StatisticsHandler extends HandlerWrapper
42  {
43      private final AtomicLong _statsStartedAt = new AtomicLong();
44  
45      private final CounterStatistic _requestStats = new CounterStatistic();
46      private final SampleStatistic _requestTimeStats = new SampleStatistic();
47      private final CounterStatistic _dispatchedStats = new CounterStatistic();
48      private final SampleStatistic _dispatchedTimeStats = new SampleStatistic();
49      private final CounterStatistic _asyncWaitStats = new CounterStatistic();
50  
51      private final AtomicInteger _asyncDispatches = new AtomicInteger();
52      private final AtomicInteger _expires = new AtomicInteger();
53  
54      private final AtomicInteger _responses1xx = new AtomicInteger();
55      private final AtomicInteger _responses2xx = new AtomicInteger();
56      private final AtomicInteger _responses3xx = new AtomicInteger();
57      private final AtomicInteger _responses4xx = new AtomicInteger();
58      private final AtomicInteger _responses5xx = new AtomicInteger();
59      private final AtomicLong _responsesTotalBytes = new AtomicLong();
60  
61      private final AsyncListener _onCompletion = new AsyncListener()
62      {
63          @Override
64          public void onTimeout(AsyncEvent event) throws IOException
65          {
66              _expires.incrementAndGet();
67          }
68          
69          @Override
70          public void onStartAsync(AsyncEvent event) throws IOException
71          {
72              event.getAsyncContext().addListener(this);
73          }
74          
75          @Override
76          public void onError(AsyncEvent event) throws IOException
77          {
78          }
79          
80          @Override
81          public void onComplete(AsyncEvent event) throws IOException
82          {
83              HttpChannelState state = (HttpChannelState)event.getAsyncContext();
84              
85              Request request = state.getBaseRequest();
86              final long elapsed = System.currentTimeMillis()-request.getTimeStamp();
87  
88              _requestStats.decrement();
89              _requestTimeStats.set(elapsed);
90  
91              updateResponse(request);
92  
93              if (!state.isDispatched())
94                  _asyncWaitStats.decrement();
95          }
96          
97      };
98  
99      /**
100      * Resets the current request statistics.
101      */
102     @ManagedOperation(value="resets statistics", impact="ACTION")
103     public void statsReset()
104     {
105         _statsStartedAt.set(System.currentTimeMillis());
106 
107         _requestStats.reset();
108         _requestTimeStats.reset();
109         _dispatchedStats.reset();
110         _dispatchedTimeStats.reset();
111         _asyncWaitStats.reset();
112 
113         _asyncDispatches.set(0);
114         _expires.set(0);
115         _responses1xx.set(0);
116         _responses2xx.set(0);
117         _responses3xx.set(0);
118         _responses4xx.set(0);
119         _responses5xx.set(0);
120         _responsesTotalBytes.set(0L);
121     }
122 
123     @Override
124     public void handle(String path, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException
125     {
126         _dispatchedStats.increment();
127 
128         final long start;
129         HttpChannelState state = request.getHttpChannelState();
130         if (state.isInitial())
131         {
132             // new request
133             _requestStats.increment();
134             start = request.getTimeStamp();
135         }
136         else
137         {
138             // resumed request
139             start = System.currentTimeMillis();
140             _asyncWaitStats.decrement();
141             if (state.isDispatched())
142                 _asyncDispatches.incrementAndGet();
143         }
144 
145         try
146         {
147             super.handle(path, request, httpRequest, httpResponse);
148         }
149         finally
150         {
151             final long now = System.currentTimeMillis();
152             final long dispatched=now-start;
153 
154             _dispatchedStats.decrement();
155             _dispatchedTimeStats.set(dispatched);
156 
157             if (state.isSuspended())
158             {
159                 if (state.isInitial())
160                     state.addListener(_onCompletion);
161                 _asyncWaitStats.increment();
162             }
163             else if (state.isInitial())
164             {
165                 _requestStats.decrement();
166                 _requestTimeStats.set(dispatched);
167                 updateResponse(request);
168             }
169             // else onCompletion will handle it.
170         }
171     }
172 
173     private void updateResponse(Request request)
174     {
175         Response response = request.getResponse();
176         switch (response.getStatus() / 100)
177         {
178             case 0:
179                 if (request.isHandled())
180                     _responses2xx.incrementAndGet();
181                 else
182                     _responses4xx.incrementAndGet();
183                 break;
184             case 1:
185                 _responses1xx.incrementAndGet();
186                 break;
187             case 2:
188                 _responses2xx.incrementAndGet();
189                 break;
190             case 3:
191                 _responses3xx.incrementAndGet();
192                 break;
193             case 4:
194                 _responses4xx.incrementAndGet();
195                 break;
196             case 5:
197                 _responses5xx.incrementAndGet();
198                 break;
199             default:
200                 break;
201         }
202         _responsesTotalBytes.addAndGet(response.getContentCount());
203     }
204 
205     @Override
206     protected void doStart() throws Exception
207     {
208         super.doStart();
209         statsReset();
210     }
211 
212     /**
213      * @return the number of requests handled by this handler
214      * since {@link #statsReset()} was last called, excluding
215      * active requests
216      * @see #getAsyncDispatches()
217      */
218     @ManagedAttribute("number of requests")
219     public int getRequests()
220     {
221         return (int)_requestStats.getTotal();
222     }
223 
224     /**
225      * @return the number of requests currently active.
226      * since {@link #statsReset()} was last called.
227      */
228     @ManagedAttribute("number of requests currently active")
229     public int getRequestsActive()
230     {
231         return (int)_requestStats.getCurrent();
232     }
233 
234     /**
235      * @return the maximum number of active requests
236      * since {@link #statsReset()} was last called.
237      */
238     @ManagedAttribute("maximum number of active requests")
239     public int getRequestsActiveMax()
240     {
241         return (int)_requestStats.getMax();
242     }
243 
244     /**
245      * @return the maximum time (in milliseconds) of request handling
246      * since {@link #statsReset()} was last called.
247      */
248     @ManagedAttribute("maximum time spend handling requests (in ms)")
249     public long getRequestTimeMax()
250     {
251         return _requestTimeStats.getMax();
252     }
253 
254     /**
255      * @return the total time (in milliseconds) of requests handling
256      * since {@link #statsReset()} was last called.
257      */
258     @ManagedAttribute("total time spend in all request handling (in ms)")
259     public long getRequestTimeTotal()
260     {
261         return _requestTimeStats.getTotal();
262     }
263 
264     /**
265      * @return the mean time (in milliseconds) of request handling
266      * since {@link #statsReset()} was last called.
267      * @see #getRequestTimeTotal()
268      * @see #getRequests()
269      */
270     @ManagedAttribute("mean time spent handling requests (in ms)")
271     public double getRequestTimeMean()
272     {
273         return _requestTimeStats.getMean();
274     }
275 
276     /**
277      * @return the standard deviation of time (in milliseconds) of request handling
278      * since {@link #statsReset()} was last called.
279      * @see #getRequestTimeTotal()
280      * @see #getRequests()
281      */
282     @ManagedAttribute("standard deviation for request handling (in ms)")
283     public double getRequestTimeStdDev()
284     {
285         return _requestTimeStats.getStdDev();
286     }
287 
288     /**
289      * @return the number of dispatches seen by this handler
290      * since {@link #statsReset()} was last called, excluding
291      * active dispatches
292      */
293     @ManagedAttribute("number of dispatches")
294     public int getDispatched()
295     {
296         return (int)_dispatchedStats.getTotal();
297     }
298 
299     /**
300      * @return the number of dispatches currently in this handler
301      * since {@link #statsReset()} was last called, including
302      * resumed requests
303      */
304     @ManagedAttribute("number of dispatches currently active")
305     public int getDispatchedActive()
306     {
307         return (int)_dispatchedStats.getCurrent();
308     }
309 
310     /**
311      * @return the max number of dispatches currently in this handler
312      * since {@link #statsReset()} was last called, including
313      * resumed requests
314      */
315     @ManagedAttribute("maximum number of active dispatches being handled")
316     public int getDispatchedActiveMax()
317     {
318         return (int)_dispatchedStats.getMax();
319     }
320 
321     /**
322      * @return the maximum time (in milliseconds) of request dispatch
323      * since {@link #statsReset()} was last called.
324      */
325     @ManagedAttribute("maximum time spend in dispatch handling")
326     public long getDispatchedTimeMax()
327     {
328         return _dispatchedTimeStats.getMax();
329     }
330 
331     /**
332      * @return the total time (in milliseconds) of requests handling
333      * since {@link #statsReset()} was last called.
334      */
335     @ManagedAttribute("total time spent in dispatch handling (in ms)")
336     public long getDispatchedTimeTotal()
337     {
338         return _dispatchedTimeStats.getTotal();
339     }
340 
341     /**
342      * @return the mean time (in milliseconds) of request handling
343      * since {@link #statsReset()} was last called.
344      * @see #getRequestTimeTotal()
345      * @see #getRequests()
346      */
347     @ManagedAttribute("mean time spent in dispatch handling (in ms)")
348     public double getDispatchedTimeMean()
349     {
350         return _dispatchedTimeStats.getMean();
351     }
352 
353     /**
354      * @return the standard deviation of time (in milliseconds) of request handling
355      * since {@link #statsReset()} was last called.
356      * @see #getRequestTimeTotal()
357      * @see #getRequests()
358      */
359     @ManagedAttribute("standard deviation for dispatch handling (in ms)")
360     public double getDispatchedTimeStdDev()
361     {
362         return _dispatchedTimeStats.getStdDev();
363     }
364 
365     /**
366      * @return the number of requests handled by this handler
367      * since {@link #statsReset()} was last called, including
368      * resumed requests
369      * @see #getAsyncDispatches()
370      */
371     @ManagedAttribute("total number of async requests")
372     public int getAsyncRequests()
373     {
374         return (int)_asyncWaitStats.getTotal();
375     }
376 
377     /**
378      * @return the number of requests currently suspended.
379      * since {@link #statsReset()} was last called.
380      */
381     @ManagedAttribute("currently waiting async requests")
382     public int getAsyncRequestsWaiting()
383     {
384         return (int)_asyncWaitStats.getCurrent();
385     }
386 
387     /**
388      * @return the maximum number of current suspended requests
389      * since {@link #statsReset()} was last called.
390      */
391     @ManagedAttribute("maximum number of waiting async requests")
392     public int getAsyncRequestsWaitingMax()
393     {
394         return (int)_asyncWaitStats.getMax();
395     }
396 
397     /**
398      * @return the number of requests that have been asynchronously dispatched
399      */
400     @ManagedAttribute("number of requested that have been asynchronously dispatched")
401     public int getAsyncDispatches()
402     {
403         return _asyncDispatches.get();
404     }
405 
406     /**
407      * @return the number of requests that expired while suspended.
408      * @see #getAsyncDispatches()
409      */
410     @ManagedAttribute("number of async requests requests that have expired")
411     public int getExpires()
412     {
413         return _expires.get();
414     }
415 
416     /**
417      * @return the number of responses with a 1xx status returned by this context
418      * since {@link #statsReset()} was last called.
419      */
420     @ManagedAttribute("number of requests with 1xx response status")
421     public int getResponses1xx()
422     {
423         return _responses1xx.get();
424     }
425 
426     /**
427      * @return the number of responses with a 2xx status returned by this context
428      * since {@link #statsReset()} was last called.
429      */
430     @ManagedAttribute("number of requests with 2xx response status")
431     public int getResponses2xx()
432     {
433         return _responses2xx.get();
434     }
435 
436     /**
437      * @return the number of responses with a 3xx status returned by this context
438      * since {@link #statsReset()} was last called.
439      */
440     @ManagedAttribute("number of requests with 3xx response status")
441     public int getResponses3xx()
442     {
443         return _responses3xx.get();
444     }
445 
446     /**
447      * @return the number of responses with a 4xx status returned by this context
448      * since {@link #statsReset()} was last called.
449      */
450     @ManagedAttribute("number of requests with 4xx response status")
451     public int getResponses4xx()
452     {
453         return _responses4xx.get();
454     }
455 
456     /**
457      * @return the number of responses with a 5xx status returned by this context
458      * since {@link #statsReset()} was last called.
459      */
460     @ManagedAttribute("number of requests with 5xx response status")
461     public int getResponses5xx()
462     {
463         return _responses5xx.get();
464     }
465 
466     /**
467      * @return the milliseconds since the statistics were started with {@link #statsReset()}.
468      */
469     @ManagedAttribute("time in milliseconds stats have been collected for")
470     public long getStatsOnMs()
471     {
472         return System.currentTimeMillis() - _statsStartedAt.get();
473     }
474 
475     /**
476      * @return the total bytes of content sent in responses
477      */
478     @ManagedAttribute("total number of bytes across all responses")
479     public long getResponsesBytesTotal()
480     {
481         return _responsesTotalBytes.get();
482     }
483 
484     public String toStatsHTML()
485     {
486         StringBuilder sb = new StringBuilder();
487 
488         sb.append("<h1>Statistics:</h1>\n");
489         sb.append("Statistics gathering started ").append(getStatsOnMs()).append("ms ago").append("<br />\n");
490 
491         sb.append("<h2>Requests:</h2>\n");
492         sb.append("Total requests: ").append(getRequests()).append("<br />\n");
493         sb.append("Active requests: ").append(getRequestsActive()).append("<br />\n");
494         sb.append("Max active requests: ").append(getRequestsActiveMax()).append("<br />\n");
495         sb.append("Total requests time: ").append(getRequestTimeTotal()).append("<br />\n");
496         sb.append("Mean request time: ").append(getRequestTimeMean()).append("<br />\n");
497         sb.append("Max request time: ").append(getRequestTimeMax()).append("<br />\n");
498         sb.append("Request time standard deviation: ").append(getRequestTimeStdDev()).append("<br />\n");
499 
500 
501         sb.append("<h2>Dispatches:</h2>\n");
502         sb.append("Total dispatched: ").append(getDispatched()).append("<br />\n");
503         sb.append("Active dispatched: ").append(getDispatchedActive()).append("<br />\n");
504         sb.append("Max active dispatched: ").append(getDispatchedActiveMax()).append("<br />\n");
505         sb.append("Total dispatched time: ").append(getDispatchedTimeTotal()).append("<br />\n");
506         sb.append("Mean dispatched time: ").append(getDispatchedTimeMean()).append("<br />\n");
507         sb.append("Max dispatched time: ").append(getDispatchedTimeMax()).append("<br />\n");
508         sb.append("Dispatched time standard deviation: ").append(getDispatchedTimeStdDev()).append("<br />\n");
509 
510 
511         sb.append("Total requests suspended: ").append(getAsyncRequests()).append("<br />\n");
512         sb.append("Total requests expired: ").append(getExpires()).append("<br />\n");
513         sb.append("Total requests resumed: ").append(getAsyncDispatches()).append("<br />\n");
514 
515         sb.append("<h2>Responses:</h2>\n");
516         sb.append("1xx responses: ").append(getResponses1xx()).append("<br />\n");
517         sb.append("2xx responses: ").append(getResponses2xx()).append("<br />\n");
518         sb.append("3xx responses: ").append(getResponses3xx()).append("<br />\n");
519         sb.append("4xx responses: ").append(getResponses4xx()).append("<br />\n");
520         sb.append("5xx responses: ").append(getResponses5xx()).append("<br />\n");
521         sb.append("Bytes sent total: ").append(getResponsesBytesTotal()).append("<br />\n");
522 
523         return sb.toString();
524 
525     }
526 }