Logging

Author: Adrián Pérez <aperez@igalia.com>
Copyright: 2009 Igalia S.L.
License:GPL3

Abstract

Logging module for use in Bill shell scripts. Vaguely inspired by Python's “logging” module, which in turn is based upon Apache's “log4j” system.

Contents

Discussion

Log levels

There are seven levels of logging. Enabling a higher level of logging also enables the preceding ones. Levels are (in order):

  1. LOG_NONE
  2. LOG_FATAL
  3. LOG_ERROR
  4. LOG_WARN
  5. LOG_INFO
  6. LOG_DEBUG
  7. LOG_TRACE

Note that LOG_NONE is noy really a log level, but just a convenience to merely disable logging. Logging functions will not have effect at all at this level, even no side effects.

You can set the log level by assigning the global LOG_LEVEL variable which, by default, is set to LOG_INFO. You can also control the log level with the log_enable, log_disable, log_chattier and log_quieter functions.

# Our program is quiet by default, but increase the log level if
# requested by the user...
LOG_LEVEL=${LOG_WARN}

if [ "$FOO_DEBUG" ] ; then
    log_chattier
fi

You can change the value of LOG_LEVEL whenever it is needed, but it is usually set only at program startup.

Root logger and destinations

Unless specified otherwise, by default logging output goes to the so called root logger, so there is no need to specify a destination to send messages to it. The root logger is created automatically for you the first time you use a logging function, and it will print messages to standard output. This way using the module is only a matter of:

use text/log
log_info 'I am a log message'

If you create a new logger (using log_create) you can send data to it by putting an exlamation mark (!) after the log level and then the name of the logger. Suppose we have created the accesslog logger, then we would send data to it with:

log_create accesslog ... # Put a log chain instead of the ellipsis
log_warn ! accesslog 'This message goes to "accesslog"'

You can change the logger used as root by using the log_root function. This, in combination with the ability of defining your own log chains, allows for bypassing the default initialization:

log_create mylog ... # Put a log chain instead of the ellipsis
log_root   mylog
log_info  'Built-in defaults are not initialized this time'

Log chains

The text/log module works with log chains: a text message which is about to be logged is passed through a series of filters, and then final output is done by a sink. The full set of components and their ordering is a log chain:

formatter1 » formatter2 » ... » formatterN » sink

A formatter receives the log level and message as arguments, and is responsible of transforming text and echoing it. This way text can be processed before it reaches its destination. A log

A sink is responsible for outputting the messages to whatever destination they may have (files, console, etc).

Both sinks and formatters may have configuration options, which can be changed and inspected via log_config. Configuration of components belongs to the chain.

Producing output

All output can be done by using the log function. Per-level logging functions are provided as a convenience, though. You mus pass at least the log level and the message to the function:

log $LOG_INFO 'This is an informative message'
log $LOG_WARN 'This is a warning, so be warned!'

The destination of such statements will be the current root logger. If you want to change where message go, make sure you define an alternate logger with log_create and put its name preceded by a exclamation mark, after the logging level:

log $LOG_INFO ! $(log_root) 'This is a message for the root logger'

In the previous example, the log_root function is used to get the name of the root logger, but you could specify which logger to use instead. Supposing that you have defined a logger named mylog, that would be:

log $LOG_INFO ! mylog 'Message for the "mylog" logger'

Per-level logging functions

Alongside with the general log function, for each one of the log levels there is a log_* function (i.e. log_info, log_fatal...) which is a convenience wrapper over the generic one. All those functions accept the same parameters as log, with the exception of the log level. As an example, the following three lines are equivalent:

log $LOG_INFO 'I am an uninformative message'
log_info 'I am an uninformative message'
log_info ! default 'I am an uninformative message'

The only difference between both is the number of characters typed. Note that using convenience functions does not involve extra function calls when logging is not to be produced.

Functions

log_chattier

log_chattier

Increases the log level by one. If level has already reached the maximum useable value, does nothing.

log_quieter

log_quieter

Decreases the log level by one. If level has already reached the minimum useable value, does nothing.

Important

Note that this never disables logging completely, use log_disable or set LOG_LEVEL to LOG_NONE for that.

log_disable

log_disable

Disables logging. This has the same effect of setting LOG_LEVEL to LOG_NONE, but the active log level will be saved and restored if logging is re-enabled using log_enable.

log_enable

log_enable [ level ]

Enables logging with a given level. If the new level is not given, or it is not one of the valid LOG_* values, the log level saved by log_disable will be restored. If log_disable has never been used, the default log level is set.

Important

This function always enables some degree af logging, at least the bare minimum of logging fatal errors.

log

log level [! destination] message

log_config

log_config name option [value]

log_root

log_root [name]

Obtains the name of the root logger when called with no arguments, or changes the root logger when a name is passed in.

log_create

log_create name sink [formatter1 [formatter2 ... [formatterN]

Creates a new logging chain associated to a name, which outputs messages using to a particular sink. Formatting of the message strings will pass along formatter1, formatter2 and so on until formatterN (in that order) before being passed to the sink. The function takes care of initializing components of the chain of filters and the sink to their default values, it is the responsibility of the programmer to reconfigure the items as desired using log_config.

See also: log_destroy, log_replace, log_config

log_destroy

log_destroy name

Destroys a logger given its name. This is frees resources which could be allocated by log_create during initialization of the logger components.

See also: log_create, log_replace

log_replace

log_replace name sink [formatter1 [formatter2 ... [formatterN]]]

Destroys a logger and creates a new one with the same name, thus replacing the existing logger. This is mainly useful to redefine the default logger at startup and avoid having to type in the destination of messages in every call to the log functions. Do not forget to use log_config to customize the new logger.

See also: log_create, log_destroy