Skip to content

Instantly share code, notes, and snippets.

@richdougherty
Created February 27, 2018 21:31
Show Gist options
  • Save richdougherty/91649b8efd6304376ed4187cc427a6d0 to your computer and use it in GitHub Desktop.
Save richdougherty/91649b8efd6304376ed4187cc427a6d0 to your computer and use it in GitHub Desktop.
Example of capturing logging output in Play Framework
/*
* Copyright (C) 2009-2017 Lightbend Inc. <https://www.lightbend.com>
*/
package play.it.http
import ch.qos.logback.classic.Logger
import ch.qos.logback.classic.spi.ILoggingEvent
import ch.qos.logback.core.AppenderBase
import org.slf4j.LoggerFactory
import org.specs2.execute.AsResult
import play.api.inject.guice.GuiceApplicationBuilder
import play.api.mvc._
import play.api.test._
import play.api.{ Configuration, Mode }
import play.core.server.ServerConfig
import play.it._
import scala.collection.mutable.ArrayBuffer
class NettyRequestHeadersSpec extends RequestHeadersSpec with NettyIntegrationSpecification
class AkkaHttpRequestHeadersSpec extends RequestHeadersSpec with AkkaHttpIntegrationSpecification
trait RequestHeadersSpec extends PlaySpecification with ServerIntegrationSpecification with HttpHeadersCommonSpec {
sequential
def withServerAndConfig[T](configuration: (String, Any)*)(action: (DefaultActionBuilder, PlayBodyParsers) => EssentialAction)(block: Port => T) = {
val port = testServerPort
val serverConfig: ServerConfig = {
val c = ServerConfig(port = Some(testServerPort), mode = Mode.Test)
c.copy(configuration = c.configuration ++ Configuration(configuration: _*))
}
running(play.api.test.TestServer(serverConfig, GuiceApplicationBuilder().appRoutes { app =>
val Action = app.injector.instanceOf[DefaultActionBuilder]
val parse = app.injector.instanceOf[PlayBodyParsers]
({
case _ => action(Action, parse)
})
}.build(), Some(integrationServerProvider))) {
block(port)
}
}
def withServer[T](action: (DefaultActionBuilder, PlayBodyParsers) => EssentialAction)(block: Port => T) = {
withServerAndConfig()(action)(block)
}
"Play request header handling" should {
"not complain about invalid User-Agent headers" in {
// This test modifies the global (!) logger to capture log messages.
// The test will not be reliable when run concurrently. However, since
// we're checking for the *absence* of log messages the worst thing
// that will happen is that the test will pass when it should fail. We
// should not get spurious failures which would cause our CI testing
// to fail. I think it's still worth including this test because it
// will still often report correct failures, even if it's not perfect.
import scala.collection.immutable
def recordLogEvents[T](block: => T): immutable.Seq[ILoggingEvent] = {
/** Collects all log events that occur */
class RecordingAppender extends AppenderBase[ILoggingEvent] {
private val eventBuffer = ArrayBuffer[ILoggingEvent]()
override def append(e: ILoggingEvent): Unit = synchronized {
eventBuffer += e
}
def events: immutable.Seq[ILoggingEvent] = synchronized {
eventBuffer.to[immutable.Seq]
}
}
// Get the Logback root logger and attach a RecordingAppender
val rootLogger = LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME).asInstanceOf[Logger]
val appender = new RecordingAppender()
appender.setContext(rootLogger.getLoggerContext)
appender.start()
rootLogger.addAppender(appender)
block
rootLogger.detachAppender(appender)
appender.stop()
appender.events
}
withServerAndConfig()((Action, _) => Action { rh =>
Results.Ok(rh.headers.get("User-Agent").toString)
}) { port =>
def testAgent[R: AsResult](agent: String) = {
val logMessages = recordLogEvents {
val Seq(response) = BasicHttpClient.makeRequests(port)(
BasicRequest("GET", "/", "HTTP/1.1", Map(
"User-Agent" -> agent
), "")
)
response.body must beLeft(s"Some($agent)")
}
logMessages.map(_.getFormattedMessage) must contain(contain(agent))
}
// These agent strings come from https://github.com/playframework/playframework/issues/7997
testAgent("Mozilla/5.0 (iPhone; CPU iPhone OS 11_0_3 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Mobile/15A432 [FBAN/FBIOS;FBAV/147.0.0.46.81;FBBV/76961488;FBDV/iPhone8,1;FBMD/iPhone;FBSN/iOS;FBSV/11.0.3;FBSS/2;FBCR/T-Mobile.pl;FBID/phone;FBLC/pl_PL;FBOP/5;FBRV/0]")
testAgent("Mozilla/5.0 (Linux; Android 7.0; TRT-LX1 Build/HUAWEITRT-LX1; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/61.0.3163.98 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/148.0.0.51.62;]")
testAgent("Mozilla/5.0 (Linux; Android 7.0; SM-G955F Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/62.0.3202.84 Mobile Safari/537.36 [FB_IAB/Orca-Android;FBAV/142.0.0.18.63;]")
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment