I've had some trouble with Boost Log that I can't overcome. That coupled with the fact that it actually is not part of the core of Boost, but have to be built separately have made me change my mind about the logging I will use with the Shadow World server.
Instead I will have to implement my own logging, which will of course take some more time but on the other hand should work on any platform. To help me with this I will use the Boost iostreams library.
Design
All of the logging functionality will be in the log namespace. There will be a function to initialize the logging system, and a function to clean up everything.
Logging will not, unlike most other logging systems, have different levels. Instead it will be channel-based, and it should be possible to enable or disable channels at compilation time, boot time and run time.
From the start, the different channels should be:
- debug
- info
- boot
- network
- error
The debug channel is for debugging messages. It will normally be turned off at boot time.
The info channel is for general information.
The boot channel is for startup and shutdown messages
The network channel is for messages pertaining to the network system.
And the error channel is for run time errors of different kinds.
Output from the logging channels will by default be printed to std::clog, but other destinations will also be possible (including, but not limited to, files, POSIX system log, Windows system log).
Implementation details
I know I said that I would keep implementation details out of this series of posts. However there is one thing I will use for the logging system that I think is important for all C++ programmers to know.
If you have been programming C++ for some time, you would certainly come across the term RAII, or Resource Acquisition Is Initialization. In short it means that to handle resource you tie them to objects, because when an object goes out of scope its destructor is called and it releases the resource automatically.
The use of RAII in the logging system is not so much about handling resources, as it's about using the idiom that when an object goes out of scope then its destructor is called to "automatically" clean up after itself.
If we If we take the (modified) example of some logging from the previous post:
log::info() << "Starting Shadow World version " << version::tag << '\n';
Here the function log::info() returns an object based on std::ostream, and that object is only temporary and will be destructed as soon as the statement is done. This means that our temporary object can write out the logging header (date and time together with channel name), let the C++ stream objects handle the actual output, and when the destructor is called at the end of the statement it flushes the whole output to whatever logging sinks are used for the channel.