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