28. Logging

Log Creation

The Log class standardizes how to embed logging statements into Fantom applications. Every Log instance in the VM has a unique name which by convention always starts with the pod name and uses dot separators. Once a Log instance has been created for a specified name, it remains bound to that name for the lifetime of the VM. Logs are const, immutable instances shared by all threads.

Most of the time, you should just use Pod.log to get the standard log for your pod:

Pod.of(this).log.err("...")

You can also use Log.get which will create the Log on the first call, and look it up on subsequent calls:

// get or create a log named "acme"
const static Log log = Log.get("acme")

// find an existing log
Log.find("acme")          // throw exception if not found
Log.find("acme", false)   // return null if not found

// list all the active logs
Log.list

Log Statements

The following methods are used to generate log records:

  • Log.err: something bad happened
  • Log.warn: something happened which might be bad
  • Log.info: something interesting happened
  • Log.debug: something happened which is interesting only if you happen to be debugging

All logging methods take a Str message, and an optional Err. Some simple examples:

log.err("The freaking file didn't load", err)
log.info("CatchRoadRoader service started on port $port")

When writing debug log statements, we expect that they will be turned off most of the time. Therefore be aware of the hidden costs of string concatenation. You can use the isDebug method to skip creating a log message:

// this code performs string concatenation on every call
log.debug("The values are x=$x, y=$y, and z=$z")

// this code performs string concatenation only when needed
if (log.isDebug)
  log.debug("The values are x=$x, y=$y, and z=$z")

Log Levels

Each Log is configured to log events at or above a given LogLevel. These levels from least to most severe:

  • debug: log everything
  • info: log everything but debug
  • warn: log everything but debug, info
  • err: log only errors
  • silent: log nothing

All logs default to level info (see setup to change default levels).

You can get/set the current severity level of a Log via the level field. Some code examples:

log.level = LogLevel.warn
log.level < LogLevel.err    // returns true
log.level < LogLevel.info     // returns false

Log Handlers

Log handlers are functions designed to process LogRecs. The following Log methods are used to manage the handlers in a VM:

Handlers must be an instance of an immutable Func (they are shared by all threads). On startup there is always one handler installed which will print each record to the console via the LogRec.print method.

Here is a simple example of an installing a handler:

Log.addHandler |rec| { echo("My Handler: $rec") }

Log Setup

By default all log levels will default to info. You can programatically change the level via Log.level. You can also use the "etc/sys/log.props" file to setup the default level for any log. The "log.props" file is a standard props file where the log name is the key and the value is a String LogLevel:

web=debug
acmeWombat.requests=silent
acmeWombat.responses=warn