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.
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.
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 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)
}
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)
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
Lift includes a widget that can be used to change the log levels at runtime. More info can be found here
Configuration is made at two levels:
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.
Add the following dependency
"org.slf4j" % "slf4j-log4j12" % "1.6.1",
Add the following dependency
"ch.qos.logback" % "logback-classic" % "0.9.28",
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
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.
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:
modeName
is omitted for development mode, but is otherwise one of: production, test, staging, pilot, profile or default.hostName
is optionaluserName
is optionalfilename.extension
is logback.xml
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.