65.9K
CodeProject is changing. Read more.
Home

Fixed-Length Logging

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.58/5 (11 votes)

May 14, 2002

3 min read

viewsIcon

84543

downloadIcon

1627

A simple class derived from CFile that will handle many of your logging needs.

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

// maximum size of each record
#define RECORDSIZE  140     

// maximum number of records in the log (including header)
#define MAXRECORDS  11      

struct LogRecord
{
    char cDate[10];         // date of record entry
    char cTime[8];          // time of record entry
    char cModuleName[20];   // name of the exe the record 
                            //is associated with

    char cProcName[20];     // name of the procedure 
                            //generating the record

    char cDescription[80];  // description of the event 
                            // being logged

    // leave 2 bytes for \r\n at the end of the record
};

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;  // archive the log file
char cFileName[MAX_PATH + 1]; // log file full path
char cArchiveName[MAX_PATH + 1]; // archive file full path
char cModuleName[20]; // name of the module using this log
char cProcName[20]; // name of the procedure generating the record
LogRecord HeaderRec; // header record
long lCurrentRecord; // the number of the last record in the log

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!