Introduction
I do a lot of behind the scenes programming (no GUI) either in
DLLs or DCOM/COM objects, and its not as
easy to figure out any problems that occur if you can't see what
is going on. Therefore, I try to log
as many areas of my programs as I can. In this article I will
give you a simple logging class that you
can modify to suit your own needs.
To MFC, or not to MFC?
That is the question. I decided to derive the CLogFile
class from CFile
instead
of using the File APIs because CFile
is much easier to use.
If you want to modify the code to use the
API functions, have at it.
Why Fixed-Length?
Fixed-Length logging, where every record in the log
is the same size in bytes, is easy to read with your eyes as
well as with your applications. Each field in the record is
also given a fixed-length. When the file is opened
in Notepad you can easily distinguish each field as well as
each record. If your application needs to read the log file,
a fixed-length log is much easier for parsing records.
The biggest drawbacks to fixed-length logging is limited
information. Since you are limited by the size of the
field, you may not always have enough room for important
information. Make sure you give your fields enough space
before you start using the log.
LogRecord Structure and #defines
#define RECORDSIZE 140
#define MAXRECORDS 11
struct LogRecord
{
char cDate[10];
char cTime[8];
char cModuleName[20];
char cProcName[20];
char cDescription[80];
};
LogRecord
is used to read and write all of the fields in the record.
I chose 140 bytes
as the size of each record (RECORDSIZE
) and 11
records per log file (MAXRECORDS
).
RECORDSIZE
is 2 bytes larger than LogRecord
to
account for a carriage return and
line feed at the end of each record. If you change
RECORDSIZE
you must reflect that change in
the size of your LogRecord
fields and vise versa.
The CLogFile Class
As noted earlier, CLogFile
inherits from CFile
.
Public methods/members:
CLogFile();
CLogFile(LPCSTR filename, LPCSTR archivename = "");
virtual ~CLogFile();
void Set_cFileName(LPCSTR filename);
LPCSTR Get_cFileName();
void Set_cArchiveName(LPCSTR archivename);
LPCSTR Get_cArchiveName() { return cArchiveName; };
void Set_cModuleName(LPCSTR module);
void Set_cProcName(LPCSTR proc);
BOOL AddRecord(LPCSTR description);
Protected methods/members:
BOOL bArchiveIfMaxReached;
char cFileName[MAX_PATH + 1];
char cArchiveName[MAX_PATH + 1];
char cModuleName[20];
char cProcName[20];
LogRecord HeaderRec;
long lCurrentRecord;
BOOL OpenLogFile(LPCSTR filename);
void Set_HeaderRec();
BOOL AddHeaderRecord();
To get started, include LogFile.h and
declare a CLogFile
object.
Use Set_cFileName(LPCSTR filename)
to set
the path and filename of the log file and
Set_cModuleName(LPCSTR module)
to set the
Module name (I just use AfxGetApp()->m_pszAppName
).
CLogFile
supports archiving.
Use Set_cArchiveName(LPCSTR archivename)
to
set the path and filename of the archive file and also to
set bArchiveIfMaxReached = TRUE
.
If you do not archive, the first record in the file will
be deleted for each new record surpassing the
MAXRECORDS
limit (FIFO - first in first out).
At the beginning of a procedure that will utilize the log,
call the Set_cProcName(LPCSTR proc)
method, passing in the name of your procedure. To add
a log record, call AddRecord(LPCSTR description)
and pass in the text you would like to see in the
log file's description field.
The log file is opened before each record is added
and closed directly afterwards so your app won't keep it
open in case of a catastrophic failure.
Conclusion
The sample application I included, creates
C:\mylog.log and C:\mylog.archive so you can see how the logging
works first hand. There are more fields that I could have added,
like error codes or record ids, but I will leave that up to you.
Happy logging!
I have been a professional developer since 1996. I live in Illinois, in the USA. I am married and have four children.