Log all requests in Play 2
Asked Answered
A

4

7

How can I log all requests coming in to a Play 2 application, in a similar way to how Apache logs (i.e. including the URL and client IP)?

Ariellearies answered 24/5, 2013 at 10:48 Comment(2)
That kind of log is called access log. And if play does not have an access log plugin, I would suggest fronting it with a web server that does.Myth
Unfortunately I don't have root access to install or configure Apache, and I just need a quick fix right now.Ariellearies
H
7

That's what http filters are for. Here are some detailed examples: http://www.playframework.com/documentation/2.1.1/ScalaHttpFilters

Hospitable answered 24/5, 2013 at 11:50 Comment(0)
P
10

I wanted to do the same thing with with Play 2.5.4. It took me a little while to put the pieces together, so I thought I'd share my steps in the hope that it'll save time for someone else:

  1. Make sure you have an access logger. There's an example of how to configure one at https://www.playframework.com/documentation/2.5.x/SettingsLogger but you might want to play with the settings; I put configured my logger with <file>${application.home:-.}/logs/access.log</file>, and <immediateFlush>true</immediateFlush>.

  2. Create a new RequestHandler in your root package, extending the default handler:

import javax.inject.Inject
import play.api.http._
import play.api.routing._
import play.api.mvc.RequestHeader
import play.api.Logger

/**
 * Implemented to get custom, apache-style logging of requests without dumping the full netty wire.
 */
class RequestHandler @Inject() (router: Router, errorHandler: HttpErrorHandler,
                                configuration: HttpConfiguration, filters: HttpFilters) extends DefaultHttpRequestHandler(
  router, errorHandler, configuration, filters) {

  override def routeRequest(request: RequestHeader) = {
    Logger("access").info(s"Request from ${request.remoteAddress}: ${request}")
    super.routeRequest(request)
  }
}

I was coming from Play 2.3, so I was originally planning to use GlobalSettings until I found this guide: https://www.playframework.com/documentation/2.5.x/GlobalSettings

Apologies to anyone who wants answers specific to Play 2.0, but seeing as my own 2.5-focused searches led me here, I figure this answer won't do much harm.

Proptosis answered 28/7, 2016 at 17:33 Comment(0)
H
7

That's what http filters are for. Here are some detailed examples: http://www.playframework.com/documentation/2.1.1/ScalaHttpFilters

Hospitable answered 24/5, 2013 at 11:50 Comment(0)
S
2

In Play 2.5.x, I used the following

import javax.inject.Inject
import akka.stream.Materializer
import play.api.Logger
import play.api.mvc._
import scala.concurrent.{ExecutionContext, Future}
import java.util.Calendar
import java.text.SimpleDateFormat

class ApacheLoggingFilter @Inject() (implicit val mat: Materializer, ec: ExecutionContext) extends Filter {

  def apply(nextFilter: RequestHeader => Future[Result])
           (requestHeader: RequestHeader): Future[Result] = {

    nextFilter(requestHeader).map { result =>

      val responseSize =  result.body.contentLength.getOrElse("-")
      Logger("access").info(s"""${requestHeader.remoteAddress} - - [${serverTime}] "${requestHeader}" ${result.header.status} ${responseSize}""")
      result
    }
  }

  private def serverTime = {
    val calendar = Calendar.getInstance()
    val dateFormat = new SimpleDateFormat(
      "dd/MMM/yyyy:HH:mm:ss Z")
    dateFormat.setTimeZone(calendar.getTimeZone)
    dateFormat.format(calendar.getTime())
  }
}

Make sure you configure this Filter correctly - https://www.playframework.com/documentation/2.5.x/ScalaHttpFilters#Using-filters

Skipper answered 22/6, 2017 at 17:55 Comment(0)
S
-1

Create your own Action is far more powerful and flexible.

object MyAction {
  def apply[A](bodyParser: BodyParser[A])(block: Request[A] => Result): Action[A] = Action(bodyParser) {
    request =>
      // TODO : authentication, cache logics here         

      // time it 
      val start = ...
      // process
      val r = block(request)
      val end = ...

      // log remote address, user agent, time, etc.

      r
  }

  // simply override to use MyAction
  def apply(block: Request[AnyContent] => Result): Action[AnyContent] = this.apply(BodyParsers.parse.anyContent)(block)

  // simply override to use MyAction
  def apply(block: => Result): Action[AnyContent] = this.apply(_ => block)
}

in order to use it in controller, simply replace Action with MyAction

def index = MyAction {
  implicit request =>
     // nothing to be changed here
}
Soleure answered 24/5, 2013 at 12:55 Comment(1)
...as well as more invasivePaterson

© 2022 - 2024 — McMap. All rights reserved.