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.
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.
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.
Domain names may only consist of the following characters:
'A'
to 'Z'
'0'
to '9'
'-'
) 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###"
.
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:
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:
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:
As such setting is always recursive, the order of setting domains is important. If the statements above were performed in reverse order:
the setting for domain '/UI/DIALOGS' to All would be overwritten by the setting of parent domain '/UI'.
The advantages of hierarchical domains so far are:
But the real reason, why hierarchical domains drive the flexibility of ALox really to a next level, becomes only obvious in the next chapter!
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.
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:
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
We have already gained some advantages from this most simple use of a Scope Domain
As we have learned above, the following two Log Statements are the same when a Scope Domain was set for Scope.Method:
But we have to be careful! The following two Log Statements are not using the same Log Domain:
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:
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!
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:
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.
false
, e.g. bool isStaticallyInitialized= false;
true
.#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:
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:
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.
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).
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:
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.
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!
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.