Logging

Most of us are used to the great logging infrastructure available on the server side and sometimes it helps in situations where direct debugging is difficult or plain impossible (for example a customer is using your app). Luckily there are nice logging libraries available also for Javascript that we can utilize through thin facades.

To emulate log4j style API, we'll define a LoggerFactory providing Logger instances. These will hook up to the underlying Javascript library to provide the real functionality. See Log4JavaScript.scala for details.

The package object logger provides a default logger called log for easy access to logging functionality. If you want to create separate loggers, you may do so through LoggerFactory.getLogger(name) method. To create an entry in the log, all you need to do is call the appropriate log-level function with a message and an optional Exception.

log.debug(s"User selected ${items.size} items")

log.error("Invalid response from server", ex)

The default logger prints all messages to the browser console, but you can also use a more advanced popup window logger, to analyze log messages with better granularity and filtering. Use getPopupLogger to create such logger.

Sending client logs to the server

The logging library also provides functionality to send all your log messages to the server, for easier analysis in error situations. It packages each log message into a small JSON object and POSTs it to the specified URL. On the server side we define a path to receive and print those log messages.

def logging = Action(parse.anyContent) {
  implicit request =>
    request.body.asJson.foreach { msg =>
      println(s"CLIENT - $msg")
    }
    Ok("")
}

To enable server side logging, call log.enableServerLogging("/logging"). In the server logs the client log messages will be shown as below:

sharedProjectJVM CLIENT - [{"logger":"Log","timestamp":1425500652089,"level":"INFO","url":"http://localhost:8080/#todo","message":"This message goes to server as well"}]
sharedProjectJVM Sending 4 Todo items
sharedProjectJVM Sending 4 Todo items
sharedProjectJVM Todo item was updated: TodoItem(3,Walk away slowly from an explosion without looking back.,TodoHigh,false)
sharedProjectJVM CLIENT - [{"logger":"Log","timestamp":1425500661456,"level":"DEBUG","url":"http://localhost:8080/#todo","message":"User is editing a todo"}]
sharedProjectJVM CLIENT - [{"logger":"Log","timestamp":1425500664865,"level":"DEBUG","url":"http://localhost:8080/#todo","message":"Todo editing cancelled"}]
sharedProjectJVM CLIENT - [{"logger":"Log","timestamp":1425500668485,"level":"DEBUG","url":"http://localhost:8080/#todo","message":"User is editing a todo"}]
sharedProjectJVM CLIENT - [{"logger":"Log","timestamp":1425500671017,"level":"DEBUG","url":"http://localhost:8080/#todo","message":"User is editing a todo"}]
sharedProjectJVM CLIENT - [{"logger":"Log","timestamp":1425500671751,"level":"DEBUG","url":"http://localhost:8080/#todo","message":"User is editing a todo"}]
sharedProjectJVM CLIENT - [{"logger":"Log","timestamp":1425500672101,"level":"DEBUG","url":"http://localhost:8080/#todo","message":"Todo edited:
  TodoItem(3,Walk away slowly from an explosion without looking back.,TodoNormal,false)"}]

Many of the advanced features of the underlying logging library are not exposed in this tutorial project, but you can look at the current implementation and continue from there if you find the missing features useful!

Limiting log messages in production

While extensive debug messages are a life saver in development, you don't want to flood your customer's browser console with debug messages. To disable low level log messages in the production build we'll use a special Scala annotation called @elidable. It works much like #ifdef in C/C++ removing the function and all calls to the function in the final byte-code. Therefore there is no performance penalty whatsoever even if you litter your code with hundreds of log.debug calls, as they are all totally optimized away.

  @elidable(FINEST) def trace(msg: String, e: Exception): Unit
  @elidable(FINEST) def trace(msg: String): Unit
  @elidable(FINE) def debug(msg: String, e: Exception): Unit
  @elidable(FINE) def debug(msg: String): Unit
  @elidable(INFO) def info(msg: String, e: Exception): Unit
  @elidable(INFO) def info(msg: String): Unit

To control what calls to eliminate and what to keep, use a scalac command line option -Xelide-below <level>. This is automatically set to WARNING in the release command.

// in settings we have scalacOptions ++= elideOptions.value,
set elideOptions in js := Seq("-Xelide-below", "WARNING")

In the production build all log calls below the WARNING level will be optimized away.

results matching ""

    No results matching ""