Monday, October 21, 2013

Liftweb 2.4 and Jamon 2.74 request monitoring

I like Jamon as a library for collecting performance statistics. It is well established and provides sufficient types of statistics. One of the favorites statistics of mine being the frequency distribution of response times. Using Jamon in simple but with Lift 2.4 it was little challenging. The Jamon MonitorFactory returns an instance of Timing Monitor that maintains the statistics. For the collection to occur the same instance must be accessed to call stop after method execution. i.e-
Monitor monitor = MonitorFactory.start("key1");
//Code whose performance needs to be measured

The problem with older version of Liftweb is there is no way of wrapping every request such that this monitor can be used. The S.addAround can be used with LoanWrapper but this gets executed multiple times as described here. As from the discussion from that thread you can see that a common LoanWrapper surrounding all requests would be (or has already been) added in Liftweb. But, if you can not upgrade the Liftweb to newer version then here is the solution. The idea is to use the LiftRules onBeginServicing and onEndServicing. But note that we need to use the same instance of Monitor created in onBeginServicing method. The request/response lifecycle in liftweb is described very well here. This will catch both Statefull GET and Ajax requests. The code below uses the setAttribute method of the original HHTPServletRequest to save and retrieve the Monitor between methods. Have fun with scala, liftweb and jamon!
def boot {
val labelPrefix: String = "LIFT_WEB_HTTP."
val monitorInReq: String = "monitorInReq"
val extensionsNotToMonitor = List(".jpg", ".png", ".jpeg", ".ico", ".gif", ".bmp")

def pathFilter = {
req: Req =>
req.path.wholePath.takeRight(1) match {
case x :: _ if (extensionsNotToMonitor.contains(x.substring(x.lastIndexOf(".")))) => None
case _ => Some(req.path.wholePath.mkString("/"))

LiftRules.onBeginServicing.append((req: Req) => {
val label: Option[String] = pathFilter(req)
if (label.isDefined) {
val start: Monitor = MonitorFactory.start(labelPrefix + label.get) => (r.asInstanceOf[HTTPRequestServlet]).req.setAttribute(monitorInReq, start))

LiftRules.onEndServicing.append((req, respBox) => { => {
val attribute: AnyRef = (r.asInstanceOf[HTTPRequestServlet]).req.getAttribute(monitorInReq)
if (null != attribute) {

No comments:

Post a Comment