Best Practices

Create a BaseLogger subclass

You 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"
}

Use the Default Dispatcher in Libraries

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.

Specify a Logging Backend for Testing

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.6.0" % "test"

If you want anything fancier than that, read about using the timber backend to discover everything you can do.

Help improve this page.