I couldn’t resist the temptation, and started to write yet another Scheme logging library; this has been on my TODO-list for quite some time now, but now it became more urgent, since we need a logging facility for
conjure. I hope
jao will be pleased to see that my branch of conjure already
makes use of the new logging module

.
Of course, I didn’t in fact start from scratch, but had a look at the various logging libraries available; the Scheme ones being:
- The logging package in guile-lib. I couldn’t really use that code, since it’s uses GOOPS, Guile’s CLOS-like object system. Also it doesn’t support hierarchies of loggers, which I consider an important feature, since it allows flexible configuration of the logging behaviour for a software system that is contains of multiple subsystems. For example, you may want to enable debugging output in a specific subsytem, while only getting warnings and errors for everything else.
- log4scm. I don’t know what to say about that but: strange. While named in the log4X tradition initiated by log4j, a rather powerful (and complex, IMHO) logging system for Java, it seems very unflexible, and not suitable for a general-purpose logging system. Nevertheless it uses threads, a callback system for fatal logs, and SSAX to parse XML files for configuration - speak about heavyweight! Strange license, too. I will refrain from commenting further
.
I also had a look at
log4r and the
Python logging package; both seem worthy taking as a starting point. What I’ve come up with so far has a pretty similiar model to these systems; you send a
log entry that should be logged to a
logger, which has zero or more
handlers that are responsible for actually doing anything with the log entry, such as writing it to a port. All loggers, besides the
root logger have a parent, (also a logger) which log entries are sent to, in addition to being processed by the handlers attached to the logger; the propagation to the parent can also be turned off. The logger hierarchy is established by a hierarchical namespace: The logger with the name
'(conjure cc) is a child of the logger named
'(conjure).
This may all sound overly complicated, but getting started is very straightforward:
;; ,open spells.logging
(define log-info (make-log ‘(test) ‘info))
(configure-logger ‘())
(log-info “hello world!”)
make-log creates a
logging procedure. Logging procedures take a single argument to be logged; the argument may be an arbitrary object; it is up to the handlers on how to go about formatting this object. The argument passed to the logging procedure is wrapped in a log entry, which additionally contains the log level.
The name of the logger that is to be used is
'(test), and
'info is the log level. If the logger
'(test) doesn’t already exist, it is created on the fly with no handlers and the root logger as a parent; it can be configured later. This separation of logger creation and configuration is important: In a real application, the logging can be configured from the command line or a configuration file; you don’t want to (hard-)code it into the same module that uses the logger.
(configure-logger '()) configures the root logger (which has the empty list as name) with a default configuration.
configure-logger also has an optional argument in which you can provide a custom configuration, but I won’t go into this now, since this part of interface is most likely to change. The default configuration amounts to logging everything on
(current-output-port).
Finally, we log a simple string through the logging procedure
log-info. The output looks like this:
(test: [info] hello world!)
Well, enough of this for now; if you got curious you can take peek at the
code.