Logging

Logging is an important part of any application that has outgrown the toy stage. Often, logging is the only way to get insight into an application running in production.

Lift provides a Scala interface to the SLF4J logging facade. As such, configuring logging in Lift is the same as in SLF4J, but a few helpers have been added.

Dependencies

If you are getting an error about StaticLoggerBinder, make sure to include one of the supported bindings in your app’s classpath, normally as a project dependency.

Usage

The simplest way to start using logging is to mixin the Logger trait. This will provide direct access to methods for the different logging levels:

trait MyService extends Logger {
  info("Creating MyService at %s".format)
}

Note that there is no need to wrap the logger call with @if @ since the string argument to the logger call is a by-name parameter and thus not evaluated if the loglevel is not active.

Be careful about mixing in the Logger trait with CometActor as some error methods conflict.

Sometimes you don’t want to pollute the namespace with all the Logger methods. In this case, you can get a nested logger if you mixin the Loggable trait:

trait MyService extends Loggable {
  logger.info("Creating MyService at %s".format)
}

The logger name

The default logger name is this.getClass.getName. This is usually fine for application level services. But for reusable types that will be used in different places it may make sense to name the logger differently. Either by using the static type:

package demoapp
trait MyService  {
  val logger = Logger // This will name the logger demoapp.MyService no matter where this trait is mixed in
  logger.info("Creating MyService at %s".format)
}

or by giving it an arbitrary name:

trait MyService  {
  val logger = Logger // This will name the logger audit.payments no matter where this trait is mixed in
  logger.info("Creating MyService at %s".format)
}

Tracing function results

When coding in a functional style it sometimes ruins the readability if you have to break an expression into several intermediate steps To remedy this, a trace method has been added that can be used to log a value within an expression:

first map {f => user.firstName(trace)

Using a singleton for logging

Mixing in either Logger or Loggable to application components as described previously will create quite a few loggers, arranged in a hierarchy that resembles your package structure. This makes is easy to filter out irrelevant log messages at a granular level by changing the logging level of individual loggers. This happens at runtime, without changing any source code.

But if you for some reason don’t need this filtering you can create a single logger for use throughout the application:

object Log extends Logger
...
Log.info

Changing log levels at runtime

Lift includes a widget that can be used to change the log levels at runtime. More info can be found here

Configuration

Configuration is made at two levels:

  1. Selecting a backend. Since SLF4J is just a facade, an actual logging backend needs to be included in the application. This is done by adding the appropriate dependencies to your SBT project/ pom.xml. If you don’t include a logging backend in your application, you well get a warning during boot and there will not be any logging output.
  2. Configuring the chosen logging backend. This includes selecting where to log , what should be logged etc. This step is optional. A logging backend usually has a default configuration process that will be used of no other configuration is made.

Selecting a backend

The default logging backend in the Lift archetypes is logback. If you start from scratch or for some reason want to change the default this is easily done by adding a dependency to your project configuration.

Using Log4j

Add the following dependency


  "org.slf4j" % "slf4j-log4j12" % "1.6.1",

Using Logback

Add the following dependency


  "ch.qos.logback" % "logback-classic" % "0.9.28",

Configuring the backend

If you’re only using lift-common, no backend configuration is made by default.

You can specify a function in Logger.setup that will be called before the first Logger is created. This can be used to configure the backend you have chosen.

Two helper objects are provided: Logback and Log4j. They can be used to configure the specified backend like this:

Logger.setup = Full(Logback.withFile)

For logback – a config file named logback.xml can be placed in src/main/resources/ and this will be automatically picked up – without using the above function.

or

Logger.setup = Full(Log4j.withFile) 

If you put one of the above function inside Boot, then Boot itself can’t mix in Logger or Loggable. If Boot does mix one of them in a default logging config from will used /common/Logging.scala

Automatic run.mode dependent configuration

If you’re using lift-webkit, LiftRules contains a property, configureLogging, that can be used to specify how logging should be configured. Note this must be set before any Loggers are created

By default this is set to LoggingAutoConfigurer, which tries to guess which of the Logback or Log4j backends are used. If a backend is found, it is configured with a file which should be named by the Lift property file naming conventions and ending in either logback.xml, log4j.xml, or log4j.props.

Example with logback.xml.

You can configure logging by providing a configuration files in src/main/resources/. There is a standard naming format that allows the configuration to be based on the run mode, user and host.

The format is:

modeName.userName.hostName.filename.extension

where:

Note: in case both hostName and userName are ommitted default keyword is mandated.
pilot.default.logback.xml is taken in pilot mode, pilot.logback.xml would be ignored.

Lift searches for the the most specific file in the order. For example, on my machine , where I’m logged in as “richard” running in development mode, the search order is:

In production mode the list looks like this:

The development file will usually only specify logging to the console and a rather verbose logging level for most components. When running in production, the file will usally specify logging to a file, rollover policy, maybe send email on errors etc. The loglevel will probably be set to error for most components.

Logback config

http://logback.qos.ch/manual/configuration.html