BaseLogger
subclassYou can use the timber Logger
class directly to log the five standard levels (TRACE
, DEBUG
, INFO
, WARN
and
ERROR
). However, if you decide, that you want to change the way that you’re logging (e.g., add a custom log level
or associate all of your library’s entries with a specific tag), you’ll have to do it everywhere that you create a
logger or log entries. For this reason, it’s a good practice to have a custom Logger
subclass that you use
throughout your source code. This gives you a single place to control the way logging works in your library or
application.
Here’s an example that uses the default levels but adds a custom tag to all entries generated by your loggers:
import org.scalawag.timber.api._
object MyLoggerTag extends Tag
class MyLogger(override val attributes:Map[String,Any] = Map.empty, override val tags:Set[Tag] = Set.empty)
extends Logger(attributes, tags + MyLoggerTag)
Here’s another example that uses a custom set of log levels:
import org.scalawag.timber.api._
import org.scalawag.timber.api.level._
class MyLogger(override val attributes:Map[String,Any] = Map.empty, override val tags:Set[Tag] = Set.empty)
extends BaseLogger(attributes, tags) with Trace with Debug with Emergency
{
override protected[this] val traceLevel = Level.TRACE
override protected[this] val debugLevel = Level.DEBUG
override protected[this] val emergencyLevel = Level.ERROR + 1000 as "EMERGENCY"
}
When using timber in a library, it’s important that you don’t do anything that constrains developers using
your library in their applications. To achieve this, all of your loggers should use the DefaultDispatcher
,
which allows the logging backend to be determined at run time. Then, application developers can choose the
logging backend and then configure that backend any way they choose. Your library code should be oblivious to how
the entries it generates are being processed.
It’s easier to use the DefaultDispatcher
than not. When you create a logger, if you don’t explicitly specify
the dispatcher as a constructor parameter and there is no implicit Dispatcher
in scope, the DefaultDispatcher
is
used. Since there are no concrete dispatcher implementations in the timber API, this shouldn’t be too hard to achieve.
(You could implement Dispatcher
yourself and instantiate it and construct all of your loggers to use it but this
would only server to anger the developer trying to use your library and cause them not to decide against using it.)
An easy way to guard against this is to create a custom BaseLogger
subclass for use in your library that explicitly
uses the DefaultDispatcher
instead of relying on there being no implicit dispatcher in scope.
import org.scalawag.timber.api._
class MyLogger extends Logger()(DefaultDispatcher)
Of course, you may want to combine this example with one of the examples above to both restrict the dispatcher and add custom tags or log levels.
Even if you’re only using the timber API, you’ll need to have a logging backend available for your unit tests. You
can create a simple backend that logs to stderr just by including timber.jar
in the test classpath. Make sure you
don’t include it in your compile
classpath or you might end up inadvertently creating a dependency on timber.jar
in your library and that’s a bad thing (see above).
libraryDependencies += "org.scalawag.timber" %% "timber-backend" % "0.7.0-SNAPSHOT" % "test"
If you want anything fancier than that, read about using the timber backend to discover everything you can do.