All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.http4s.server.jetty.InstrumentedHandler.scala Maven / Gradle / Ivy

/*
 * Licensed under the Apache License, Version 2.0
 * Copyright 2010-2012 Coda Hale and Yammer, Inc.
 * https://github.com/dropwizard/metrics/blob/v3.1.0/LICENSE
 */
package org.http4s.server.jetty

import com.codahale.metrics.Counter
import com.codahale.metrics.Meter
import com.codahale.metrics.MetricRegistry
import com.codahale.metrics.RatioGauge
import com.codahale.metrics.RatioGauge._
import com.codahale.metrics.Timer
import org.eclipse.jetty.http.HttpMethod
import org.eclipse.jetty.server.AsyncContextState
import org.eclipse.jetty.server.Handler
import org.eclipse.jetty.server.HttpChannelState
import org.eclipse.jetty.server.Request
import org.eclipse.jetty.server.handler.HandlerWrapper

import javax.servlet.AsyncEvent
import javax.servlet.AsyncListener
import javax.servlet.ServletException
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import java.io.IOException
import java.util.concurrent.TimeUnit
import HttpMethod._

import com.codahale.metrics.MetricRegistry.{name => registryName}
/**
 * A Jetty {@link Handler} which records various metrics about an underlying {@link Handler}
 * instance.
 *
 * See http4s/http4s#204.
 */
