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.ServletException;
26  import javax.servlet.http.HttpServletRequest;
27  import javax.servlet.http.HttpServletResponse;
28  
29  import org.eclipse.jetty.continuation.Continuation;
30  import org.eclipse.jetty.continuation.ContinuationListener;
31  import org.eclipse.jetty.server.AsyncContinuation;
32  import org.eclipse.jetty.server.Request;
33  import org.eclipse.jetty.server.Response;
34  import org.eclipse.jetty.util.statistic.CounterStatistic;
35  import org.eclipse.jetty.util.statistic.SampleStatistic;
36  
37  public class StatisticsHandler extends HandlerWrapper
38  {
39      private final AtomicLong _statsStartedAt = new AtomicLong();
40      
41      private final CounterStatistic _requestStats = new CounterStatistic();
42      private final SampleStatistic _requestTimeStats = new SampleStatistic();
43      private final CounterStatistic _dispatchedStats = new CounterStatistic();
44      private final SampleStatistic _dispatchedTimeStats = new SampleStatistic();
45      private final CounterStatistic _suspendStats = new CounterStatistic();
46  
47      private final AtomicInteger _resumes = new AtomicInteger();
48      private final AtomicInteger _expires = new AtomicInteger();
49      
50      private final AtomicInteger _responses1xx = new AtomicInteger();
51      private final AtomicInteger _responses2xx = new AtomicInteger();
52      private final AtomicInteger _responses3xx = new AtomicInteger();
53      private final AtomicInteger _responses4xx = new AtomicInteger();
54      private final AtomicInteger _responses5xx = new AtomicInteger();
55      private final AtomicLong _responsesTotalBytes = new AtomicLong();
56  
57      private final ContinuationListener _onCompletion = new ContinuationListener()
58      {
59          public void onComplete(Continuation continuation)
60          {
61              final Request request = ((AsyncContinuation)continuation).getBaseRequest();
62              final long elapsed = System.currentTimeMillis()-request.getTimeStamp();
63              
64              _requestStats.decrement();
65              _requestTimeStats.set(elapsed);
66              
67              updateResponse(request);
68              
69              if (!continuation.isResumed())
70                  _suspendStats.decrement();
71          }
72  
73          public void onTimeout(Continuation continuation)
74          {
75              _expires.incrementAndGet();
76          }
77      };
78      
79      /**
80       * Resets the current request statistics.
81       */
82      public void statsReset()
83      {
84          _statsStartedAt.set(System.currentTimeMillis());
85          
86          _requestStats.reset();
87          _requestTimeStats.reset();
88          _dispatchedStats.reset();
89          _dispatchedTimeStats.reset();
90          _suspendStats.reset();
91  
92          _resumes.set(0);
93          _expires.set(0);
94          _responses1xx.set(0);
95          _responses2xx.set(0);
96          _responses3xx.set(0);
97          _responses4xx.set(0);
98          _responses5xx.set(0);
99          _responsesTotalBytes.set(0L);
100     }
101 
102     @Override
103     public void handle(String path, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException
104     {
105         _dispatchedStats.increment();
106 
107         final long start;
108         AsyncContinuation continuation = request.getAsyncContinuation();
109         if (continuation.isInitial())
110         {
111             // new request
112             _requestStats.increment();
113             start = request.getTimeStamp();
114         }
115         else
116         {
117             // resumed request
118             start = System.currentTimeMillis();
119             _suspendStats.decrement();
120             if (continuation.isResumed())
121                 _resumes.incrementAndGet();
122         }
123 
124         try
125         {
126             super.handle(path, request, httpRequest, httpResponse);
127         }
128         finally
129         {
130             final long now = System.currentTimeMillis();
131             final long dispatched=now-start;
132             
133             _dispatchedStats.decrement();
134             _dispatchedTimeStats.set(dispatched);
135             
136             if (continuation.isSuspended())
137             {
138                 if (continuation.isInitial())
139                     continuation.addContinuationListener(_onCompletion);
140                 _suspendStats.increment();
141             }
142             else if (continuation.isInitial())
143             {
144                 _requestStats.decrement();
145                 _requestTimeStats.set(dispatched);
146                 updateResponse(request);
147             }
148             // else onCompletion will handle it.
149         }
150     }
151 
152     private void updateResponse(Request request)
153     {
154         Response response = request.getResponse();
155         switch (response.getStatus() / 100)
156         {
157             case 1:
158                 _responses1xx.incrementAndGet();
159                 break;
160             case 2:
161                 _responses2xx.incrementAndGet();
162                 break;
163             case 3:
164                 _responses3xx.incrementAndGet();
165                 break;
166             case 4:
167                 _responses4xx.incrementAndGet();
168                 break;
169             case 5:
170                 _responses5xx.incrementAndGet();
171                 break;
172             default:
173                 break;
174         }
175         _responsesTotalBytes.addAndGet(response.getContentCount());
176     }
177 
178     @Override
179     protected void doStart() throws Exception
180     {
181         super.doStart();
182         statsReset();
183     }
184 
185     /**
186      * @return the number of requests handled by this handler
187      * since {@link #statsReset()} was last called, excluding
188      * active requests
189      * @see #getResumes()
190      */
191     public int getRequests()
192     {
193         return (int)_requestStats.getTotal();
194     }
195 
196     /**
197      * @return the number of requests currently active.
198      * since {@link #statsReset()} was last called.
199      */
200     public int getRequestsActive()
201     {
202         return (int)_requestStats.getCurrent();
203     }
204 
205     /**
206      * @return the maximum number of active requests
207      * since {@link #statsReset()} was last called.
208      */
209     public int getRequestsActiveMax()
210     {
211         return (int)_requestStats.getMax();
212     }
213 
214     /**
215      * @return the maximum time (in milliseconds) of request handling
216      * since {@link #statsReset()} was last called.
217      */
218     public long getRequestTimeMax()
219     {
220         return _requestTimeStats.getMax();
221     }
222 
223     /**
224      * @return the total time (in milliseconds) of requests handling
225      * since {@link #statsReset()} was last called.
226      */
227     public long getRequestTimeTotal()
228     {
229         return _requestTimeStats.getTotal();
230     }
231 
232     /**
233      * @return the mean time (in milliseconds) of request handling
234      * since {@link #statsReset()} was last called.
235      * @see #getRequestTimeTotal()
236      * @see #getRequests()
237      */
238     public double getRequestTimeMean()
239     {
240         return _requestTimeStats.getMean();
241     }
242 
243     /**
244      * @return the standard deviation of time (in milliseconds) of request handling
245      * since {@link #statsReset()} was last called.
246      * @see #getRequestTimeTotal()
247      * @see #getRequests()
248      */
249     public double getRequestTimeStdDev()
250     {
251         return _requestTimeStats.getStdDev();
252     }
253 
254     /**
255      * @return the number of dispatches seen by this handler
256      * since {@link #statsReset()} was last called, excluding
257      * active dispatches
258      */
259     public int getDispatched()
260     {
261         return (int)_dispatchedStats.getTotal();
262     }
263 
264     /**
265      * @return the number of dispatches currently in this handler
266      * since {@link #statsReset()} was last called, including
267      * resumed requests
268      */
269     public int getDispatchedActive()
270     {
271         return (int)_dispatchedStats.getCurrent();
272     }
273 
274     /**
275      * @return the max number of dispatches currently in this handler
276      * since {@link #statsReset()} was last called, including
277      * resumed requests
278      */
279     public int getDispatchedActiveMax()
280     {
281         return (int)_dispatchedStats.getMax();
282     }
283 
284     /**
285      * @return the maximum time (in milliseconds) of request dispatch
286      * since {@link #statsReset()} was last called.
287      */
288     public long getDispatchedTimeMax()
289     {
290         return _dispatchedTimeStats.getMax();
291     }
292     
293     /**
294      * @return the total time (in milliseconds) of requests handling
295      * since {@link #statsReset()} was last called.
296      */
297     public long getDispatchedTimeTotal()
298     {
299         return _dispatchedTimeStats.getTotal();
300     }
301 
302     /**
303      * @return the mean time (in milliseconds) of request handling
304      * since {@link #statsReset()} was last called.
305      * @see #getRequestTimeTotal()
306      * @see #getRequests()
307      */
308     public double getDispatchedTimeMean()
309     {
310         return _dispatchedTimeStats.getMean();
311     }
312     
313     /**
314      * @return the standard deviation of time (in milliseconds) of request handling
315      * since {@link #statsReset()} was last called.
316      * @see #getRequestTimeTotal()
317      * @see #getRequests()
318      */
319     public double getDispatchedTimeStdDev()
320     {
321         return _dispatchedTimeStats.getStdDev();
322     }
323     
324     /**
325      * @return the number of requests handled by this handler
326      * since {@link #statsReset()} was last called, including
327      * resumed requests
328      * @see #getResumes()
329      */
330     public int getSuspends()
331     {
332         return (int)_suspendStats.getTotal();
333     }
334 
335     /**
336      * @return the number of requests currently suspended.
337      * since {@link #statsReset()} was last called.
338      */
339     public int getSuspendsActive()
340     {
341         return (int)_suspendStats.getCurrent();
342     }
343 
344     /**
345      * @return the maximum number of current suspended requests
346      * since {@link #statsReset()} was last called.
347      */
348     public int getSuspendsActiveMax()
349     {
350         return (int)_suspendStats.getMax();
351     }
352     
353     /**
354      * @return the number of requests that have been resumed
355      * @see #getExpires()
356      */
357     public int getResumes()
358     {
359         return _resumes.get();
360     }
361 
362     /**
363      * @return the number of requests that expired while suspended.
364      * @see #getResumes()
365      */
366     public int getExpires()
367     {
368         return _expires.get();
369     }
370 
371     /**
372      * @return the number of responses with a 1xx status returned by this context
373      * since {@link #statsReset()} was last called.
374      */
375     public int getResponses1xx()
376     {
377         return _responses1xx.get();
378     }
379 
380     /**
381      * @return the number of responses with a 2xx status returned by this context
382      * since {@link #statsReset()} was last called.
383      */
384     public int getResponses2xx()
385     {
386         return _responses2xx.get();
387     }
388 
389     /**
390      * @return the number of responses with a 3xx status returned by this context
391      * since {@link #statsReset()} was last called.
392      */
393     public int getResponses3xx()
394     {
395         return _responses3xx.get();
396     }
397 
398     /**
399      * @return the number of responses with a 4xx status returned by this context
400      * since {@link #statsReset()} was last called.
401      */
402     public int getResponses4xx()
403     {
404         return _responses4xx.get();
405     }
406 
407     /**
408      * @return the number of responses with a 5xx status returned by this context
409      * since {@link #statsReset()} was last called.
410      */
411     public int getResponses5xx()
412     {
413         return _responses5xx.get();
414     }
415 
416     /**
417      * @return the milliseconds since the statistics were started with {@link #statsReset()}.
418      */
419     public long getStatsOnMs()
420     {
421         return System.currentTimeMillis() - _statsStartedAt.get();
422     }
423     
424     /**
425      * @return the total bytes of content sent in responses
426      */
427     public long getResponsesBytesTotal()
428     {
429         return _responsesTotalBytes.get();
430     }
431     
432     public String toStatsHTML()
433     {   
434         StringBuilder sb = new StringBuilder();
435 
436         sb.append("<h1>Statistics:</h1>\n");
437         sb.append("Statistics gathering started ").append(getStatsOnMs()).append("ms ago").append("<br />\n");
438 
439         sb.append("<h2>Requests:</h2>\n");
440         sb.append("Total requests: ").append(getRequests()).append("<br />\n");
441         sb.append("Active requests: ").append(getRequestsActive()).append("<br />\n");
442         sb.append("Max active requests: ").append(getRequestsActiveMax()).append("<br />\n");
443         sb.append("Total requests time: ").append(getRequestTimeTotal()).append("<br />\n");
444         sb.append("Mean request time: ").append(getRequestTimeMean()).append("<br />\n");
445         sb.append("Max request time: ").append(getRequestTimeMax()).append("<br />\n");
446         sb.append("Request time standard deviation: ").append(getRequestTimeStdDev()).append("<br />\n");
447         
448 
449         sb.append("<h2>Dispatches:</h2>\n");
450         sb.append("Total dispatched: ").append(getDispatched()).append("<br />\n");
451         sb.append("Active dispatched: ").append(getDispatchedActive()).append("<br />\n");
452         sb.append("Max active dispatched: ").append(getDispatchedActiveMax()).append("<br />\n");
453         sb.append("Total dispatched time: ").append(getDispatchedTimeTotal()).append("<br />\n");
454         sb.append("Mean dispatched time: ").append(getDispatchedTimeMean()).append("<br />\n");
455         sb.append("Max dispatched time: ").append(getDispatchedTimeMax()).append("<br />\n");
456         sb.append("Dispatched time standard deviation: ").append(getDispatchedTimeStdDev()).append("<br />\n");
457 
458 
459         sb.append("Total requests suspended: ").append(getSuspends()).append("<br />\n");
460         sb.append("Total requests expired: ").append(getExpires()).append("<br />\n");
461         sb.append("Total requests resumed: ").append(getResumes()).append("<br />\n");
462         
463         sb.append("<h2>Responses:</h2>\n");
464         sb.append("1xx responses: ").append(getResponses1xx()).append("<br />\n");
465         sb.append("2xx responses: ").append(getResponses2xx()).append("<br />\n");
466         sb.append("3xx responses: ").append(getResponses3xx()).append("<br />\n");
467         sb.append("4xx responses: ").append(getResponses4xx()).append("<br />\n");
468         sb.append("5xx responses: ").append(getResponses5xx()).append("<br />\n");
469         sb.append("Bytes sent total: ").append(getResponsesBytesTotal()).append("<br />\n");
470 
471         return sb.toString();
472 
473     }
474 }