- 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.
The ALox tutorials (C++, C#, Java) and most sample code provide information about debug logging. The main goal of this chapter is to explain how to implement release logging. This might be easiest to explain when comparing both types of logging and listing the differences. (For a general definition see What do the terms "debug logging" and "release logging" mean?).
As previous chapters described, any logging with ALox is roughly performed as follows:
- A dedicated instance of class Lox (C++, C#, Java) is created.
- This Lox object is configured, mainly by populating it with one or more Loggers which get Verbosity set for different Log Domains.
- Then, anywhere in the source code, Log Statements which perform the logging exclusively through the interface of that dedicated object of class Lox are inserted.
Debug Logging
- Note
- A-Worx (the maker of ALox) is a consultant to the software industry and one area of competence is code style and code cleanness. We emphasize our clients to stop using temporary debug print lines in their sources. Instead, using ALox (or a similar tool) all debug output lines should be implemented tidy and clean using a nice language for the messages. With ALox, such debug output should never be temporary again and as such never be removed from the code! Programmers often remove debug output after they think a piece of code works. But if problems arise or if the code is further extended at a later stage, similar lines are inserted and re-written. This is obviously a waste of time. With ALox, you just disable certain Log Domains for which you think the code is fine and be able to reactivate them (with different verbose levels) in the moment your interest moves back to it!
Now, with debug logging, there are some specialties that do not apply in release logging:
- A dedicated, pre-created, static object of class Lox is provided for the purpose of debug logging, which is publicly accessible from virtually anywhere in the code (including external library components).
- For being able to prune (C#, Java) debug logging statements from the code, the interface to this dedicated Lox singleton is slightly different than just using the interface of the Lox object itself:
- in Java and C# Versions of ALox, class Log is wrapping all access to the dedicated, static 'debug Lox' by mimicking the interface methods of class Lox. All interface methods that are copied are the same in respect to method names and parameters, the only difference is that they are static and just forward the parameters to the static debug-Lox object.
- In C++, an own set of preprocessor macros each type of logging exist. The almost only difference is, that those macros used for debug logging (that log into the debug-Lox singleton and get pruned in release compilations), are prefixed Log_, while those that are used for release logging are prefixed Lox_.
- For convenience, ALox does not only provide a pre-configured Lox singleton for debug logging, but it also creates an appropriate debug Logger (or even two of them for certain IDEs) in the case that no other Logger was explicitly created prior to invoking the first log call.
This way, ALox allows to just start using debug logging with no 'bootstrap efforts' as shown in Tutorial: Hello ALox (C++, C#, Java).
Release Logging
From the previous discussion about the specialties of debug logging, we can now quite easily identify how release logging differs and derive a guideline on how to implement it:
- An object of type Lox (C++, C#, Java) has to be created. (In more complex scenarios two or even more of such objects might be created and used for different use cases of release logging.)
It has to be assured that each code entity that should be enabled perform release logging on this Lox object has proper access to it.
- One or more objects of (abstract) type Logger (C++, C#, Java) have to be created and attached to the release-Lox object(s), usually with setting the right Verbosity for different Log Domains.
(Both actions are usually performed at the beginning of the life-cycle of a software process, what we call the bootstrapping section of a software.)
- The interface of the lox object itself is directly used for logging. The Log Statements work and look similar to debug logging, because as already explained above:
- in Java and C# versions of ALox, the debug logging interface of class Log (C++, C#, Java) simply mimics the original interface of class Lox (C++, C#, Java), while
- In C++, an own set of preprocessor macros each type of logging exist that differ only in their prefixes Log_ and Lox_.
In C++, each code entity has to set the preprocessor macro LOX_LOX prior to using release logging macros. This can be done in a general header file of the software, (e.g. the same that exposes the release-Lox object to that source), or, in more complex scenarios with more than one release-Lox object, at any other appropriate source location.
- The language-related Scopes are not usable with release logging. It is elaborated in Language-Related Scopes, why this is not considered as a big penalty. Release executables just should not contain information about source code directories and files. In Java, it is a good advice to obfuscate release software to make reverse engineering much harder this way. For security reasons and to protect a companies or programmers intellectual property.
- Attention
- If release logging is used, the explicit initialization of ALox by invoking ALox.Init (C#, Java)) is mandatory and failure to do so might lead to critical errors and undefined behavior in release builds when debug-logging is pruned!
Further Thoughts
Pruning Release Logging
Probably just because it was easy to implement (!), the C++ version of ALox supports the optional pruning of release logging statements as well (see ALOX_REL_LOG). This might sound confusing in the first moment, but it allows to create different versions of release-executables, which is not completely uncommon. It is optional and just provides a next level of flexibility to adopt ALox to complex use cases.
Although (to keep it simple) not provided with the ALox library, the same can be achieved in the other flavors of ALox quite easily.
For ALox for C#, the recipe looks like this:
- Create a copy of class Log, e.g. naming it RLog.
- Remove redundant things, e.g. the enum declarations, ALox version number, etc.
Replace the compiler symbol of lines
[Conditional("ALOX_DBG_LOG")]
With a different, new symbol, e.g. ALOX_REL_LOG
- Use this class for release logging the same as class Log is used for debug logging.
- Add or remove the new compiler symbol to different release configurations of your C# project
For ALox for Java, the recipe looks similar:
- Create a copy of class Log, e.g. naming it RLog.
- Remove redundant things, e.g. the enum declarations, ALox version number, etc.
- Use this class for release logging the same as class Log is used for debug logging.
- Maintain an alternative configuration for your 2.2 How Pruning in ALox for Java works 'tool used for obfuscation' to either prune this class (and all invocation on objects of it) or not.
More Complex Use Cases
ALox allows to be used in various fashions. By having the concept of different Loggers aggregated in one or more Lox objects, using an hierarchical structure of Log Domains, which by default inherit their Verbosity from their parent domain (and this way automatically adjust to probably unknown sub-domains), we think that a good balance of "ease of use" and "unlimited extensibility" was found with the ALox architecture.
The simple and common scenarios incorporate:
- Debug logging with
- An appropriate Console Logger and probably an IDE specific Logger when run in an debugger
- Optionally a simple text Logger that allows to review the output of 'historic' debug sessions
- A few root-level Log Domains like "UI", "TCPIP", "DB", "IO", etc. having a maximum of one sub domains
- Optionally release logging e.g. for collecting severe errors or collecting statistical summaries from 'the field'
These scenarios can be addressed with a few lines of bootstrap code and a little discipline when inserting the Log Statements.
Various ways of matching use cases of complex scenarios with ALox exist. By purpose (to keep things simple) ALox is not providing extended classes, for example to allow pruning release logging as explained in the 'recipes' of the previous paragraph. However, once the basic concepts of ALox are understood, it is easy to build such extensions, tailored to the complex case itself.
Using ALox and Libraries
Using ALox with libraries, is basically the same as using ALox in a single code entity. However, we recommend the following principles:
- For release logging, a library should provide an initialization method that receives a reference to the dedicated release-Lox instance it is supposed to log into. If such object is not provided (which means the library is not duly initialized), the library should use ALox debug logging to notify that.
- A library should not create and set any Loggers by itself. The creation of Loggers should generally be controlled by the main source entity of a process.
- A library should document which Log Domains it is using. Furthermore it is helpful to also disclose which Verbosity is used by Log Statements domain and maybe roughly what log output might be expected. This is especially important for rather uncommon log messages, like severe warnings and errors.
- A library should expose a root-level Log Domain with optionally different sub-domains.
- A library should (by nature of all library code) make thoughtful and reasonable use of combinations Verbosity and Log Domains. This allows to control the verbosity of the library in granular way.
The ALib Report Writer
ALib, the utility library that ALox builds on, provides a concept for writing reports. This mechanism is used to report problems in debug-versions of ALib and optionally of software using ALib. With class ALoxReportWriter (C++, C#, Java), ALox implements such mechanism to direct ALib reports to an ALox Logger. This is all transparently set up and destructed with methods Log.AddDebugLogger (C++, C#, Java) and Log.RemoveDebugLogger (C++, C#, Java).
Applications that do not use debug logging may want to use methods Log.AddALibReportWriter (C++, C#, Java) and Log.RemoveALibReportWriter (C++, C#, Java) to install a report writer in debug compilations.
It is important to set the verbosity of the (internal) domain provided with method ALoxReportWriter.LogDomain (C++, C#, Java), to a proper level, to enable the logging of any reports.
A demonstration of how to do this is found in the ALox release logging sample code (language specific).
- Note
- ALox for C++ does not provide a corresponding preprocessor macro. Instead, macro Log_Prune (not Lox_Prune!) should be used to perform the the invocation of Log::AddALibReportWriter and Log::RemoveALibReportWriter in debug builds. The following code samples a proper bootstrap. First the following header has to be included by the compilation unit:
-
Then this code adds the report writer:
-
Upon termination, such report writer is to be removed as sampled here:
Other differences of Debug and Release Logging
Appendix reference chapter II - ALox Auto-Configuration and Orthogonality might hint to other differences of debug and release logging.
Next chapter: 11 - Internal Logging Back to index