protected case class InstrumentedHandler(registry:MetricRegistry , _prefix:Option[String] = None) extends HandlerWrapper {

  var name:String                               = null

  private val registerTimer:String   => Timer   = name => registry.timer(registryName(prefix,   name))
  private val registerMeter:String   => Meter   = name => registry.meter(registryName(prefix,   name))
  private val registerCounter:String => Counter = name => registry.counter(registryName(prefix,   name))
  private val listener:AsyncListener            = new AsyncListener() {
            def onTimeout(event:AsyncEvent )   = asyncTimeouts.mark()
            def onStartAsync(event:AsyncEvent) = event.getAsyncContext().addListener(this)
            def onError(event:AsyncEvent )     = ()
            def onComplete(event:AsyncEvent)   = {
                val state:AsyncContextState =  event.getAsyncContext().asInstanceOf[AsyncContextState]
                val request:Request =  state.getRequest().asInstanceOf[Request]
                updateResponses(request)
                if (state.getHttpChannelState().getState() != HttpChannelState.State.DISPATCHED) {
                    activeSuspended.dec()
                }
            }
        }

  private lazy val prefix            = _prefix.fold(registryName(getHandler().getClass(), name) )(p => registryName(p, name))
  // the requests handled by this handler, excluding active
  private lazy val requests          = registerTimer("requests")
  // the number of dispatches seen by this handler, excluding active
  private lazy val  dispatches       = registerTimer("dispatches")
  // the number of active requests
  private lazy val  activeRequests   = registerCounter("active-requests")
  // the number of active dispatches
  private lazy val  activeDispatches = registerCounter("active-dispatches")
  // the number of requests currently suspended.
  private lazy val  activeSuspended  = registerCounter("active-suspended")
  // the number of requests that have been asynchronously dispatched
  private lazy val  asyncDispatches  = registerMeter("async-dispatches")
  // the number of requests that expired while suspended
  private lazy val  asyncTimeouts    = registerMeter(   "async-timeouts")
  private lazy val  responses        = Array[Meter](
                                        registerMeter( "1xx-responses"), // 1xx
                                        registerMeter( "2xx-responses"), // 2xx
                                        registerMeter( "3xx-responses"), // 3xx
                                        registerMeter( "4xx-responses"), // 4xx
                                        registerMeter( "5xx-responses")) // 5xx
  private lazy val  getRequests      = registerTimer("get-requests")
  private lazy val  postRequests     = registerTimer( "post-requests")
  private lazy val  headRequests     = registerTimer( "head-requests")
  private lazy val  putRequests      = registerTimer( "put-requests")
  private lazy val  deleteRequests   = registerTimer( "delete-requests")
  private lazy val  optionsRequests  = registerTimer( "options-requests")
  private lazy val  traceRequests    = registerTimer( "trace-requests")
  private lazy val  connectRequests  = registerTimer( "connect-requests")
  private lazy val  moveRequests     = registerTimer( "move-requests")
  private lazy val  otherRequests    = registerTimer( "other-requests")

  override protected def doStart  {
      super.doStart()
      registry.register(registryName(prefix, "percent-4xx-1m"), new RatioGauge() {
          protected def getRatio() = {
            Ratio.of(responses(3).getOneMinuteRate(),
                     requests.getOneMinuteRate())
          }
      })
      registry.register(registryName(prefix, "percent-4xx-5m"), new RatioGauge() {
          protected def getRatio() =
            Ratio.of(responses(3).getFiveMinuteRate(),requests.getFiveMinuteRate())
      })
      registry.register(registryName(prefix, "percent-4xx-15m"), new RatioGauge() {
          protected def getRatio() =
            Ratio.of(responses(3).getFifteenMinuteRate(),requests.getFifteenMinuteRate())

      })
      registry.register(registryName(prefix, "percent-5xx-1m"), new RatioGauge() {
          protected def getRatio() =
            Ratio.of(responses(4).getOneMinuteRate(),requests.getOneMinuteRate())

      })
      registry.register(registryName(prefix, "percent-5xx-5m"), new RatioGauge() {
          protected def getRatio() =
            Ratio.of(responses(4).getFiveMinuteRate(),requests.getFiveMinuteRate())
      })
      registry.register(registryName(prefix, "percent-5xx-15m"), new RatioGauge() {
          protected def getRatio() =
            Ratio.of(responses(4).getFifteenMinuteRate(),requests.getFifteenMinuteRate())
      })
  }

    override def handle(path:String,
                        request:Request,
                        httpRequest:HttpServletRequest,
                        httpResponse:HttpServletResponse):Unit = {
        activeDispatches.inc()
        val state = request.getHttpChannelState()
        val start = if (state.isInitial()) {
            // new request
            activeRequests.inc()
            request.getTimeStamp()
        } else {
            // resumed request
            activeSuspended.dec()
            if (state.getState() == HttpChannelState.State.DISPATCHED) {
                asyncDispatches.mark()
            }
            System.currentTimeMillis()
        }

        try {
            super.handle(path, request, httpRequest, httpResponse)
        } finally {
            val now = System.currentTimeMillis()
            val dispatched = now - start

            activeDispatches.dec()
            dispatches.update(dispatched, TimeUnit.MILLISECONDS)

            if (state.isSuspended()) {
                if (state.isInitial()) {
                    state.addListener(listener)
                }
                activeSuspended.inc()
            } else if (state.isInitial()) {
                updateResponses(request)
            }
            // else onCompletion will handle it.
        }
    }

    private def requestTimer( method:String) = HttpMethod.fromString(method) match  {
      case GET     => getRequests
      case POST    => postRequests
      case PUT     => putRequests
      case HEAD    => headRequests
      case DELETE  => deleteRequests
      case OPTIONS => optionsRequests
      case TRACE   => traceRequests
      case CONNECT => connectRequests
      case MOVE    => moveRequests
      case default => otherRequests
    }

  private def updateResponses(request:Request) = {
      val response = request.getResponse().getStatus() / 100
      if (response >= 1 && response <= 5) {
          responses(response - 1).mark()
      }
      activeRequests.dec()
        val elapsedTime = System.currentTimeMillis() - request.getTimeStamp()
        requests.update(elapsedTime, TimeUnit.MILLISECONDS)
        requestTimer(request.getMethod()).update(elapsedTime, TimeUnit.MILLISECONDS)
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy