What does the code do
The ‘LogSystem’ class can be used by developers to add powerful logging capabilites
in their MFC applications. It offers the following features:
- Use of arbitrary number of log levels
- Use of arbitrary number of log files
- Fully customizable mappings between log levels and
log files - Efficient synchronization mechanism for file writing
- VERY small overhead, easy to use, robust, flexible and well documented code
Now let me explain the system with particular referrence to the above features.
Some applications ( such as the one I am currently working on) require extensive
logging with various log levels. How you define a log level ofcourse may vary from
application to application but the reason to have it is to be able to specify how
fine grained you want the logs to be. As an example, for my application I can set
log level 0 to be the highest log level and define it as the log level for entering
and exitting some MAJOR methods (the methods that are encapsulate the main
functionalities of my application). Similarly, I set log level 1 to be a bit
more fine grained and define it as the log level for important steps in those
major methods. These steps may span more than one minor method calls. Furthermore,
I define log level 2 as the log level for entering and exitting minor methods. And
finally, I define log level 4 as the log level for MFC & windows api call results.
I define these four ! log levels for normal logs. For error log, I can have a
seperate log level for each error type (warning, serious, or fatal) or have one
log level for all of them or assign any of these error logs to any of the log levels
I have already defined.
Once I have done this (which is very easy to do as you’ll see) I can run my application
at any log level and the logs will be done based on it. For example, if I set the
application to run at log level 2, all the logs messages belonging to log levels 0-2
will be logged and the others wont. One thing to keep in mind is that the a log level
is just a UINT to the log system and thats it. You just supply it to one of the
‘logXXX()’ ( logNormal(), logWarning(), logSeriousError(), or logFatalError() )
methods. The actual meaning of a log is what you want to give it. The log system
just uses the log level to determine which log file the log is written to.
Each log level is mapped to one file. You can have any number of log levels and log
files but the number of log files has to be less than or equal to the number of log
levels. This is because there is a one to many mapping between a log file and a log
level (and this, in my opinion, is the only mapping that makes sense so this was done
on purpose.) For example from the above example, I want to have three log files. So
the four log levels have to be mapped to either of these log files. Once the mapping
is done logs of that level will be written to that file. The default mapping mechanism
maps log levels to files on a one to one basis and in case there are more levels than
files, the rest of the log levels will be mapped to the last file. So if I have four
log levels and three log files this is how the default mapping would be done:
Log Level | Log File |
0 | 0 |
1 | 1 |
2 | 2 |
3 | 2 |
Note: Log levels and Log files MUST always begin at 0. This is because
the log system keeps an array of these and array indexes are zero based.
Even though for most of you, the default mapping will be OK. But you can change
the default mapping at ANY time during the life of your application (not just
during initialization) using the ‘setLogLevelToFile()’ method and from that
point and on, logs of that level would be written to that file.
The reason I say that file writing syncronization mechanism is efficient is because
during writing, log files are locked on a per file basis. So if you have 3 log files
then suppose three message handling threads are currently spawned in your application
and each of them calls the logXXX() method at pretty much the same time then they can
do that in parallel. This is reason I have an inner class called ‘LogFile’ in the log
system. This inner class defines a boolean called ‘bLocked’ which is set to TRUE when
some thread is writing to that file. This approach is better than doing the writing
in a critical section because setting a boolean’s value is a much much faster operation
than writing to a file.
All those praises that I wrote about log system (small overhead, easy to use,
robust …) may or may not be true in your opinion but since it’s my best shot,
and Ive started using MFC only a month ago, in my opinion those comments are true 🙂
Just take a look at the source and see for yourself.
How to use it in your application
Please dont complain if I make it toooo simple! Im only trying to help 🙂
1. Include the header file “LogSystem.h” in your application (CWinApp derived)
class header and implementation file
2. Declare a ‘CLogSystem’ object and a accessor method in your application class
header file as shown below
private: CLogSystem m_appLog; public: CLogSystem* getAppLog() { return &m_appLog; }
3. In the default constructor of your application class, initialize
the log system by passing a LogInfo structure
LogInfo logInfo; logInfo.nLogFiles = 1; logInfo.nLogFileSize = 256 * 1024; logInfo.nLogLevels = 2; logInfo.strAppName = _T("My Application ver 1.0"); logInfo.strAppTag = _T("WDEClientUI"); logInfo.strLogFilesPath = _T("C:\PROGRAM FILES\" "MICROSOFT VISUAL STUDIO\MYPROJECTS\TEMP\"); if( !m_appLog.initialize( logInfo ) ) AfxMessageBox("Couldnt intialize log system!");
The ‘nLogFileSize’ is currently not used by the log system. However when I have
time, I will implement log file backup and recreate mechanism which will backup
the log file when it’s size exceeds this value and start logging in a new empty
file
The ‘strAppName’ is used by the log system when it writes the header of a log
file. The format of the header is like this:
<APPLICATION Name> Log File Started at: <UNIX time and date>
The ‘strAppTag’ is prepended to the names of the log files in the log file
generation algorithm ‘getLogFileName()’
The ‘strLogFilesPath’ is where the log files will be created. This directory
must already exist
Now you’re ready to use the log system! Below are a couple of examples for logging
in your main application:
Example1:
((CLogSystem*) getAppLog())->logNormal( 0, "In InitInstance() of the application");
Example2:
((CLogSystem*)getAppLog())->logSeriousError( 1, "Now going to create the document view and frame of the application" );
Here are a couple of examples to do logging in any class other than the main application:
Example1:
((CLogSystem*)((CTempApp*)AfxGetApp())->getAppLog())->logFatalError(2, "In the constructor for the main frame" );
Example2:
((CLogSystem*)((CTempApp*)AfxGetApp())->getAppLog())->logWarning(3, "In OnCreate() of the main frame" );
If you want to change the current log level, use setLogLevel( nLogLevel ). The log
message belonging to log levels greater than nLogLevel wont be logged then.
Other APIs of interest
- If you dont like the default log level to file mapping mechanism, change
the doDefaultMappings() method - If you dont like the default file name generation mechanism, change the
getLogFileName() method - If you dont like the default log file header change the openLogFiles()
method - If you dont like the formatting of log messages change the log() method
- If you dont like this log system, well, dont use it 🙂
Limitations and possible extensions
- The supplied path for log files should be created if
it doesnt exist
- There should be a backup and recreate mechanism
- The log header should be customizable Instead of
using a log file name generation algorithm, the LogInfo struct could have an
array of log file names
- There should be support for exception handling and error checking
(specially for open, writing to, and closing log files)
File Notes
- It is important to extract the source code to “C:”. Winzip will extract
it in “C:Program filesMicrosoft Visual StudioMyProjectsTemp” - The executable can be stored in any directory but “C:Program
filesMicrosoft Visual StudioMyProjectsTemp” must exist otherwise the log
files will not be created