Home | History | Annotate | Download | only in handler
      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      */
     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 }
    475