io7m-jlog 3.1.0
io7m-jlog 3.1.0 Documentation
Package Information
Orientation
Overview
The jlog package provides a simple API for configurable program logging.
The API exposed to the programmer essentially has the programmer build an (implicit) tree of log outputs and pass one output to each relevant program component. The leaves/branches of the tree can be individually enabled/disabled at runtime from a simple Java properties file. This allows the programmer to specify exactly which debugging messages they require at any given time, preventing the typical flood of messages when setting high verbosity levels for traditional UNIX programs.
Simplicity
The entire library consists of around 700 lines of pure Java, and includes selectable log levels and precise control over logging from program components. All non-essential "logging" features are rejected: No timestamps, no automatic log rotation, no logging to databases or mail systems - all of these features should be provided by external programs or libraries [0].
Installation
Source compilation
The project can be compiled and installed with Maven:
$ mvn -C clean install
Maven
Regular releases are made to the Central Repository, so it's possible to use the io7m-jlog package in your projects with the following Maven dependency:
<dependency>
  <groupId>com.io7m.jlog</groupId>
  <artifactId>io7m-jlog-core</artifactId>
  <version>3.1.0</version>
</dependency>
All io7m.com packages use Semantic Versioning [1], which implies that it is always safe to use version ranges with an exclusive upper bound equal to the next major version - the API of the package will not change in a backwards-incompatible manner before the next major version.
Platform Specific Issues
There are currently no known platform-specific issues.
License
All files distributed with the io7m-jlog package are placed under the following license:
Copyright © 2014 <code@io7m.com> http://io7m.com

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
        
Usage
Concepts
Log interfaces accept messages and conditionally log those messages to an output stream based on both the current log level and whether or not the specific log interface is enabled or disabled.
Log interfaces are named and form a hierarchy [2]. If the root log interface is named main and has three children a, b, and c, then the absolute destinations of the children are main.a, main.b, and main.c, respectively.
Specific log interfaces can be enabled and disabled programatically, but the default status of each can be specified by a policy. The package provides a simple policy implementation backed by Java properties.
Usage
Initialization
First, an initial log interface - a specific implementation of the LogType interface - must be created. Instantiating a log interface requires a policy to determine the default settings for logging. The io7m-jlog package provides a simple logging implementation named Log. It provides multiple policy implementations, the simplest of which is the LogPolicyAllOn type, which simply enables all logging and requires the programmer to specify the initial log level. The root log also requires a name. In this case, the name main is chosen, but any name that does not contain a dot (U+002E) can be used.
LogPolicyType policy =
  LogPolicyAllOn.newPolicy(LogLevel.LOG_DEBUG);
LogType log =
  Log.newLog(policy, "main");
Logging
At this point, messages logged to the interface will appear on Java's current standard error stream.
log.debug("A debug message");
log.critical("A critical message");
Logging is completely thread-safe. Multiple threads may log to the same log interface, or call any of the log interface functions without explicit synchronization.
Only messages of a level greater than or equal to the current log level are logged.
Hierarchy
Programs with multiple subsystems will want to take advantage of the hierarchical nature of io7m-jlog interfaces. For example, a program with three subsystems - renderer, audio, and filesystem - would be structured such that each subsystem receives its own log interface (and can create new children of that log interface as required):
LogType log_r = log.with("renderer");
LogType log_f = log.with("filesystem");
LogType log_a = log.with("audio");

RendererType r = Renderer.newRenderer(log_r);
AudioSystemType a = AudioSystem.newAudio(log_a);
FilesystemType f = Filesystem.newFilesystem(log_f);
The renderer can then, for example, create further children using the log_r interface it receives, allowing for potentially extremely fine-grained logging.
Callbacks
The io7m-jlog package allows for the association of at most one callback function with each root log interface. The given callback will be executed on receipt of any log message. This is useful for, for example, intercepting all log messages so that they can be logged to a Swing console window in graphical programs.
log.setCallback(new CallbackType() {
  @Override public void call(
    final LogConfigReadableType log,
    final LogLevel level,
    final String message)
  {
    SwingUtilities.invokeLater(new Runnable() {
      @Override public void run()
      {
        StringBuilder m = new StringBuilder();
        m.append(level.getName());
        m.append(": );
        m.append(message);
        text_area.appendText(m.toString());
      }
    });
  }
});
Property-based policies
Experience has shown that the hierarchies of log interfaces created when using the io7m-jlog package typically match the structure of the program using them. That is, the hierarchies are often completely static, and logging policy is usually decided upon program startup and then not changed. The io7m-jlog package therefore provides an implementation of the policy interface named LogPolicyProperties that reads settings from a given set of properties.
The LogPolicyProperties type takes a set of Properties and a prefix string p. The implementation will then attempt to read from a key named p.level to determine the current log level, falling back to LOG_DEBUG if the key does not exist. It then attempts to read from all keys with names beginning with p.logs to determine the default state of log interfaces. For example:
com.io7m.example.level         = LOG_CRITICAL
com.io7m.example.logs          = true
com.io7m.example.logs.main     = true
com.io7m.example.logs.main.a   = false
com.io7m.example.logs.main.a.c = true
Given the above properties and a prefix com.io7m.example, the default log level will be LOG_CRITICAL. An interface created with name main will be enabled by default, but a child a of that interface (main.a) will not. If a specific entry does not exist for a given name, the closest ancestor specified in the properties is used. Therefore, a child b of main (main.b) does not have an entry in the above properties, and the closest ancestor of main.b is main, so the default state of true is inherited.
If there is no ancestor available (consider a new root interface named other), then the default value (given by the key p.logs) is used, defaulting to true if the key does not exist.
The types of the properties are checked at run-time (so it is an error to specify, for example, an integer value where a boolean is expected), and are loaded eagerly on construction of the policy. Later modifications to the given Properties will not be noticed by the policy.
Capabilities
The LogType interface is actually a composite of several interfaces, giving programs fine-grained control over what separate parts of the program are allowed to do with each interface [3].
The LogType interface consists of the following sub-interfaces:
If, for example, a function requires the ability to write log messages and does not require the ability to affect the logging configuration, it should accept a value of type LogWritableType instead of the full LogType interface.
The LogUsableType is provided as a convenience. It allows for the writing of log messages, the creation of log interfaces, but does not allow for the modification of the current log level or other configuration settings.
API Reference
Javadoc
API documentation for the package is provided via the included Javadoc.

[0]
The package provides an utterly trivial callback interface to allow external packages to execute code upon receipt of log messages, if absolutely necessary.
[2]
A directed acyclic graph.