How to make Play print all the errors - scala

In our Scala, Play, Reactivemongo we have a big problem with exception handling - when there is an error, in Iteratee/Enumeratee or in Actor system Play just swallows it, without logging any error to the output. So we effectively need to guess where, and why this error might happen.
We made Globals override, to always print the error, and specified logger.root=TRACE, but still saw no output, from which we could analyse our problems.
How to forcebly make Play print all the errors

Didn't found the way to explicitly log everything but there is a way to log exceptions locally.
I did this:
def recover[T] = Enumeratee.recover[T] {
case (e, input) => Logger.error("Error happened on:" + input, e)
}
and then used it on all the enumeratees that can produce errors
def transform(from: Enumerator[From]): Enumerator[String] = {
heading >>> (from &> recover[From] ><> mapper) >>> tailing
}
here, mapper throws exception, and they are all logged.

I think your problem is with how Future works in scala, let's take the following exemple :
val f :Future[Int]= Future {
throw new NullPointerException("NULL")
1
}
f.map(s1 => {println(s" ==> $s1");s" ==> $s1"})
This code will throw an exception but the stack trace will not be printed as futures handle the error.
If you want to get the error that happened you can
just call:
f.onComplete{
case Success(e) => {}
case Failure(e) => e.printStackTrace()
}
e is a throwable that you can use as you want to handle the error.

At the solution I used, is override through ErrorHandling in Play https://www.playframework.com/documentation/2.4.2/ScalaErrorHandling, basically creating ErrorHandler that logs all the errors, with needed detalization.

Related

Strange timeout with ScalaTest's Selenium DSL

