1 //
2 //  ========================================================================
3 //  Copyright (c) 1995-2014 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      */
statsReset()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
handle(String path, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)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 
updateResponse(Request request)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
doStart()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      */
getRequests()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      */
getRequestsActive()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      */
getRequestsActiveMax()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      */
getRequestTimeMax()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      */
getRequestTimeTotal()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      */
getRequestTimeMean()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      */
getRequestTimeStdDev()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      */
getDispatched()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      */
getDispatchedActive()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      */
getDispatchedActiveMax()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      */
getDispatchedTimeMax()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      */
getDispatchedTimeTotal()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      */
getDispatchedTimeMean()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      */
getDispatchedTimeStdDev()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      */
getSuspends()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      */
getSuspendsActive()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      */
getSuspendsActiveMax()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      */
getResumes()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      */
getExpires()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      */
getResponses1xx()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      */
getResponses2xx()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      */
getResponses3xx()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      */
getResponses4xx()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      */
getResponses5xx()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      */
getStatsOnMs()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      */
getResponsesBytesTotal()427     public long getResponsesBytesTotal()
428     {
429         return _responsesTotalBytes.get();
430     }
431 
toStatsHTML()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 }
475