logo

Logging

Log Creation

The sys::Log class standardizes how to embed logging statements into Fan 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.

Logs are most often created via 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.error: 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.error("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
  • error: log only errors
  • silent: log nothing

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

You can get the current severity level of a Log via the level method and set it via setLevel. Some code examples:

log.setLevel(LogLevel.warn)
log.level < LogLevel.error    // returns true
log.level < LogLevel.info     // returns false

Log Handlers

Log handlers are functions designed to process LogRecords. 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 LogRecord.print method.

Here is a simple example of an installing a handler:

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

Log Setup

By default all log levels will default to info. You can programatically change the level via setLevel. You can also use the "lib/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 LogLevel. An example "log.props" file:

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