XLL+ (from version 6.2 onwards) contains a logging framework, cpplog, which helps you to write diagnostic and other information to log files.
The API for this framework is based on that of the Apache Foundation's log4cxx. log4cxx is more efficient, more powerful and more complete than cpplog. However, it is a lengthy package to install, needs extra steps to build, and comes with a lot of extra baggage.
The cpplog framework is designed so that it is trivial to switch from it to log4cxx, if you wish. If at any time you find that the cpplog framework is not providing the functionality or performance that you need, you should consider switching to log4cxx.
There are 3 aspects to logging using this framework, which are described in more detail in the next topics:
Your code decides which logging node - known as a logger - your logging events will be sent to. This will control the routing of the event and, therefore, control which files, streams and other log destinations the event will be written to.
Your code should include log statements, which
report a message, perhaps other state data, and a severity level.
These are always expressed in the form of macros,
such as LOG_ERROR(logger, "Something went wrong");
Configuring the logging framework.
You, or the user of your software, decides which messages will go to which logging devices, such as files, email, event logs or trace streams.
Below is a class that writes to the logging framework.
class MyClass { private: static LoggerPtr m_logger; public: void DoSomething(int value, const TCHAR* str) { LOG_INFO(m_logger, _T("About to do something with ") << value << _T(" and ") << str); LOG_TRACE(m_logger, _T("Leaving DoSomething")); } }; LoggerPtr MyClass::m_logger = Logger::getLogger(_T("MyClass"));
Note the LOG_xxx macros in the DoSomething method. The first macro means:
m_logger
."About to do something with [value] and [str]"
- and stream it to
all attached log sinks.Below is the context within which the above logging code is used:
int _tmain(int argc, _TCHAR* argv[]) { // Configure the logging framework BasicConfigurator::configure(_T("%-5p %c - %m%n"), LL_DEBUG); // Get a logger pointer and use it LoggerPtr logger = Logger::getLogger(_T("main")); LOG_DEBUG(logger, _T("Starting main")); // Create an instance of MyClass, and use it. MyClass c; c.DoSomething(15, _T("a string value")); // Write to the "main" logger LOG_DEBUG(logger, _T("Leaving main")); }
The first part configures the root logger to write events of severity level DEBUG and above to the console, using a simple formatting pattern. In effect this means that all events except TRACE events will be reported.
The second part gets a logger pointer for the logger "main", which it then uses to write a debug event.
After creating and using an instance of MyClass - which also does some logging - the last part of _tmain writes another debug message to the log.
The output to the console will be as follows:
DEBUG main - Starting main INFO MyClass - About to do something with 15 and a string value DEBUG main - Leaving main
Notice that the TRACE event from DoSomething() is suppressed, because it is of a lower severity than any of the loggers that it is sent to. All other events are reported to the console.