ALox  V. 2402 R. 0
Home ALox for C++ ALox for C# ALox for Java Download
04 - Log Domains

This chapter provides all details on Log Domains. Before reading it, be sure that you have read and understood the chapter 03 - Terminology and Key Concepts.

1. Why Log Domains?

Let us quickly recapture what was explained already on Log Domains in the previous chapter and the tutorials. It is very common to logging libraries to provide a system that allows to control the verboseness of the log output, by matching what ALox calls a Verbosity associated to Log Statements. ALox allows to divide the overall set of Log Statements into sub-sets that belong to different topics or 'areas of interest'. These topics are called Log Domains and each Log Statement has one associated. Furthermore, ALox allows to have multiple different Loggers performing each Log Statement in parallel. Now, ALox stores the Verbosity per combination of Log Domain and Logger.

The result is, that the Verbosity can be different not only in respect to each Log Domain but also, the same Log Domain may have a different Verbosity set for different Loggers.

The method to set such Verbosity which is found (in different, overloaded versions) in class Lox is simply called SetVerbosity(). As you see in the reference documentation of Lox.SetVerbosity (C++, C#, Java), this method as a minimum requires a value for the Logger that is affected and the Verbosity (C++, C#, Java) that is to be set. The third parameter, the Log Domain defaults to "/" if not given. Omitting parameter domain with this method tells ALox to set all Log Domains to the same Verbosity.

Note
The fourth parameter of method SetVerbosity found in the reference documentation is not covered in this chapter. The impatient reader might refer to 13 - External Verbosity Configuration.

This probably sounds more complex than it is. If you think about it for a second, it should become obvious. As a sample, this mechanism allows to log warning and error messages of an application of just any Log Domain to a file to have that log available across different debug-sessions, while the IDEs' console output is much more verbose in respect to those Log Domains a programmer currently is interested in.
Or, as another example, we could consider a Logger that sends alert Emails to system administrators. Of-course, Emails should be sent only for very special events, e.g. when something really severe happens. You may collect these severe errors in a special domain "/ADMIN" (real-time administration errors) and exclusively activate this domain for this Email-sending Logger.

1.1 Allowed Characters

Domain names may only consist of the following characters:

  • Upper case letters 'A' to 'Z'
  • Numbers '0' to '9'
  • Hyphen ('-') and underscore ('_')

If one of this characters is used, ALox replaces this character with # (which, as stated above is otherwise equally not allowed) without further notice (no internal log statement is given). As a result, the provision of illegal domain names "ABCa$)" or "ABC<=>" will result in the same domain name "ABC###".

2. Hierarchical Log Domains

One of the major design goals of ALox is simplicity. By nature simplicity is sometimes in in competition with flexibility or feature richness of an API. One way to resolve such conflicting goals is to provide features that - if not wanted or needed or not even known - are invisible to the user. The concept of hierarchical Log Domains is a good sample of how this was achieved in the ALox API design.

Consider an application with a user interface. Within the code that implements the UI, a lot of Log Statements using different Verbosities may be embedded using Log Domain 'UI'. Depending on the complexity of an application, switching domain 'UI' to a more verbose level (by setting the Verbosity of Log Domain 'UI'), might already lead to a huge amount of Log Statements. Of-course, the solution is to split that domain into several ones, e.g. one for UI dialogs, one for manipulations of the main window, one for menu events and one for mouse interactions, etc. Therefore, splitting domains in sub-topics is a very common use case as soon as code becomes more complex. To support this use case, ALox organizes Log Domains hierarchically. The sample would lead to:

Every method in ALox that accepts a Log Domain as a parameter, a domain path can be used. Domains in a path are separated by character '/'. As soon as domain parameters contain path separators, the hierarchical domain system of ALox takes action.
The Log Domains' paths in our sample would be named:

  • '/UI'
  • '/UI/DIALOGS'
  • '/UI/WINDOW'
  • '/UI/MENU'
  • '/UI/MOUSE'

Setting the Verbosity of a Log Domain is always done recursively for all its sub-domains (and sub-sub-domains). Let us look at a sample, how an ALox user would setup the domain tree, when debugging.

First, he/she might decide to allow only error messages in general:

Log.SetVerbosity( Log.DebugLogger, Verbosity.Error, "/" ); // could also just omit parameter "/"

As the interest is currently in domain '/UI/DIALOGS', he/she would first switch '/UI' to a Verbosity that logs error, warning and info statements and then switch sub-domain '/UI/DIALOGS/' to the most verbose level:

Log.SetVerbosity( Log.DebugLogger, Verbosity.Info, "/UI" );
Log.SetVerbosity( Log.DebugLogger, Verbosity.Verbose, "/UI/DIALOGS" );

As such setting is always recursive, the order of setting domains is important. If the statements above were performed in reverse order:

Log.SetVerbosity( Log.DebugLogger, Verbosity.Verbose, "/UI/DIALOGS" );
Log.SetVerbosity( Log.DebugLogger, Verbosity.Info, "/UI" );

the setting for domain '/UI/DIALOGS' to All would be overwritten by the setting of parent domain '/UI'.

Note
In some situations, it might be wanted to be able to protect a domain setting and make it 'immune' against subsequent settings of parent domains. This is especially important when configuration is done 'outside' the source code, hence using configuration files, command line parameters and such, but could be also helpful otherwise. This protection mechanism is explained in chapter 13 - External Verbosity Configuration.

The advantages of hierarchical domains so far are:

  • You can control a whole set of domains with one statement
  • If parts of a software that is out of your control (other team, code in a library, etc.) introduces new Sub-Log Domains, no changes of Verbosity settings in your code parts need to be done (as long as you are not interested in directly manipulating this new sub-domain).

But the real reason, why hierarchical domains drive the flexibility of ALox really to a next level, becomes only obvious in the next chapter!

3. Scope Domains

People might argue that providing a Log Domain to each Log Statement is an overhead of typing. That is true. For this reason - and many more - ALox provides the concept of Scope Domains.

Scope Domains are set using method Lox.SetDomain (C++, C#, Java). This method, along with parameter scopeDomain, requires a parameter of type Scope (C++, C#, Java) that determines the 'Scope' that the given domain path should default to.

Such Scope is assembled of two parts: one part that is taken from the source code information that ALox collects at compile-time (in Java done at runtime) with each Log Statement. The other part is related to the thread that is executing the Log Statement, hence is evaluated at runtime.

Attention
For this chapter, explaining the benefit of being able to assign Scope Domains to Scopes, we are simplifying ALox Scope features to a bare minimum. We are deferring the full explanation of possible Scope settings to chapter 05 - Scopes in ALox.

We start really simple and look at Scope.Method (C++, C#, Java) which denotes that the Scope Domain given with method Lox.SetDomain (C++, C#, Java) should apply to all Log Statements of the current method. Here is a sample:

public void MyMethod()
{
Log.SetDomain( "/MYDOM", Scope.Method );
Log.Info( "This log statment uses domain /MYDOM" );
}

This sets the domain "/MYDOM" as the Scope Domain for this method. As a result, a Log Statement that does not provide a domain uses the Scope Domain. The output could be:

UT_dox_manual.cs(58):MyMethod()     [/MYDOM]: This log statment uses domain /MYDOM
Note
For this and the following samples, ALox' default meta information output was reduced.

We have already gained some advantages from this most simple use of a Scope Domain

  1. Less typing (we can omit the Log Domain)
  2. Less 'clutter' in the code, so better readability (Log Statements serve as comment lines)
  3. When the Log Domain should be changed, this has to be done only at one point in the code
  4. When code including Log Statements is copied and pasted into a different Scope, the Log Domain adjusts to the destination Scopes' Scope Domain.

4. Absolute and Relative Domain Paths

As we have learned above, the following two Log Statements are the same when a Scope Domain was set for Scope.Method:

public void MyMethod()
{
Log.SetDomain( "/MYDOM", Scope.Method );
Log.Info( "This log statment uses domain '/MYDOM'" );
Log.Info( "/MYDOM", "Of-course we can still use domains explicitly" );
}

But we have to be careful! The following two Log Statements are not using the same Log Domain:

public void MyMethod()
{
Log.SetDomain( "/MYDOM", Scope.Method );
Log.Info( "This log statment uses domain 'MYDOM'" );
Log.Info( "MYDOM", "Oooops, this goes to '/MYDOM/MYDOM'!" );
}

As can bee seen in the output:

UT_dox_manual.cs(86):MyMethod()            [/MYDOM      ]: This log statment uses domain 'MYDOM'
UT_dox_manual.cs(87):MyMethod()            [/MYDOM/MYDOM]: Oooops, this goes to '/MYDOM/MYDOM'!

The difference of the sample is that while we previously always used a '/' at the beginning of domains, now for parameter domain of the last Log Statement this was omitted. This caused ALox to concatenate the domain provided in the Log Statement with the Scope Domain set, to a new domain path.

This mechanism does not need too much further explanation, as this is similar to file system path usage: A Log Domain path starting with '/' is called an absolute path and one that omits the '/' character is called a relative path.

Same as with file system paths, a relative path can also be expressed with a single dot. As a Sample, domain paths "DOM" and "./DOM" are the same. The use of a leading extra 'dot'-domain in the path to signal relative domains is optional and probably a matter of taste.
More important is that ALox Log Domains also support two dots '..' in relative path names for addressing a parent domain. Later in this chapter, a sample for addressing a parent domain is shown.

While the previous sample code looked more like an erroneous use of a domain (resulting in domain '/MYDOM/MYDOM'), the following sample shows a use case of a relative domain path which absolutely makes sense:

public void ReadChangeAndWriteBack()
{
Log.SetDomain( "/IO", Scope.Method );
// Reading file
Log.Info( "READ", "Reading file" );
// ...
// ...
// ...
// Process file
Log.Info( "PROCESS", "Processing data" );
// ...
// ...
// ...
// Writing file
Log.Info( "./WRITE", "Writing file" ); // note relative-path-prefix "./", same as if omitted (!)
// ...
// ...
// ...
Log.Info( "Success!" );
}

The output would be:

UT_dox_manual.cs(121):ReadChangeAndWriteBack()        [/IO/READ    ]: Reading file
UT_dox_manual.cs(127):ReadChangeAndWriteBack()        [/IO/PROCESS ]: Processing data
UT_dox_manual.cs(133):ReadChangeAndWriteBack()        [/IO/WRITE   ]: Writing file
UT_dox_manual.cs(138):ReadChangeAndWriteBack()        [/IO         ]: Success!

5. Inner and Outer Scopes

The previous samples used Scope.Method. Another important Scope 'level' is Scope.Filename (C++, C#), respectively in the Java version of ALox, Scope.CLASS (Java).

As mentioned before, we are not going into the details of the extended Scope definition of ALox (those are given in 05 - Scopes in ALox), but what is very important to understand is that scopes are nested into each other. We talk about outer scopes and inner scopes. As you might guess, Scope.Filename (resp. in Java Scope.CLASS) is an outer Scope of Scope.Method.

Now, Scope Domains which can be set (or not) per Scope level, are concatenated to a complete domain path. ALox starts from the 'most inner' Scope and then prepends (!) the paths of outer Scopes as long as relative domain paths are given. In other words, as soon as an absolute path is found as a Scope Domain for a scope, the further concatenation of outer Scopes' Scope Domains is stopped.

We are using the same sample as above, but this time, the public interface method internally is split into different parts:

class IO
{
// static constructor, setting the Scope Domain for this file (class) once
static IO()
{
Log.SetDomain( "IO", Scope.Filename );
}
// interface
public void ReadChangeAndWriteBack()
{
checkSetup();
read();
process();
write();
writeStats();
}
// private methods
private void checkSetup()
{
Log.SetDomain( "/CHECKS", Scope.Method );
Log.Info( "Setup OK!" );
}
private void read()
{
Log.SetDomain( "READ", Scope.Method );
Log.Info("Reading file" );
}
private void process()
{
Log.SetDomain( "PROCESS", Scope.Method );
Log.Info( "Processing data" );
}
private void write()
{
Log.SetDomain( "./WRITE", Scope.Method );
Log.Info( "Writing file" );
}
private void writeStats()
{
Log.SetDomain( "../STATS", Scope.Method );
Log.Info( "Statistics" );
}
}

The first important note on the source is that this class has a 'static constructor' which is executed exactly once for the life-time of the process and which sets the Scope Domain 'IO' for the entire class by using Scope.Filename.

Note
For users of ALox for C++:
This is a C# sample. A static constructor in C# is executed automatically, once a class is first used. Similar semantics is available in the Java language.
In C++ one might come to the idea to implement this using a function whose result is assigned to a static variable. This would get executed in an 'automatic' fashion. But attention: this is not allowed. Generally, it is considered bad code style to use such static construction, because the language does not define the behavior properly and different compilers and operating systems (when initializing dynamic libraries) handle such static initialization differently. As a result, it is not assured if ALox and ALib itself are initialized, yet (the truth is, they are definitely not!).
Now, the good news is that ALox does not mind if a Scope Domain is set multiple times. So a first alternative is to put the statement just to the constructor of the class and have ALox execute it every time an instance of the class is created.
If it is a very time-critical constructor, invoked many times, one recommended way to implement a similar behavior in C++ as we have in C# and Java with static constructors is:
  • Add a static boolean variable to the class with default value set to false, e.g.
    bool isStaticallyInitialized= false;
  • In the (all) constructor(s) of the class check for this variable. If not set, invoke the Scope Domain setting for the source file and flag the boolean variable to true.
  • If all is related to pure debug-logging, wrap the variable definition and the initialization statements in each constructor into pairs of preprocessor directives
    #if defined(ALOX_DBG_LOG)
    ...
    #endif

When sample method ReadChangeAndWriteBack() is executed, this output is generated:

UT_dox_manual.cs(174):checkSetup()      [/CHECKS    ]: Setup OK!
UT_dox_manual.cs(182):read()            [/IO/READ   ]: Reading file
UT_dox_manual.cs(189):process()         [/IO/PROCESS]: Processing data
UT_dox_manual.cs(196):write()           [/IO/WRITE  ]: Writing file
UT_dox_manual.cs(203):writeStats()      [/STATS     ]: Statistics

With the takeaways from the features explained already in this chapter, the code is quite self-explanatory. Some remarks:

  • As explained and observable in the output, relative domains are concatenated from inner scopes to outer scopes. Samples are the domains '/IO/READ', '/IO/PROCESS' and '/IO/WRITE'
  • Like in the previous sample, method write() volunteers to use prefix "./" to signal that the domain path is relative. This is a matter of taste and the result is the same as if this prefix was omitted like in methods read() and process().
  • Method checkSetup() uses an absolute domain path for its Scope. This tells ALox to stop prepending any Scope Domains of outer Scopes.
  • Method writeStats() uses a relative domain pathstarting addressing the "../". parent domain. The effect is that the outer Scope domain "IO" is removed. The effective path is: '/IO/../STATS' which ALox of-course shortens to just '/STATS'.
    Hence 'STATS' is a sub-domain of the root-domain, the same as domain 'CHECKS' is. Note that, if there were other Scope Domains in Scopes that are 'more outer' than Scope.Filename, the 'depth' of both domains would be different.



6. Further Information and Wrap-Up

6.1 Where to set the Verbosity

The usual way to use ALox is to separate the code that sets the Verbosity of Log Domains into a single location, normally somewhere in the 'boot-strapping part of a process. This is probably the same location where the Loggers used and also Lox objects are created and configured.

It is of-course tempting to quickly (temporily) increase the Verbosity of the Log Domain(s) that a code unit uses while working on that code unit. Method Lox.SetVerbosity (C++, C#, Java), properly uses the current Scope, so that a statement like this:

Log.SetVerbosity( Log.DebugLogger, Verbosity.Verbose, "./" );

properly sets the evaluated Scope Domain. However, if such code lines are inserted, they should be duly marked as 'temporary' and 'to-be-removed'.

Especially when working in a team, it is even more convenient to not even set the Verbosity of any domain from within the code, but by using external configuration data.

Note
To keep this chapter as simple as possible, all samples herein exclusively used the method Lox.SetVerbosity and you may not even have heard, yet, about other possibilities. Information about how ALox is configured externally, is completely separated into an own chapter of this manual: 13 - External Verbosity Configuration.

The separation has good reasons: A portion of the code that logs using a certain set of domains should not modify these Log Domain's Verbosity. One day such code could be moved into a library and then such changes are merely irreversible (well, in-fact this misbehavior can be 'healed', but this is also only explained in chapter 13 - External Verbosity Configuration).

6.2 Why Does Verbosity Setting Always Work Recursively?

It was explained in section Hierarchical Log Domains, that setting the Verbosity is always done recursively for all sub-domains of the given Log Domain. The reason for this is to avoid 'random' settings as much as possible.

For convenience, Log Domains do not need to be 'registered' with ALox. As soon as an unknown Log Domain is used in a Log Statement, ALox creates a reference to this Log Domain and for each Logger in the corresponding Lox the Verbosity of the parent Log Domain is inherited.

Now, if a Log Domain setting was allowed to be non-recursive, the setting of a sub-domain could have different values:

  • If it was known to ALox already (because it was used in a Log Statement), it would keep the Verbosity that it previously received by implicitly inheriting from its parent.
  • If it was implicitly registered with ALox after such non-recursive setting, it would still inherit its parents setting.

Therefore, the order of the use of a Log Domain would decide about its Verbosity.

One of ALoxs' design goals is to hide features that are not used and which are unnecessarily increasing complexity of using ALox if they are not needed (Further explained in II - ALox Auto-Configuration and Orthogonality). For this reason, within this chapter of the manual (which is all about understand what domains Log Domain just are), a parameter of method Lox.SetVerbosity (C++, C#, Java) that can be omitted because it has a default value, was not even named. Using this parameter allows to restrict the setting of the Verbosity of to a domain and and allows to include arbitrary sets of sub-domains or not, by assigning a priority to each Verbosity setting.

Apart from lifting the always recursive Verbosity setting, the main motivation for introducing this concept resulted from the general need to prioritize conflicting settings which may come from different sources of configuration data. Therefore, if non-recursive settings are needed for any reason, refer to chapter 13 - External Verbosity Configuration for explanations about how to achieve this.

6.3 Absolute or Relative Domain Paths?

As we learned, ALox allows to use absolute or relative Log Domain paths. This is true for Scope Domains as well as for parameter domain in a Log Statement.

The general recommendation is to use relative paths. There are several advantages which all arise from the fact that ALox assembles the Scope Domain path by looping through all Scopes from the most inner to the most outer. As soon as an absolute path is found, the loop is aborted and concatenation stops. The benefits of keeping all Log Domain paths relative are highlighted in the next chapter (05 - Scopes in ALox) when all about Scopes is explained - including an important use case that justifies absolute Log Domain paths!

6.4 ALox Internal Log Domains

ALox uses 'itself' to log information about its use. This is helpful e.g. to observe where and when a Log Domain is used for the first time or where and when a Verbosity setting is performed.

To keep the 'tree of Log Domains' clean and reserved exclusively for application specific Log Domains and to avoid accidental activation of internal log messages, ALox manages the internal Log Domains in a separated tree.

Information about how to 'address' this tree and when to use it, is explained in chapter 11 - Internal Logging.



Next chapter: 05 - Scopes in ALox
Back to index
Verbosity
Verbosity
Log
lox::Log Log
Scope
Scope