I'm writing Selenium tests with ScalaTest's Selenium DSL and I'm running into timeouts I can't explain. To make matters more complicated, they only seem to happen some of the time.
The problem occurs whenever I access an Element after a page load or some Javascript rendering. It looks like this:
click on "editEmployee"
eventually {
textField(name("firstName")).value = "Steve"
}
My PatienceConfig is configured like this:
override implicit val patienceConfig: PatienceConfig =
PatienceConfig(timeout = Span(5, Seconds), interval = Span(50, Millis))
The test fails with the following error:
- should not display the old data after an employee was edited *** FAILED ***
The code passed to eventually never returned normally. Attempted 1 times over 10.023253653000001 seconds.
Last failure message: WebElement 'firstName' not found.. (EditOwnerTest.scala:24)
It makes sense that it doesn't succeed immediately, because the click causes some rendering, and the textfield may not be available right away. However, it shouldn't take 10 seconds to make an attempt to find it, right?
Also, I find it very interesting that the eventually block tried it only once, and that it took almost precisely 10 seconds. This smells like a timeout occurred somewhere, and it's not my PatienceConfig, because that was set to time out after 5 seconds.
With this workaround, it does work:
click on "editEmployee"
eventually {
find(name("firstName")).value // from ScalaTest's `OptionValues`
}
textField(name("firstName")).value = "Steve"
I did some digging in the ScalaTest source, and I've noticed that all calls that have this problem (it's not just textField), eventually call webElement at some point. The reason why the workaround works, is because it doesn't call webElement. webElement is defined like this:
def webElement(implicit driver: WebDriver, pos: source.Position = implicitly[source.Position]): WebElement = {
try {
driver.findElement(by)
}
catch {
case e: org.openqa.selenium.NoSuchElementException =>
// the following is avoid the suite instance to be bound/dragged into the messageFun, which can cause serialization problem.
val queryStringValue = queryString
throw new TestFailedException(
(_: StackDepthException) => Some("WebElement '" + queryStringValue + "' not found."),
Some(e),
pos
)
}
}
I've copied that code into my project and played around with it, and it looks like constructing and/or throwing the exception is where most of the 10 seconds are spent.
(EDIT Clarification: I've actually seen the code actually spend its 10 seconds inside the catch block. The implicit wait is set to 0, and besides, if I remove the catch block everything simply works as expected.)
So my question is, what can I do to avoid this strange behaviour? I don't want to have to insert superfluous calls to find all the time, because it's easily forgotten, especially since, as I said, the error occurs only some of the time. (I haven't been able to determine when the behaviour occurs and when it doesn't.)
It is clear that the textField(name("firstName")).value = "Steve" ends up calling the WebElement as you have found out.
Since the issue in the op is happening where ever web elements are involved (which in turn implies that webdriver is involved), I think it is safe to assume that the issue is related to the implicit wait on the Web driver.
implicitlyWait(Span(0, Seconds))
The above should ideally fix the issue. Also, making implicit wait to be 0 is a bad practice. Any web page might have some loading issues. The page load is handled by Selenium outside its wait conditions. But slow element load (may be due to ajax calls) could result in failure. I usually keep 10 seconds as my standard implicit wait. For scenarios which require more wait, explicit waits can be used.
def implicitlyWait(timeout: Span)(implicit driver: WebDriver): Unit = {
driver.manage.timeouts.implicitlyWait(timeout.totalNanos, TimeUnit.NANOSECONDS)
}
Execution Flow:
name("firstName") ends up having value as Query {Val by = By.className("firstName") }.
def name(elementName: String): NameQuery = new NameQuery(elementName)
case class NameQuery(queryString: String) extends Query { val by = By.name(queryString) }
Query is fed to the textField method which calls the Query.webElement as below.
def textField(query: Query)(implicit driver: WebDriver, pos: source.Position): TextField = new TextField(query.webElement)(pos)
sealed trait Query extends Product with Serializable {
val by: By
val queryString: String
def webElement(implicit driver: WebDriver, pos: source.Position = implicitly[source.Position]): WebElement = {
try {
driver.findElement(by)
}
catch {
case e: org.openqa.selenium.NoSuchElementException =>
// the following is avoid the suite instance to be bound/dragged into the messageFun, which can cause serialization problem.
val queryStringValue = queryString
throw new TestFailedException(
(_: StackDepthException) => Some("WebElement '" + queryStringValue + "' not found."),
Some(e),
pos
)
}
}
}
I don't know ScalaTest's specifics, but such strange timeouts usually occur when you're mixing up implicit and explicit waits together.
driver.findElement uses implicit waits internally. And depending on specified explicit waits timeout, you may face with summing both together.
Ideally, implicit waits should be set to 0 to avoid such issues.

How to catch exception in future in play framework 2.4

I'm trying to figure out how to catch an exception from within a future in a function being called by an asynchronous action in Play Framework 2.4. However, the code I've got using recover never seems to get executed - I always get an Execution exception page rather than an Ok response.
The action code is:
def index = Action.async {
cardRepo.getAll()
.map {
cards => Ok(views.html.cardlist(cards))
}.recover{
case e: Exception => Ok(e.getMessage)
}
}
The code in cardRepo.getAll (that I've hard-coded a throw new Exception for experimenting) is:
def getAll(): Future[Seq[Card]] = {
implicit val cardFormat = Json.format[Card]
val cards = collection.find(Json.obj())
.cursor[Card]()
.collect[Seq]()
throw new Exception("OH DEAR")
cards
}
I've seen similar questions on Stack Overflow but I can't see what I'm doing wrong.
Thanks Mon Calamari - I think I understand now. The future is coming from collection.find, so if an error was inside that, my code would work but because I've put I've got it inside the function above it, there is no Future at that point.

ScalaTest display original Failure stacktrace

How can display the original stacktrace for a Try Failure as part of the test output?
When I do the following:
result.success.value should equal blah
And the result is a Failure I get
The Try on which success was invoked was not a Success.
Or, if I do this first:
result should be a 'success
It's a little more informative, because I can see the exception:
Failure(java.lang.IllegalArgumentException: Cannot format given Object as a Date) was not a success
But the stack trace shows where it failed in the test, not the original stack trace of the Failure.
If some error-causing code is wrapped into Try, it means that exception is handled somewhere inside calculation, and it's cause and message will not be printed somewhere until explicitly requested. In order to see original cause, you can access failure object directly (docs), or handle failure manually:
val (ok, message) = result match {
case Success(v) => (true, "")
case Failure(ex) =>
(false, ex.getMessage + "\n" + ex.getStackTrace.mkString("\n"))
}
assert(ok, message)
This link also looks helpful.
If you want original stack trace to be printed - use result.get, but check not for 'success but inner type/value:
result.get should equal blah

How to do: Try {block of code} for X duration and catch {Exceptions, such as Timeout}?

Alright so the title is a little obscure since I was unsure how to word it, but essentially I am trying to do a try-catch statement that will timeout... Here's some pseudo-code that may help describe what I'm trying to do:
try (10 seconds) {
*make some connection and do some things*
} catch {
case ex1: TimeoutException => *do something*
case ex2: Exception => *do something else*
}
Currently there is a bug in the hardware I'm working with where the request for a connection never gets a response back, so it just sits there and doesn't catch any exceptions. Since it's only a bug (that should be temporary), I don't want to manipulate the architecture of the application (specifically I do not want to create a new actor just to account for something small) and it would be very ideal if I could implement this pseudo-code within the scope of the class.
So essentially my question is how do I implement the pseudo-code above within the scope of the class it's in?
Let me know if anything is unclear! Thank you!
Try:
import scala.concurrent._
import ExecutionContext.Implicits.global
val f = future {
// make some connection and do some things
}
try {
Await.result(f, 10 seconds);
} catch {
case e: TimeoutException => // do something
case _ => // do something else
}
More info: Futures and Promises

Scala play catch/log java.lang.ExceptionInInitializerError

In Play, if an in thrown in initializing a class - for example, configuration.get("badKey").get being called, I believe there is a java.lang.ExceptionInInitializerError. However, no error is ever caught or logged by Play and it just goes on, broken.
How can I make sure this is caught, or logged, or something other than just ignored?
It brings the website to halt, but I don't know that it's happened.
I know that I shouldn't have these errors in the first place. What first prompted this is that the Application.conf file differs depending on if its my local environment or production. If a developer forgets to add a key to the production one, it can bring the website to a halt (silently). Conceivably, though, there could be other errors too.
You can use play's built in Logger.
import play.Logger
try {
configuration.get("badKey").get
} catch {
case e:ExceptionInInitializerError => {
Logger.error("ExceptionInInitializerError")
Logger.error(e.getMessage())
}
case e:Exception => {
Logger.error("Exception")
Logger.error(e.getMessage())
}
}
Or write an accessor that goes to a default or logs an error without needing a try catch
import play.Logger
import play.api.Play
object Conf {
private val config = Play.current.configuration
def get(key:String):String =
config.getString(key) match {
case Some(value:String) => value
case _ => {
Logger.warn("No value found in application.conf for [" + key + "]")
"some default value"
}
}
}