View Javadoc

1   // ========================================================================
2   // Copyright (c) 2006-2009 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // All rights reserved. This program and the accompanying materials
5   // are made available under the terms of the Eclipse Public License v1.0
6   // and Apache License v2.0 which accompanies this distribution.
7   // The Eclipse Public License is available at
8   // http://www.eclipse.org/legal/epl-v10.html
9   // The Apache License v2.0 is available at
10  // http://www.opensource.org/licenses/apache2.0.php
11  // You may elect to redistribute this code under either of these licenses.
12  // ========================================================================
13  
14  package org.eclipse.jetty.server.handler;
15  
16  import java.io.IOException;
17  import java.util.concurrent.atomic.AtomicInteger;
18  import java.util.concurrent.atomic.AtomicLong;
19  import javax.servlet.ServletException;
20  import javax.servlet.http.HttpServletRequest;
21  import javax.servlet.http.HttpServletResponse;
22  
23  import org.eclipse.jetty.server.AsyncContinuation;
24  import org.eclipse.jetty.server.Request;
25  import org.eclipse.jetty.server.Response;
26  
27  public class StatisticsHandler extends HandlerWrapper
28  {
29      private transient final AtomicLong _statsStartedAt = new AtomicLong();
30      private transient final AtomicInteger _requests = new AtomicInteger();
31      private transient final AtomicInteger _resumedRequests = new AtomicInteger();
32      private transient final AtomicInteger _expiredRequests = new AtomicInteger();
33      private transient final AtomicLong _requestMinTime = new AtomicLong();
34      private transient final AtomicLong _requestMaxTime = new AtomicLong();
35      private transient final AtomicLong _requestTotalTime = new AtomicLong();
36      private transient final AtomicLong _suspendMinTime = new AtomicLong();
37      private transient final AtomicLong _suspendTotalTime = new AtomicLong();
38      private transient final AtomicInteger _requestsActive = new AtomicInteger();
39      private transient final AtomicInteger _requestsMaxActive = new AtomicInteger();
40      private transient final AtomicInteger _responses1xx = new AtomicInteger();
41      private transient final AtomicInteger _responses2xx = new AtomicInteger();
42      private transient final AtomicInteger _responses3xx = new AtomicInteger();
43      private transient final AtomicInteger _responses4xx = new AtomicInteger();
44      private transient final AtomicInteger _responses5xx = new AtomicInteger();
45      private transient final AtomicLong _responsesTotalBytes = new AtomicLong();
46  
47      /**
48       * Resets the current request statistics.
49       */
50      public void statsReset()
51      {
52          _statsStartedAt.set(System.currentTimeMillis());
53          _requests.set(0);
54          _resumedRequests.set(0);
55          _expiredRequests.set(0);
56          _requestMinTime.set(Long.MAX_VALUE);
57          _requestMaxTime.set(0L);
58          _requestTotalTime.set(0L);
59          _suspendMinTime.set(Long.MAX_VALUE);
60          _suspendTotalTime.set(0L);
61          _requestsActive.set(0);
62          _requestsMaxActive.set(0);
63          _responses1xx.set(0);
64          _responses2xx.set(0);
65          _responses3xx.set(0);
66          _responses4xx.set(0);
67          _responses5xx.set(0);
68          _responsesTotalBytes.set(0L);
69      }
70  
71      private void updateMax(AtomicInteger valueHolder, int value)
72      {
73          int oldValue = valueHolder.get();
74          while (value > oldValue)
75          {
76              if (valueHolder.compareAndSet(oldValue, value))
77                  break;
78              oldValue = valueHolder.get();
79          }
80      }
81  
82      private void updateMax(AtomicLong valueHolder, long value)
83      {
84          long oldValue = valueHolder.get();
85          while (value > oldValue)
86          {
87              if (valueHolder.compareAndSet(oldValue, value))
88                  break;
89              oldValue = valueHolder.get();
90          }
91      }
92  
93      private void updateMin(AtomicLong valueHolder, long value)
94      {
95          long oldValue = valueHolder.get();
96          while (value < oldValue)
97          {
98              if (valueHolder.compareAndSet(oldValue, value))
99                  break;
100             oldValue = valueHolder.get();
101         }
102     }
103 
104     public void handle(String path, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException
105     {
106         _requests.incrementAndGet();
107 
108         int activeRequests = _requestsActive.incrementAndGet();
109         updateMax(_requestsMaxActive, activeRequests);
110 
111         // The order of the ifs is important, as a continuation can be resumed and expired
112         // We test first if it's expired, and then if it's resumed
113         AsyncContinuation continuation = request.getAsyncContinuation();
114         if (continuation.isExpired())
115         {
116             _expiredRequests.incrementAndGet();
117         }
118         else if (continuation.isResumed())
119         {
120             _resumedRequests.incrementAndGet();
121 
122             long initialTime = request.getTimeStamp();
123             long suspendTime = System.currentTimeMillis() - initialTime;
124             updateMin(_suspendMinTime, suspendTime);
125             _suspendTotalTime.addAndGet(suspendTime);
126         }
127 
128         try
129         {
130             super.handle(path, request, httpRequest, httpResponse);
131         }
132         finally
133         {
134             _requestsActive.decrementAndGet();
135 
136             if (!continuation.isSuspended())
137             {
138                 updateResponse(request);
139             }
140         }
141     }
142 
143     private void updateResponse(Request request)
144     {
145         long elapsed = System.currentTimeMillis() - request.getTimeStamp();
146 
147         updateMin(_requestMinTime, elapsed);
148         updateMax(_requestMaxTime, elapsed);
149         _requestTotalTime.addAndGet(elapsed);
150 
151         Response response = request.getResponse();
152         switch (response.getStatus() / 100)
153         {
154             case 1:
155                 _responses1xx.incrementAndGet();
156                 break;
157             case 2:
158                 _responses2xx.incrementAndGet();
159                 break;
160             case 3:
161                 _responses3xx.incrementAndGet();
162                 break;
163             case 4:
164                 _responses4xx.incrementAndGet();
165                 break;
166             case 5:
167                 _responses5xx.incrementAndGet();
168                 break;
169             default:
170                 break;
171         }
172 
173         _responsesTotalBytes.addAndGet(response.getContentCount());
174     }
175 
176     protected void doStart() throws Exception
177     {
178         super.doStart();
179         statsReset();
180     }
181 
182     /**
183      * @return the number of requests handled by this handler
184      * since {@link #statsReset()} was last called, including
185      * resumed requests
186      * @see #getRequestsResumed()
187      */
188     public int getRequests()
189     {
190         return _requests.get();
191     }
192 
193     /**
194      * @return the number of requests currently active.
195      * since {@link #statsReset()} was last called.
196      */
197     public int getRequestsActive()
198     {
199         return _requestsActive.get();
200     }
201 
202     /**
203      * @return the maximum number of active requests
204      * since {@link #statsReset()} was last called.
205      */
206     public int getRequestsActiveMax()
207     {
208         return _requestsMaxActive.get();
209     }
210 
211     /**
212      * @return the number of requests that have been resumed
213      * @see #getRequestsExpired()
214      */
215     public int getRequestsResumed()
216     {
217         return _resumedRequests.get();
218     }
219 
220     /**
221      * @return the number of requests that expired while suspended.
222      * @see #getRequestsResumed()
223      */
224     public int getRequestsExpired()
225     {
226         return _expiredRequests.get();
227     }
228 
229     /**
230      * @return the number of responses with a 1xx status returned by this context
231      * since {@link #statsReset()} was last called.
232      */
233     public int getResponses1xx()
234     {
235         return _responses1xx.get();
236     }
237 
238     /**
239      * @return the number of responses with a 2xx status returned by this context
240      * since {@link #statsReset()} was last called.
241      */
242     public int getResponses2xx()
243     {
244         return _responses2xx.get();
245     }
246 
247     /**
248      * @return the number of responses with a 3xx status returned by this context
249      * since {@link #statsReset()} was last called.
250      */
251     public int getResponses3xx()
252     {
253         return _responses3xx.get();
254     }
255 
256     /**
257      * @return the number of responses with a 4xx status returned by this context
258      * since {@link #statsReset()} was last called.
259      */
260     public int getResponses4xx()
261     {
262         return _responses4xx.get();
263     }
264 
265     /**
266      * @return the number of responses with a 5xx status returned by this context
267      * since {@link #statsReset()} was last called.
268      */
269     public int getResponses5xx()
270     {
271         return _responses5xx.get();
272     }
273 
274     /**
275      * @return the milliseconds since the statistics were started with {@link #statsReset()}.
276      */
277     public long getStatsOnMs()
278     {
279         return System.currentTimeMillis() - _statsStartedAt.get();
280     }
281 
282     /**
283      * @return the minimum time (in milliseconds) of request handling
284      * since {@link #statsReset()} was last called.
285      */
286     public long getRequestTimeMin()
287     {
288         return _requestMinTime.get();
289     }
290 
291     /**
292      * @return the maximum time (in milliseconds) of request handling
293      * since {@link #statsReset()} was last called.
294      */
295     public long getRequestTimeMax()
296     {
297         return _requestMaxTime.get();
298     }
299 
300     /**
301      * @return the total time (in milliseconds) of requests handling
302      * since {@link #statsReset()} was last called.
303      */
304     public long getRequestTimeTotal()
305     {
306         return _requestTotalTime.get();
307     }
308 
309     /**
310      * @return the average time (in milliseconds) of request handling
311      * since {@link #statsReset()} was last called.
312      * @see #getRequestTimeTotal()
313      * @see #getRequests()
314      */
315     public long getRequestTimeAverage()
316     {
317         int requests = getRequests();
318         return requests == 0 ? 0 : getRequestTimeTotal() / requests;
319     }
320 
321     /**
322      * @return the total bytes of content sent in responses
323      */
324     public long getResponsesBytesTotal()
325     {
326         return _responsesTotalBytes.get();
327     }
328 
329     /**
330      * @return the minimum time (in milliseconds) of request suspension
331      * since {@link #statsReset()} was last called.
332      */
333     public long getSuspendedTimeMin()
334     {
335         return _suspendMinTime.get();
336     }
337 
338     /**
339      * @return the total time (in milliseconds) of request suspension
340      * since {@link #statsReset()} was last called.
341      */
342     public long getSuspendedTimeTotal()
343     {
344         return _suspendTotalTime.get();
345     }
346 }