Formatters take entries and turn them into text that can be written to a file or stream or transferred to another system. Formatters control all aspects of the formatting, including what metadata headers are included and how dates and times are represented.

Most receivers that eventually turn their entries into text take a formatter as a constructor parameter. It can usually either be supplied explicitly or implicitly.


Timber comes with a flexible entry formatter that you can probably use for most cases called the ProgrammableEntryFormatter. It comes with its own mini-DSL to determine what metadata you want to appear in with your entries. The default entry formatter used by timber dispatchers if you don’t specify another one is actually just a configuration of a ProgrammableEntryFormatter. Here’s an example of creating one.

import org.scalawag.timber.backend.receiver.formatter.ProgrammableEntryFormatter
import org.scalawag.timber.backend.receiver.formatter.ProgrammableEntryFormatter._
import org.scalawag.timber.backend.receiver.formatter.timestamp._
import org.scalawag.timber.backend.receiver.formatter.level._

object MyEntryFormatter extends ProgrammableEntryFormatter(Seq(
  entry.timestamp formattedWith HumanReadableTimestampFormatter,
  entry.level formattedWith NameLevelFormatter,
  entry.tags formattedWith Commas,
  entry.threadAttributes map TopsOnly formattedWith CommasAndEquals

Metadata DSL

The constructor for ProgrammableEntryFormatter allows you to specify a list of metadata providers that you want it to use to extract metadata from its entries. These providers can be access through fields on the entry object as seen in the example above.

These are the providers that are built-in to the ProgrammableEntryFormatter.

new ProgrammableEntryFormatter(Seq(

You can also specify Strings to be included literally.

new ProgrammableEntryFormatter(Seq(

In addition to the providers above, you can specify some modifiers to tweak them before their inclusion:

If you don’t specify a formatter, the toString method of the object will be used. There are some built-in formatters for timestamps and levels that you can use. There are also some built-in formatters for any Iterables that may be more appealing than the default toString implementation.

entry.timestamp formattedWith HumanReadableTimestampFormatter
entry.timestamp formattedWith SimpleDateFormatTimestampFormatter("yyyy/MM/dd")
entry.threadName map { name => name.length }
entry.loggerAttributes without "name" formattedWith CommasAndEquals

For the metadata that may not be present (indicated with “if available” above), you can specify a fallback to use when it’s absent with orElse. You can’t use orElse after a metadata provider that will definitely return something.

entry.loggingClass orElse entry.loggerAttribute("name") orElse "unknown"

With certain combinations of modifiers, you may have to include parentheses if the scala compiler can’t figure out the operator precedence. You can also specify the modifiers without utilizing the infix call style, if you prefer.

Change the Formatter for the Default Dispatcher

The entry format is tied into the dispatcher configuration through a receiver. If you want to change the format of the default dispatcher, you’ll need to establish a new default dispatcher so that you have a reference to it and can change its configuration. Here’s an example very similar to the one in Getting Started except that this changes the formatter used by the default dispatcher to include only the timestamp as metadata with the logged message.

import org.scalawag.timber.api.Logger
import org.scalawag.timber.api.Level.INFO
import org.scalawag.timber.backend.DefaultDispatcher
import org.scalawag.timber.backend.dispatcher.Dispatcher
import org.scalawag.timber.backend.dispatcher.configuration.Configuration
import org.scalawag.timber.backend.dispatcher.configuration.dsl._
import org.scalawag.timber.backend.receiver.formatter.ProgrammableEntryFormatter
import org.scalawag.timber.backend.receiver.formatter.ProgrammableEntryFormatter._

object Main {
  def setupLogging(): Unit = {
    // Implicitly specify an EntryFormatter
    implicit val formatter = new ProgrammableEntryFormatter(Seq(entry.timestamp))
    val config = Configuration {
      ( level >= INFO ) ~> stderr
    val disp = new Dispatcher(config)

  def main(args:Array[String]): Unit = {

    val log = new Logger
    log.trace("trace message")
    log.error("error message")

Help improve this page.