Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Play / Logging / Print Response Body / Run over enumerator / buffer the body

I'm looking for a way to print the response body in Play framework, I have a code like this:

object AccessLoggingAction extends ActionBuilder[Request] {
  def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = {
    Logger.info(s"""Request:
      id=${request.id} 
      method=${request.method} 
      uri=${request.uri} 
      remote-address=${request.remoteAddress} 
      body=${request.body}
    """)
    val ret = block(request)
    /*
    ret.map {result =>
      Logger.info(s"""Response:
      id=${request.id} 
      body=${result.body}
      """)
    }
    */ //TODO: find out how to print result.body (be careful not to consume the enumerator)
    ret
  }
}

Currently the commented-out code is not working as I wanted, I mean, it would print:

Response:
id=1
body=play.api.libs.iteratee.Enumerator$$anon$18@39e6c1a2

So, I need to find a way to get a String out of Enumerator[Array[Byte]]. I tried to grasp the concept of Enumerator by reading this: http://mandubian.com/2012/08/27/understanding-play2-iteratees-for-normal-humans/

So..., if I understand it correctly:

  1. I shouldn't dry-up the enumerator in the process of converting it to String. Otherwise, the client would receive nothing.

  2. Let's suppose I figure out how to implement the T / filter mechanism. But then... wouldn't it defeat the purpose of Play framework as non-blocking streaming framework (because I would be building up the complete array of bytes in the memory, before calling toString on it, and finally log it)?

So, what's the correct way to log the response?

Thanks in advance, Raka

like image 321
Cokorda Raka Avatar asked Dec 23 '14 00:12

Cokorda Raka


1 Answers

This code works:

object AccessLoggingAction extends ActionBuilder[Request] {
  def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = {
    val start = System.currentTimeMillis

    Logger.info(s"""Request:
      id=${request.id} 
      method=${request.method} 
      uri=${request.uri} 
      remote-address=${request.remoteAddress} 
      body=${request.body}
    """)

    val resultFut = block(request)

    resultFut.map {result =>
      val time = System.currentTimeMillis - start
      Result(result.header, result.body &> Enumeratee.map(arrOfBytes => {
        val body = new String(arrOfBytes.map(_.toChar))
        Logger.info(s"""Response:
      id=${request.id} 
      method=${request.method}
      uri=${request.uri}
      delay=${time}ms 
      status=${result.header.status}
      body=${body}""")
        arrOfBytes
      }), result.connection)
    }
  }
}

I partly learned it from here (on how to get the byte array out of enumerator): Scala Play 2.1: Accessing request and response bodies in a filter.

I'm using Play 2.3.7 while the link I gave uses 2.1 (and still uses PlainResult, which no longer exists in 2.3).

like image 160
Cokorda Raka Avatar answered Oct 12 '22 11:10

Cokorda Raka