2014-12-23 23 views
6

ich nach einem Weg suchen, um die Antworttext im Play-Rahmen zu drucken, habe ich einen Code wie folgt:Play/Protokollierung/Drucken Antwort Body/enumerator Lauft/puffern den Körper

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 
    } 
} 

Derzeit ist die Code kommentiert-out funktioniert nicht, wie ich wollte, ich meine, es wäre drucken:

Response: 
id=1 
[email protected] 

Also, ich brauche einen Weg zu finden, einen String aus Enumerator [Array [Byte]] zu erhalten. Ich habe versucht, das Konzept des Enumerator zu begreifen durch das Lese: http://mandubian.com/2012/08/27/understanding-play2-iteratees-for-normal-humans/

So ..., wenn ich es richtig verstehen:

  1. ich soll nicht trocken-up des Enumerator im Prozess der Umwandlung zu Schnur. Andernfalls würde der Client nichts erhalten.

  2. Nehmen wir an, ich finde heraus, wie man den T/Filter-Mechanismus implementiert. Aber dann ... würde es den Zweck des Play-Frameworks nicht als blockierungsfreies Streaming-Framework vereiteln (weil ich das komplette Array von Bytes im Speicher aufbauen würde, bevor ich toString darauf anrufe und es schließlich aufzeichne)?

Also, was ist der richtige Weg, um die Antwort zu protokollieren?

Vielen Dank im Voraus, Raka

+0

Sie sollten die Antwort auf eine Weise protokollieren, die auch streamt. Z.B. 'System.out' ist ein' OutputStream', Sie können sich dort streamingweise anmelden (obwohl Sie möglicherweise zwei verschachtelte Antworten erhalten). Wenn Sie sich bei einer Datenbank anmelden, können Sie dorthin streamen. Und so weiter. – lmm

+0

Ich glaube, ich fand die Antwort hier: http://stackoverflow.com/questions/17752151/scala-play-2-1-accessing-request-and-response-bodies-in-a-filter –

Antwort

3

Dieser Code funktioniert:

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) 
    } 
    } 
} 

ich lernte es teilweise von hier (wie aus enumerator das Byte-Array zu erhalten): Scala Play 2.1: Accessing request and response bodies in a filter.

Ich verwende Play 2.3.7, während der Link, den ich gab, 2.1 verwendet (und immer noch PlainResult verwendet, das in 2.3 nicht mehr existiert).

1

Ich habe die obige Antwort als Ausgangspunkt verwendet, aber festgestellt, dass es Antworten nur protokolliert, wenn ein Körper vorhanden ist. Wir haben es so weit angepasst:

var responseBody = None:Option[String] 
val captureBody = Enumeratee.map[Array[Byte]](arrOfBytes => { 
    val body = new String(arrOfBytes.map(_.toChar)) 
    responseBody = Some(body) 
    arrOfBytes 
}) 
val withLogging = (result.body &> captureBody).onDoneEnumerating({ 
    logger.debug(.. create message here ..) 
}) 
result.copy(body=withLogging) 
2

Wie es mir scheint, wenn Sie in result.body &> Enumeratee.map Sie Logging (wie in https://stackoverflow.com/a/27630208/1781549 vorgeschlagen) und das Ergebnis Körper in mehr als ein Klumpen vorgestellt, dann wird jeder Brocken sein unabhängig protokolliert. Das willst du wahrscheinlich nicht.

ich es so umsetzen würde:

val ret = block(request).flatMap { result => 
    val consume = Iteratee.consume[Array[Byte]]() 
    val bodyF = Iteratee.flatten(result.body(consume)).run 
    bodyF.map { bodyBytes: Array[Byte] => 
    // 
    // Log the body 
    // 

    result.copy(body = Enumerator(bodyBytes)) 
    } 
} 

Aber seien Sie gewarnt: die ganze Idee von all dem die Daten aus dem result.body Enumerator vor der Anmeldung zu konsumieren ist (und gibt den neuen Enumerator). Also, wenn die Antwort groß ist, oder Sie sich auf Streaming verlassen, dann ist es wahrscheinlich auch das, was Sie nicht wollen.