ALox  V. 2402 R. 0
Home ALox for C++ ALox for C# ALox for Java Download
A - Loggers and Implementing Custom Types

1. Class Logger

ALox is designed to support different log streams. A log stream is a destination for the Logables and can be the IDE console, the terminal console, a file, a web service or anything that accepts data streams. Abstract class Logger (C++, C#, Java) represents such a log stream. While some specializations of the class are provided with ALox, custom specializations may log information to any 'data drain' in any format you like.

Abstract class Logger (C++, C#, Java) has a very lean interface, in fact it is basically just one method, which in specialized classes needs to be implemented to execute a log. The data to log is provided as an array of class Object in C# and Java, respectively in C++ an instance of class Boxes, which is a container of arbitrary objects.

Note
C++ programmers that seek to implement a custom logger should be familiar with ALib Boxing classes.

This allows to create Loggers that take any type of (binary) data and use the data for writing a log entry in any custom way.

While all ALox code tries to be short and simple, class Logger is particularly simple! So, let us quickly walk through the class by looking at its members.

1.1 The fields of class Logger

There are just a few fields in class Logger (C++, C#, Java):

1.1.1 Name and TypeName

class Logger has two members, Name (C++, C#, Java) and TypeName (C++, C#, Java) that can be read using. GetName (C++, C#, Java) and GetTypName (C++, C#, Java).

Field name is defined by the user and provided with the constructor. Field typeName is 'hard-coded' by each derived classes constructor. If the user of a Logger does not provide a name in the constructor, then field name defaults to the hard-coded typeName. Both fields are not used internally but only provided to be used by classes managing multiple Loggers (which generally is class Lox (C++, C#, Java)).

If multiple Loggers are attached to a Lox, they need to to have unique Names.

1.1.2 TimeOfCreation and TimeOfLastLog

These are two time stamps that contain the time of the creation of the Logger (C++, C#, Java) (or the time this timestamp was reset to) and the time of the last log. These two members are normally used to calculate the elapsed time (the cumulated time an application is running) and the time difference between two log calls. The latter is especially interesting for log outputs on a debug console, as it allows to get for a first quick impression about your software's performance, lock states, bottlenecks, etc.

1.1.3 CntLogs

This is a simple counter of the number of logs processed so far. Feel free to reset it as you like, it is not used anywhere internally, other than as an option to output with each log line.

1.2 Methods of class Logger

Besides the protected constructor, which just initializes some default values for the Loggers' fields, abstract method Log (C++, C#, Java) is the most important.

Derived classes only need to implement this abstract method with code that is executing the Log Statement. When the method is invoked, class Lox already performed various checks, including that the Verbosity justifies the execution.

1.3 Implementing a custom <em>Logger</em>

As an experienced programmer after reading the previous sections, it is certainly fully obvious to you which steps need to be taken to create your own variation of class Logger (C++, C#, Java) that directs your Logables to a certain data drain in a specific format. Nevertheless, let us quickly name these steps explicitly:

  • Create your own class derived from class Logger.
  • Add a constructor that takes a user-defined name and pass this name along with a hard-coded string representing the type of your Logger, to the base constructor.
  • Implement the abstract method Log by converting and streaming the given objects into a custom data 'drain'.
  • In the main code entity add an instance of your custom Logger type to the Lox (C++, C#, Java) of your choice.

But before you now go ahead and implement your own Logger type class, you should first continue reading through this chapter.

2. Abstract class TextLogger

When you think about debug log output you think about logging textual messages that get displayed on your debug console. We think the use of a logging ecosystem for this quite simple purpose is advisable as soon as a software project bigger than two pages of code!

While ALox wants to be a perfect tool for quick, easy and comfortable debug logging, the goals of ALox go beyond that. This is why ALox logging interface methods are not restricted to string types, but accept any object to be passed to any derived Logger type.

It is very important to understand this. The result is twofold:

  • For textual (e.g. debug) logging, any object that is logged needs to get converted into a textual representation (a human readable format).
  • Custom Logger types are enabled to log data of an application in a very custom way, as these Loggers get the original object passed.

Class TextLogger (C++, C#, Java), which is described in this chapter, is exactly about the first thing: Log any object that is passed to it as a textual representation, hence into a character stream. All of the provided ALox Loggers that produce text output, derive from this base class instead of deriving directly from Logger. Among these classes are ConsoleLogger (C++, C#, Java), MemoryLogger (C++, C#, Java) and AnsiLogger (C++, C#, Java). Hence, the class diagram above is a little simplified. It rather looks like this:

Note
  • Even this diagram is simplified. Refer to the language specific reference documentation of class TextLogger (C++, C#, Java) to get the full picture.
  • Of-course you can also derive your own Logger types without using class TextLogger and still do pure textual logging.

2.1 Helper classes for TextLogger

Class TextLogger (C++, C#, Java) contains two helper classes as public fields. The advantage of using helpers is that they can be replaced at runtime by your own, tailored versions of these helpers and this way you can change the default behavior of existing Logger types, like without deriving new ones.

The helper classes are described in the following two paragraphs.

2.1.1 Class MetaInfo

Class MetaInfo (C++, C#, Java) is used by class TextLogger (C++, C#, Java) to assemble the meta information of each log line, which incorporates things like date and time, thread information, Verbosity and Log Domain. MetaInfo provides a public format string that defines the start (prefix) of each log line. A sample of such format string (in ALox for C#) is:

"%SF(%SL):%SM()%A3[%DD] [%TD][%TC +%TL][%tN]%V[%D]<%#>: "

The format string contains variables, marked by a leading '%' sign. The set of these format variables available are:

Variable
Description
SP
The full path of the source file (in Java: The callers' package name).
Sp
The trimmed path of the source file (in Java not available).
SF
The callers' source file name.
Sf
The callers' source file name without extension (in Java not available).
SC
The callers' class name (Java only!).
SL
The line number in the callers' source file.
SM
The callers' method name.
TD
The date the log call was invoked.
TT
Time of day the log call was invoked.
TC
Time elapsed since the Logger was created or its timer was reset.
TL
Time elapsed since the last log call. Note: These time values do not sum up correctly. This is not only because of rounding errors, but also because the time between the "end" of the last log call and the "beginning" of this log call is measured. This has the advantage that a quite accurate value of "elapsed time since the last log call" is displayed and hence a very good performance indicator is provided.
tN
Thread name
tI
Thread ID.
V
The Verbosity. For the display of the different values, MetaInfo exposes four public fields containing string definitions.
D
The Log Domains' full path.
#
The log call counter (like a line counter, but counting multi-line log output as one).
An
An auto-adjusted tabulator. This grows whenever it needs to grow, but never shrinks. The mandatory number n (a character between 0 and 9) specifies how much extra space is added when tab is adjusted. This is useful to achieve very clean column formatting.
LG
The name of the Logger. This might be useful if multiple loggers write to the same output stream (e.g. the console).
LX
The name of the Lox.
P
The name of the process / application.
Note
The available format variables vary slightly across programming languages and platforms supported by ALox. The details are documented in the corresponding class reference of MetaInfo.Format (C++, C#, Java).
Note
Class TextLogger prepends the meta information to the decoded Logables and therefore, the message itself is not provided as a formatted string variable. This restriction (that the meta information is always prepended) is a result of the otherwise smart and helpful multi-line features of TextLogger (see below).

Changing the format string MetaInfo.Format (C++, C#, Java) provides an easy way to change the look of your log output. For example, if you are not interested in thread information, just remove the "[%tN] " part from the original string.

If you want to modify the output of a certain variable or if you want to add your own variables, you can derive your on implementation of MetaInfo and override the virtual method MetaInfo.processVariable (C++, C#, Java) Within the implementation, just fetch your own variables and/or modify existing and call the original method for the rest that you do not want to modify.

Finally, if you really want to customize the logging of meta information in the class TextLogger completely and maybe do not want to even rely on a format string, then feel free to derive your on implementation of MetaInfo and override the virtual method MetaInfo.Write (C++, C#, Java)!

2.1.2 Class ObjectConverter

Class ObjectConverter (C++, C#, Java) is used by class TextLogger (C++, C#, Java) to convert the Logables that get passed by the user through the Log Statements to the Logger into a string representation. While ObjectConverter is abstract and declares only one simple interface method, the standard implementation used with the built-in loggers is provided with type StandardConverter (C++, C#, Java).

This class is still is extremely simple, as it transfers this responsibility to objects of type derived from class Formatter! This abstract class and corresponding implementations are provided with ALib, the utility library that ALox builds upon.

Please consult the ALib documentation of classes Formatter (C++, C#, Java), FormatterPythonStyle (C++, C#, Java) and FormatterJAVAStyle (C++, C#, Java) to learn more about how these classes work.

Again, there are different "levels" of possibility on how to change and implement custom functionality in respect to converting the Logables while using class TextLogger:

  • A straight forward approach is to overload methods [T|t]oString (Java/C#), respectively ALib Boxing interfaces FAppend or FFormat.
  • Own formatters may be implemented and attached to the instance of StandardConverter.
  • Alternatively an own object converter might be implemented.
Note
In C# and Java versions of ALox, the logging of arbitrary complex objects is additionally supported with class LogTools (C#, Java).
This class makes use of the reflection APIs of these languages and creates a nicely formatted output. Using this class is an alternative to the creation of a dedicated ObjectConverter for such types.

2.2 The multi-line features of class TextLogger

Class TextLogger (C++, C#, Java) provides a feature to log a message into multiple lines. This is useful for example, if you want to log a string that contains XML text. Instead of one very wide log line, TextLogger is configured by default to separate the text into multiple lines in a very controlled way.

Multi-line output behavior of class TextLogger is configured by the field TextLogger.MultiLineMsgMode (C++, C#, Java).

The following modes are available:

  • 0: Multi-line mode off
    In this mode, the text is not split into multiple lines. However, certain log streams, for example a system console window, will split the msg autonomously if the message contains line end characters (CR, LF or CRLF). Therefore, using this mode, it is recommended to set the fields TextLogger.MultiLineDelimiter (C++, C#, Java) and TextLogger.MultiLineDelimiterRepl (C++, C#, Java) in a way that your log file does not get cluttered.
  • 1: Multi-line mode with all meta information repeated in each log line
  • 2: Multi-line mode with blanked meta information starting with second log line.
    This is the default, because this setting makes it easy to visually recognize, that a log is continued in the next line and hence it is the best for debug window purposes!
  • 3: Multi-line mode which writes a (configurable) headline message in the first log line.
    All text lines of the multi-line text is logged starting in column zero.
  • 4: Like multi-line mode 3, but without writing the headline.

2.3 Deriving your own Logger from class TextLogger

As mentioned above, class TextLogger (C++, C#, Java) is still abstract. While it implements the abstract interface method Logger.Log (C++, C#, Java) it introduces a new abstract interface method in turn! This method is TextLogger.logText (C++, C#, Java). Class TextLogger takes care to build the complete textual representation of the log line, including meta information and the log message itself.

Now, the only thing that a simple derivate of TextLogger needs to do is to override this method and just copy (write) the provided buffer to its final destination.

Those custom Loggers that wish to ignore any special formatting and colorizing codes of class ESC (C++, C#, Java) might rather want to extend abstract class PlainTextLogger (C++, C#, Java) which takes care of the removal of such codes. Class PlainTextLogger implements TextLogger.logText and introduces in turn other abstract methods. As a simple sample of how PlainTextLogger can be derived, see the source code of class MemoryLogger (C++, C#, Java).

On this topic, see also chapter 16 - Colorful Loggers.

2.4 Locking the Standard Output Streams

Class TextLogger (C++, C#, Java) optionally uses a facility of ALib to avoid concurrent access to the standard output stream and standard error stream available to most applications.

This feature is described in the reference documentation of ALIB.StdOutputStreamsLock (C++, C#, Java).

To enable the use of this locker for a custom types, all that has to be done is providing value true for parameter usesStdStreams of the protected constructor of class TextLogger.

If an application writes to those streams in parallel to ALox (e.g. using std::cout in C++, Console.WriteLine in C# or System.out.println in Java), then to avoid mixing such output with ALox output, such direct writes should be performed only after the ALIB.StdOutputStreamsLock was 'acquired'. Also, such application has to register once with ALIB.StdOutputStreamsLock. Then, together with the Logger, the critical number of two 'acquirers' are reached and the SmartLock (C++, C#, Java) gets activated.

2.5 Recursive Log Statements

ALox supports recursive log calls. Recursion occurs when log statements are executed during the evaluation of the logables of an already invoked log statement. A user might think that recursive log calls are seldom and exotic, however, in reality, recursive calls might occur quite quickly.

To allow and properly treat recursion, each class and method involved in the execution of a log statement has to be prepared for it. The execution must be free of dependencies to member variables or such members need to be created per recursion.

Hence, not only class class Lox (C++, C#, Java) needs to support recursion, but also the logger classes themselves.

Class TextLogger (C++, C#, Java) and its utility class StandardConverter (C++, C#, Java) are well prepared and as long as custom loggers are built on those, recursion should not be a problem. This is because abstract method TextLogger.logText will not be invoked recursively.

When implementing own variants of class ObjectConverter or otherwise a "deeper" use of provided classes is done, the possibility of recursion of log calls has to be taken into account.

3. Logger implementations provided by ALox

While the abstract classes Logger, TextLogger and PlainTextLogger are located in the namespaces (packages)

  • [com.|cs.]aworx.lox.detail (C++, C#, Java) and
  • [com.|cs.]aworx.lox.detail::textlogger (C++, C#, Java)

you can checkout which 'ready to use' Logger implementations are available today for your preferred language version of ALox, by referring to the reference documentation of namespace (package)

  • [com.|cs.]aworx.lox.loggers (C++, C#, Java).

For convenience, method Lox.CreateConsoleLogger (C++, C#, Java) is provided. This method chooses an appropriate implementation of class Logger suitable for human readable log output. The Logger chosen depends on the platform and configuration settings.

For debug logging, method Log.AddDebugLogger (C++, C#, Java) is provided. This may even choose and attach more than one Logger, depending on language, platform and IDE.

A noteworthy, built-in specialization of Logger is found with class MemoryLogger (C++, C#, Java). It uses an internal character buffer of ALib-type AString (C++, C#, Java) and just appends each log entry to this buffer, separated by a new line sequence.

As MemoryLogger does not log to the console or any other slow thing, is extremely fast. The latest record was over on million log entries per second in a single-thread! (Achieved on Intel(R) Haswell Core(TM) i7 CPU @4.0GHz, using ALox for C++, doing release logging.)

This gives an indication that the ALox ecosystem, in combination with its MemoryLogger is extremely useful in performance critical sections of your code. If you would do 100 log entries per second, the performance loss for your application would be around 0.01%. (Yes, that is why we love Bauhaus coding style.)

4. Summary and outlook

The following summarizes the takeaways of this chapter:

  • We learned about class Logger and its simple structure and few fields.
  • Class TextLogger was introduced and explained in detail. We learned that TextLogger aims to be the abstract parent class of all Logger types that are supposed to process Logables in a human readable format.
  • We saw how the output format of class TextLogger can be manipulated without introducing new code and also, more complex but powerful, with introducing new code.
  • Furthermore, it was explained that ALox, with the help of underlying utility library ALib, provides a mechanism to lock standard output streams and protect against 'clashing' of output coming from different loggers or the application itself.
  • Then we briefly looked at the currently existing Logger types in the ALox ecosystem.

If you developed an interesting Logger, like one that

  • is supporting a specific IDE with 'clickable' log lines
  • is sending Emails
  • is reporting to an analytics/metrics server
  • is logging into the Linux journal or Windows OS event log

then please do not hesitate to propose the code to us as an extension of the open source project ALox!


Back to index