ALox  V. 2402 R. 0
Home ALox for C++ ALox for C# ALox for Java Download
II - ALox Auto-Configuration and Orthogonality
Attention
In respect to the C++ Version of ALox, this manual is outdated. C++ Users, please visit ALib C++ Library.
The deep-link (we hope this still works, otherwise please quickly find it yourself from the above library link above) should be this.

Introduction

ALox addresses two fundamental 'use cases', namely debug logging and release logging (For a general definition see What do the terms "debug logging" and "release logging" mean?). While technically both are similar, from a conceptual perspective the two are very different things.

Looking into the head of a software developer that thinks about debug logging, you will find the following thoughts:

  • "I will quickly write some output routines and if not needed comment them out or just delete them."
  • "Don't bother me, I am not willing to read a user manual for generating simple debug output."
  • "My boss will not pay me for writing nice debug log output, so I do not care."
  • "I have a debugger and comment lines in my code, so I do not urgently need a logging library."
  • "I don't care too much if logging misbehaves or even crashes, this happens only in debug-versions of my software".

⇒ We understand that a logging library has to be easy!

Now release logging:

  • "I have to obey legal rules, logging is a key component of my server"
  • "Collecting usage metrics and detecting failures in the field is a key to success of my app"
  • "Using any external library needs deep and thorough understanding of this. I am paid for making sure this works."
  • "More features of a logging library is always better than less."

⇒ We understand that a logging library has to be feature rich and reliable!

This discrepancy easily justifies the use of two different libraries for both use cases. ALox takes a different approach: it tries to match the needs of both worlds. There are two fundamental design rules in ALox that lead to a solution:

1. Orthogonality of ALox and its API.
Orthogonality here means that features that are not used, are not visible when using ALox. This is reached by optional parameters with reasonable default values, many overloaded variants of methods and by taking internal prerequisites when ALox 'detects' that a feature is not used.

2. Auto-configuration.
This means not only that missing configuration is defaulted, but also that ALox is not pedantic but designed rather non-intrusive. For example, logging into a 'new' Log Domain does not need to anyhow 'register' or 'create' such domain. Many things in ALox just happen on the fly.

Most of ALox auto-configuration features apply to debug logging only and are implemented by the dedicated, auto-created Lox instance used for debug logging and also by the dedicated API interface used for debug logging (which enables the automatic pruning of debug-logging from release code).

