What is a good way to do logging in a Scala application? Something that is consistent with the language philosophy, does not clutter the code, and is low-maintenance and uno
I pulled a bit of work form the Logging trait of scalax, and created a trait that also integrated a MessageFormat-based library.
Then stuff kind of looks like this:
class Foo extends Loggable {
info( "Dude, I'm an {0} with {1,number,#}", "Log message", 1234 )
}
We like the approach so far.
Implementation:
trait Loggable {
val logger:Logger = Logging.getLogger(this)
def checkFormat(msg:String, refs:Seq[Any]):String =
if (refs.size > 0) msgfmtSeq(msg, refs) else msg
def trace(msg:String, refs:Any*) = logger trace checkFormat(msg, refs)
def trace(t:Throwable, msg:String, refs:Any*) = logger trace (checkFormat(msg, refs), t)
def info(msg:String, refs:Any*) = logger info checkFormat(msg, refs)
def info(t:Throwable, msg:String, refs:Any*) = logger info (checkFormat(msg, refs), t)
def warn(msg:String, refs:Any*) = logger warn checkFormat(msg, refs)
def warn(t:Throwable, msg:String, refs:Any*) = logger warn (checkFormat(msg, refs), t)
def critical(msg:String, refs:Any*) = logger error checkFormat(msg, refs)
def critical(t:Throwable, msg:String, refs:Any*) = logger error (checkFormat(msg, refs), t)
}
/**
* Note: implementation taken from scalax.logging API
*/
object Logging {
def loggerNameForClass(className: String) = {
if (className endsWith "$") className.substring(0, className.length - 1)
else className
}
def getLogger(logging: AnyRef) = LoggerFactory.getLogger(loggerNameForClass(logging.getClass.getName))
}
With Scala 2.10+ Consider ScalaLogging by Typesafe. Uses macros to deliver a very clean API
https://github.com/typesafehub/scala-logging
Quoting from their wiki:
Fortunately Scala macros can be used to make our lives easier: ScalaLogging offers the class
Loggerwith lightweight logging methods that will be expanded to the above idiom. So all we have to write is:
logger.debug(s"Some ${expensiveExpression} message!")
After the macro has been applied, the code will have been transformed into the above described idiom.
In addition ScalaLogging offers the trait Logging which conveniently provides a Logger instance initialized with the name of the class mixed into:
import com.typesafe.scalalogging.slf4j.LazyLogging
class MyClass extends LazyLogging {
logger.debug("This is very convenient ;-)")
}
I was really surprised that Scribe logging framework that I use at work isn't even mentioned here. What is more, it doesn't even appear on the first page in Google after searching "scala logging". But this page appears when googling it! So let me leave that here.
Main advantages of Scribe:
Writer, Monoid and a Monad implementation.
I've actually followed the recommendation of Eugene and tried it and found out that it has a clumsy configuration and is subjected to bugs, which don't get fixed (such as this one). It doesn't look to be well maintained and it doesn't support Scala 2.10.
-Dorg.slf4j.simplelogger.defaultlog=trace to execution command or hardcode in your script: System.setProperty("org.slf4j.simplelogger.defaultlog", "trace"). No need to manage trashy config files!Run/Debug Configurations and add -Dorg.slf4j.simplelogger.defaultlog=trace to VM options.Here's what you need to be running it with Maven:
<dependency>
<groupId>com.weiglewilczek.slf4s</groupId>
<artifactId>slf4s_2.9.1</artifactId>
<version>1.0.7</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.6.6</version>
</dependency>
This is how I got Scala Logging working for me:
Put this in your build.sbt:
libraryDependencies += "com.typesafe.scala-logging" %% "scala-logging" % "3.7.2",
libraryDependencies += "ch.qos.logback" % "logback-classic" % "1.2.3"
Then, after doing an sbt update, this prints out a friendly log message:
import com.typesafe.scalalogging._
object MyApp extends App with LazyLogging {
logger.info("Hello there")
}
If you are using Play, you can of course simply import play.api.Logger for writing log messages: Logger.debug("Hi").
See the docs for more info.