Foolscap Logging Formats

This document describes the Foolscap logging format. Foolscap logging uses event dictionaries in memory. Certain tools will serialize these events onto disk. These on-disk files may have additional metadata stored in adjunct index files. This document describes the formats of all these objects.

Events

Each call to log.msg produces an event. These events are stored in memory as dictionaries, with the following keys:

In addition to these keys, all other keyword arguments to the log.msg and log.err calls are recorded in the event dictionary. Some keys are reserved: those that begin with an underscore, and those that are not legal python identifiers (i.e. they contain dots). Some of these reserved keys are used for internal purposes.

Developers are encouraged to store log parameters with keyword arguments rather than with string interpolation into the message= argument, so that later analysis/filtering tools can take advantage of it. For example, if you use this:

log.msg(format="Uploading %(size)d byte file", size=SIZE)

instead of:

log.msg("Uploading %d byte file" % SIZE)

Then later, you can write a filter expression that can do:

def _big_uploads(e):
    return bool(e["format"] == "Uploading %(size)d byte file" and
                e["size"] > 1000)
subset = filter(_big_uploads, all_events)

Other tools will be provided in the future to make this more concise. This also makes it easier to write filtering expressions that can be serialized and sent over the wire, so that flogtool tail can subscribe to a narrowly-defined subset of events, rather than to everything.

Logfiles

Several foolscap logging tools will record a sequence of events to disk: flogtool tail --save-to FILENAME and the gatherer created by flogtool create-gatherer are two of them.

These tools know about two file formats, compressed and uncompressed. If the filename ends in .bz2, then the file is opened with the bzip module, but otherwise treated exactly like the uncompressed form. No support is provided for gzip or other compression schemes.

The uncompressed save-file format contains a sequence of pickled "received event wrapper dictionaries". Each wrapper dict is pickled separately, such that code which wants to iterate over the contents needs to call pickle.load(f) repeatedly (this enables streaming processing).

The wrapper dictionary is used to record some information that is not stored in the event dictionary itself, sometimes because it is the same for long runs of events from a single source (like the tubid that generated the event). (TODO: some of this split is arbitrary and historical, and ought to be cleaned up). The wrapper dictionary contains the following keys:

Logfile Headers

The first wrapper dict in the logfile may be special: it contains headers. This header dict is distinguished by the fact that it does not contain a ["d"] member. Instead, it contains a ["header"] member. The tools which iterate over events in logfiles know to ignore the wrapper dicts which lack a ["d"] key.

On the other hand, the first wrapper dict might be a regular event. Older versions of foolscap (0.2.5 and earlier) did not produce header dicts. Tools which process logfiles must tolerate the lack of a header dict.

The header dict allows the logfile to be used for various purposes, somewhat open-ended to allow for future extensions.

All header dicts contain a key named type that describe the purpose of the logfile. The currently assigned values for type are:

log-file-observer

The header dict produced by a LogFileObserver contains the following additional keys:

Also note that the wrapper dicts recorded by the LogFileObserver will use a "from" value of "local", instead of a particular TubID, since these events are not recorded through a path that uses any specific Tub.

flogtool tail

The header dict produced by flogtool tail contains the following additional keys:

log-gatherer

The header dict produced by the flogtool log-gatherer contains the following additional keys:

Incident Reports

An Incident Report is a logfile that was recorded because of an important triggering event: a dump of the short-term history buffers that saves the activity of the application just prior to the trigger. It can also contain some number of subsequent events, to record recovery efforts or additional information that is logged after the triggering event.

Incident Reports are distinguished by their header type: e["header"]["type"]=="incident". Their header dicts contain the following additional keys:

Index Files

No index files have been defined yet. The vague idea is that each logfile could contain a summary in an index file of the same name (but with an extra .index suffix). This index would be used by other tools to quickly identify what is inside the main file without actually reading the whole contents.

In addition, it may be possible to put a table of offsets into the index file, to accelerate random-access reads of the main logfile (i.e. put the offset of every 100 events into the index, reducing the worst-case access time to two seeks and a read of no more than 100 events). Some sort of restartable compression could make such an offset table useful for compressed files as well.

These index files would need to exist as distinct files (rather than as a header in the main logfile) because they are variable-size and cannot be generated until after the main logfile is closed. Placing them at the start of the main logfile would require rewriting or copying the whole file. Further complications are present when the main logfile is compressed.