This sounds nice, and leads to the fact that this most simple sample works without any further bootstrapping of ALox (here, C# code sampled):

using cs.aworx.lox;
class HelloALox
{
public void SayHello()
{
Log.Info ( "Hello ALox!" );
}
}

But it has a little drawback: Hiding features to make an API simple reduces the understanding of the API - and this is what a user of release-logging requires. And this is the goal of this appendix chapter: We try to collect, list and name concepts of ALox that otherwise might be a little hidden for good reasons.

Note
The list might not be too well sorted and certain points may seem to be off-topic. Please excuse.
Note
Many of the things that happen 'automatically' or 'on the fly' can be observed by activating the internal logging as described in 11 - Internal Logging.

2. List of Automatic Configuration and Orthogonal Features

2.1 Initialization, Lox and Logger Instances

2.1.1 ALib/ALox Init

To perform a thorough bootstrap, ALox itself as well as underlying utility library ALib (C++, C#, Java) each provide a static initialization method ( ALIB.Init (C#, Java) and ALox.Init (C#, Java)).

Note
In the C++ version, the bootstrapping of the library with is documented here.

ALox.Init invokes ALib.Init internally. With debug-logging, it is best practice to explicitly invoke ALox.Init and provide optional parameters as needed. However, if omitted, ALox detects that (in different ways in the different language versions of ALox) and fixes this providing default values. In addition, ALox is tolerant to multiple invocations. Those are simply ignored.

As described in the API reference documentation linked above, external custom configuration source plug-ins ( see namespace aworx::lib::config (C++, C#, Java) and ConfigurationPlugin (C++, C#, Java)) have to be added to the libraries' Configuration (C++, C#, Java) prior to invoking ALox.Init - especially in the case that configuration variables read by the initialization methods and defined in these custom sources should be taken into account.

Note
In the C++ version, the initialization is performed in different phases. Therefore, the addition of user defined configuration plug-ins with C++ is not done "prior" to initialization, but in the right phase. See documentation of lang::Camp (C++) for details.
Attention
If release logging is used, the explicit initialization of ALox is mandatory and failure to do so might lead to critical errors and undefined behavior only in release builds when debug-logging is pruned!

The same applies for methods ALIB.TerminationCleanUp (C++, C#, Java) and ALox.TerminationCleanUp (C++, C#, Java).

But missing to invoke them on upon termination of a process does not have other effects than causing harmless memory leaks. (Harmless, because the end of a process makes the operating system clean up memory anyhow. OK, this again is not true for some obscure operating systems originating from the wild 1960ies...)

2.1.2 The Lox Singleton for Debug-Logging

For debug logging, a pre-configured instance of class Lox is created and used. More information on this is given in chapter alox_man_debug_and_release_logging.

2.1.3 Adding and Removing Loggers to a Lox

While method Lox.RemoveLogger (C++, C#, Java) is available for removing a Logger from a Lox instance, there is no method for explictly adding one. Instead, the method that allows to control the Verbosity of a Log Domain and its sub-domains for a Logger, Lox.SetVerbosity (C++, C#, Java) adds a Logger in the moment the one provided is not known to the Lox, yet. Consequently, the very first time a Verbosity of a Logger is set, the overloaded version of Lox.SetVerbosity which takes a reference to the Logger as parameter has to be used. In subsequent invocations, alternatively, the name of the Logger can be provided.

2.1.4 Logger Names

Each Logger attached to a Lox has to have a unique name. Nevertheless, Loggers can be constructed without explicitly providing a name. In this case, a default name which is different with each derived type of Logger is used. This way, many standard use cases do require the user to think about the name of Loggers. For example if a Logger that logs to the console should be used in parallel to one that logs into a text file, both Loggers will have different names.

However, if for example two different loggers of type TextLogger (C++, C#, Java) are to be added to one Lox, then with their creation, a dedicated, distinguishable name has to be provided.

In addition, when reading Verbosity settings from external configuration (as described in 13 - External Verbosity Configuration), it is advisable to explicitly name Loggers, so that external configuration data stay "compatible" in the moment a different Logger type is used for a certain task. On the other hand: this is not necessary for the standard debug Logger created automatically or explicitly by calling Log.AddDebugLogger (C++, C#, Java) (see more info on this below), because such Loggers are always named "DEBUG_LOGGER".

Lox.RemoveLogger (C++, C#, Java) is available for removing a Logger from a Lox instance, there is no method for explictly adding one. Instead, the method that allows to control the Verbosity of a Log Domain and its sub-domains for a Logger, Lox.SetVerbosity (C++, C#, Java) adds a Logger in the moment the one provided is not known to the Lox, yet. Consequently, the very first time a Verbosity of a Logger is set, the overloaded version of Lox.SetVerbosity which takes a reference to the Logger as parameter has to be used. In subsequent invocations, alternatively, the name of the Logger can be provided.

2.1.5 Using a Logger with Multiple Lox Instances

If a Logger is set with multiple Lox instances, using ALib class SmartLock (C++, C#, Java), it is automatically protected against concurrent access in multi-threaded applications.

2.1.6 Console Loggers and Debug Logger

Method Lox.CreateConsoleLogger (C++, C#, Java) creates an appropriate type of Logger depending on the operating system, IDE used (if in IDE), implementation language, configuration variable, etc. The goal get a maximum logging experience, e.g. colorful logging if the console supports colors.

This method can be used to create a Logger whenever a console logger is needed (also for release-logging of-course).

Furthermore, method Log.AddDebugLogger (C++, C#, Java) uses this method to attach a Logger to the Lox singleton used for debug-logging. In addition, if running in an IDE, ALox tries to detect the IDE and optionally (if available) creates a second, IDE specific Logger. See reference documentation of the methods linked above for more details.
For both Loggers, root domain '/' is set to Verbosity.Verbose. In addition, the Verbosity for internal ALox messages is set to Verbosity.Warning. (More information on internal ALox messages is found in 11 - Internal Logging.)

Finally, method Log.AddDebugLogger is automatically invoked internally in the moment that the Lox singleton used for debug-logging is used and no other Logger was attached, yet.

Note
The exclamation to this rule is when a Logger was added to the internal domain tree only. In this case, the normal standard domain tree is still free of loggers and therefore, a debug logger is automatically attached.

2.2 Source Path Trimming

If no Source Path Trim Rules are given a default rule is automatically detected and registered.
If Source Path Trim Rules are given, path separators '/' and '\' are corrected to suit the platform of compilation (C++), respectively of execution (C#).

For more information see chapter 14 - Trimming Source File Paths and Clickable IDE Output.

2.3 Log Domains and Verbosity

Log Domains are created on the fly as needed (used). Log Domain, once created, are never deleted and a user does not need to care about their management. New, unknown Log Domains inherit their Verbosity from their parent, unless optional parameter priority of method Lox.SetVerbosity (C++, C#, Java) is used.

Furthermore, when obtaining Verbosity settings from external configuration, a natural prioritization is applied: First INI file settings, which are overwritten by environment variable settings, which are overwritten by command-line parameters. All of them are overwriting settings made invocations of Lox.SetVerbosity in the source code - as long as optional parameter priority is not given.
More information on this is found in 13 - External Verbosity Configuration.

2.4 Logging Text and Objects

2.4.1 Arbitrary Logables

ALox API transparently hides the fact that it is made for logging arbitrary objects - not just text messages. Features for logging text is just an application build on the concept of logging abstract 'Logables'. This is not only true for the Logables provided in Log Statements, but also for Prefix Logables. More information about logging arbitrary objects is found in A - Loggers and Implementing Custom Types.

2.4.2 Namespace 'textlogger'

When logging text, classes found in %[cs::|com::]aworx::lox::detail::textlogger (C++, C#, Java) are doing quite a bit of things in the background that are not obvious to the user. For example:

  • ALox ESC codes (C++, C#, Java) are removed from textual Logables if a Logger does not support them. (For more information see 16 - Colorful Loggers.)
  • Classes derived from Textlogger by default recognize multi-line text in Logables (in standard line ending, Windows line ending or mixed mode) and format multi-line text accordingly.
  • With the help of utility class AutoSizes (C++, C#, Java), class TextLogger formats the log output into columns that grow over time. In the moment a write-enabled configuration plug-in is attached (e.g. an INI file), such sizes are restored when a process is re-launched. Also automatic shrinking happens. This is done when next detects that the tabulator and field sizes of a previous session it used, have been higher than needed after all. In the next run, the smaller sizes are restored. The read and write of the values is performed in methods TextLogger::AddAcquirer (C++, C#, Java) and TextLogger::RemoveAcquirer (C++, C#, Java) which are invoked when a Logger is added, respectively removed from a Lox.
  • The standard output streams (e.g. in C++ 'std::cout' and 'std::cerr') are locked against concurrent write operations as soon as more than one Logger is active. This avoids clutter that is created by intermixing log statements coming from different threads. It is also possible to synchronize such output with other entities (non-ALox) that write to these streams. In this case, locking takes place already when only one Logger which uses the streams is established. See ALIB.StdOutputStreamsLock (C++, C#, Java) for more information on how to do that.

2.4.3 C++ Strings

In ALox for C++, logging strings leverages the string facilities of underlying ALib, which allows to transparently pass just any sort of string object to Log Statements, including references or pointers to almost any custom string type - without any sort of prior conversion! This is achieved using 'template meta programming' of C++ 11. Information on how to adopt custom string types is found here: ALib Strings.

Log
lox::Log Log