|
|
//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1993.
//
// File: log.cxx
//
// Contents: Simple logging support.
//
// History: 02-08-94 DavidMun Created
//
//----------------------------------------------------------------------------
#include <headers.hxx>
#pragma hdrstop
#include "jt.hxx"
//
// Forward references for private funcs
//
static const CHAR *GetTimeStamp(); static const CHAR *GetCpuStr(); static const CHAR *GetVideoStr();
//+---------------------------------------------------------------------------
//
// Member: CLog::CLog, public
//
// Synopsis: Initialize member variables
//
// Arguments: [szTestTitle] - title of test being logged
// [szLogFile] - filename to use if LOG_TOFILE used
// [flDefaultDestinations] - LOG_TO* bits to use if none are
// specified in call to Write()
// [flLogInfoLevel] - if bitwise and of this mask and bits
// passed to Write != 0, line is
// logged.
//
// Modifies: All member vars.
//
// History: 02-11-94 DavidMun Created
//
//----------------------------------------------------------------------------
CLog::CLog( const CHAR *szTestTitle, const CHAR *szLogFile, ULONG flDefaultDestinations, ULONG flLogInfoLevel) { // make copies of test title and log filename
strncpy(_szTestTitle, szTestTitle, sizeof(_szTestTitle)); _szTestTitle[sizeof(_szTestTitle) - 1] = '\0'; strcpy(_szLogFile, szLogFile);
// copy other args
_flDefaultDestinations = flDefaultDestinations; _flInfoLevel = flLogInfoLevel;
// Zero count of each type of message logged
_cLogPass = 0; _cLogFail = 0; _cLogWarn = 0; _cLogStart = 0; _cLogInfo = 0; _cLogSkip = 0; _cLogAbort = 0; _cLogError = 0; _cLogOther = 0;
//
// Indicate that we haven't logged the header yet, and that logging of
// header and footer is not suppressed.
//
_fLoggedHeader = FALSE; _fSuppress = FALSE;
InitializeCriticalSection(&_critsec); }
//+---------------------------------------------------------------------------
//
// Member: CLog::~CLog, public
//
// Synopsis: Log the footer before terminating
//
// History: 02-11-94 DavidMun Created
//
//----------------------------------------------------------------------------
CLog::~CLog() {
//
// Make sure the footer is the last thing logged, unless its being
// suppressed.
//
if (!_fSuppress) { _LogFooter(); } }
//+---------------------------------------------------------------------------
//
// Member: CLog::SetFile, public
//
// Synopsis: Set the filename to use when LOG_TOFILE is specified
//
// Arguments: [szNewFilename] - new file
//
// Modifies: [_szLogFile]
//
// History: 02-11-94 DavidMun Created
//
//----------------------------------------------------------------------------
VOID CLog::SetFile(const CHAR *szNewFilename) { strcpy(_szLogFile, szNewFilename); }
//+---------------------------------------------------------------------------
//
// Member: CLog::SetFile
//
// Synopsis: Wide char wrapper
//
// History: 04-06-95 DavidMun Created
//
//----------------------------------------------------------------------------
VOID CLog::SetFile(const WCHAR *wszNewFilename) { CHAR szNewFilename[MAX_PATH + 1];
wcstombs(szNewFilename, wszNewFilename, MAX_PATH); SetFile(szNewFilename); }
//+---------------------------------------------------------------------------
//
// Member: CLog::SetInfoLevel, public
//
// Synopsis: Set infolevel mask bits
//
// Arguments: [flNewInfoLevel] - new mask
//
// Returns: Previous infolevel
//
// Modifies: [_flInfoLevel]
//
// History: 02-11-94 DavidMun Created
//
//----------------------------------------------------------------------------
ULONG CLog::SetInfoLevel(ULONG flNewInfoLevel) { ULONG flOldInfoLevel = _flInfoLevel; _flInfoLevel = flNewInfoLevel & ~LOG_DESTINATIONBITS; return flOldInfoLevel; }
//+---------------------------------------------------------------------------
//
// Member: CLog::_LogHeader, private
//
// Synopsis: Called the first time Write() is invoked, and never called
// again.
//
// Modifies: [_fLoggedHeader]
//
// History: 02-11-94 DavidMun Created
//
//----------------------------------------------------------------------------
VOID CLog::_LogHeader() { if (_fLoggedHeader) { return; }
//
// _fLoggedHeader MUST be set before calling Write to avoid infinite
// recursion!
//
_fLoggedHeader = TRUE;
//
// Write the header.
//
Write(LOG_TEXT, ""); Write(LOG_START, "Header"); Write(LOG_TEXT, BANNER_WIDTH_EQUALS); Write(LOG_TEXT, " Run of '%s' starting at %s", _szTestTitle, GetTimeStamp()); Write(LOG_TEXT, ""); Write(LOG_TEXT, " Processors: %s", GetCpuStr()); Write(LOG_TEXT, " Video: %s", GetVideoStr()); Write(LOG_TEXT, ""); Write(LOG_TEXT, BANNER_WIDTH_DASH); Write(LOG_END, ""); }
//+---------------------------------------------------------------------------
//
// Member: CLog::Write
//
// Synopsis: Write the printf style arguments to the destinations
// specified in [flLevelAndDest] iff the bitwise and of
// [_flInfoLevel] and [flLevelAndDest] != 0.
//
// Arguments: [flLevelAndDest] - LOG_TO* bits and at most 1 infolevel bit.
// [szFormat] - printf style format
// [...] - args for printf
//
// History: 02-11-94 DavidMun Created
//
//----------------------------------------------------------------------------
VOID CLog::Write(ULONG flLevelAndDest, const CHAR *szFormat, ...) { EnterCriticalSection(&_critsec);
if (!_fLoggedHeader && !_fSuppress) { _LogHeader(); }
static CHAR szLastStart[BANNER_WIDTH + 1]; va_list varArgs; ULONG flDestinations; FILE *fp = NULL; CHAR szMessage[CCH_MAX_LOG_STRING]; // _vsnwprintf of args
CHAR szToLog[CCH_MAX_LOG_STRING]; // prefix plus message
CHAR szCurLine[BANNER_WIDTH + 1]; CHAR *pszNextLine; ULONG cchPrefix; ULONG cchToLog;
//
// If the caller set any of the destination bits in flLevelAndDest, then
// they override the default destinations in flLogDefaultDestinations.
//
// Return without logging anything if flLevelAndDest has no destination
// bits set AND the default destination bits are also cleared. Also do
// nothing if the intersection of infolevel bits in flLevelAndDest and
// _flInfoLevel is nil.
//
flDestinations = flLevelAndDest & LOG_DESTINATIONBITS;
if (0 == flDestinations) { flDestinations = _flDefaultDestinations;
if (0 == flDestinations) { LeaveCriticalSection(&_critsec); return; } }
if (0 == (flLevelAndDest & _flInfoLevel)) { LeaveCriticalSection(&_critsec); return; }
//
// If we've reached this point then the message will be logged. sprintf
// the args into szMessage.
//
va_start(varArgs, szFormat); _vsnprintf(szMessage, CCH_MAX_LOG_STRING, szFormat, varArgs); szMessage[CCH_MAX_LOG_STRING - 1] = '\0'; va_end(varArgs);
//
// If we're starting a new section, close out the previous one if
// necessary, then output a blank line to separate the new section from
// the old one visually. Save the new section name. Note strncpy does
// not guarantee null termination.
//
if (flLevelAndDest & LOG_START) { if (szLastStart[0] != '\0') { Write((flLevelAndDest & ~LOG_START) | LOG_END, ""); } Write((flLevelAndDest & ~LOG_START) | LOG_TEXT, ""); strncpy(szLastStart, szMessage, CCH_MAX_START_MESSAGE); szLastStart[CCH_MAX_START_MESSAGE - 1] = '\0'; }
if (flDestinations & LOG_TOFILE) { fp = fopen(_szLogFile, "a"); }
if (flLevelAndDest & LOG_TEXT) { //
// LOG_TEXT strings are special: they are not wrapped, prefixed, or
// counted. Logging them is therefore easy: just write szMessage.
//
if (fp) { fprintf(fp, "%s\n", szMessage); }
if (flDestinations & LOG_TOCONSOLE) { printf("%s\n", szMessage); }
if (flDestinations & LOG_TODEBUG) { OutputDebugStringA(szMessage); OutputDebugStringA("\n"); } } else { //
// Generate the prefix for this log entry, then fill cchPrefix with
// its length. This will be the amount to indent portions of the line
// that must be wrapped.
//
// If flLevelAndDest has LOG_START then szLastStart has just been set.
// If it has LOG_END, then szLastStart has the message from the last
// time a LOG_START was logged. In either case the message has
// already been included in the prefix, so don't append it to szToLog.
// Also, in the case of LOG_END, zero out the last start message,
// since it's part of the prefix now, and the next LOG_START will
// think this LOG_END wasn't logged if szLastStart isn't empty.
//
// Otherwise flLevelAndDest has neither LOG_START nor LOG_END, so the
// string to log will be the prefix and the message.
//
_LogPrefix(flLevelAndDest, szLastStart, szToLog); cchPrefix = strlen(szToLog); if (0 == (flLevelAndDest & (LOG_START | LOG_END))) { strncat(szToLog, szMessage, CCH_MAX_LOG_STRING - cchPrefix); szToLog[CCH_MAX_LOG_STRING - 1] = '\0'; } else if (flLevelAndDest & LOG_END) { szLastStart[0] = '\0'; } //
// szToLog contains the string to be logged. This will be output
// BANNER_WIDTH characters at a time.
//
cchToLog = strlen(szToLog); pszNextLine = szToLog;
do { //
// Fill szCurLine with a BANNER_WIDTH chunk of szToLog, starting
// at pszNextLine. If pszNextLine points to the start of szToLog,
// then this is the first pass and no indent is necessary,
// otherwise indent with spaces by the size of the prefix for this
// log entry.
//
if (pszNextLine == szToLog) { strncpy(szCurLine, szToLog, BANNER_WIDTH); szCurLine[BANNER_WIDTH] = '\0'; cchToLog -= min(cchToLog, BANNER_WIDTH); pszNextLine += BANNER_WIDTH; } else { sprintf(szCurLine, "%*s%.*s", cchPrefix, "", BANNER_WIDTH - cchPrefix, pszNextLine); szCurLine[BANNER_WIDTH] = '\0'; cchToLog -= min(cchToLog, (ULONG) (BANNER_WIDTH - cchPrefix)); pszNextLine += BANNER_WIDTH - cchPrefix; } if (fp) { fprintf(fp, "%s\n", szCurLine); } if (flDestinations & LOG_TOCONSOLE) { printf("%s\n", szCurLine); } if (flDestinations & LOG_TODEBUG) { OutputDebugStringA(szCurLine); OutputDebugStringA("\n"); } } while (cchToLog); }
if (fp) { fclose(fp); } LeaveCriticalSection(&_critsec); }
//+---------------------------------------------------------------------------
//
// Member: CLog::_LogPrefix, private
//
// Synopsis: Fill [pszPrefix] with the prefix string corresponding to the
// infolevel bit set in [flLevel].
//
// Arguments: [flLevel] - exactly one LOG_* infolevel bit
// [szStart] - forms part of prefix for LOG_START and LOG_END
// [pszPrefix] - output
//
// Modifies: [pszPrefix]
//
// History: 02-09-94 DavidMun Created
//
// Notes: Caller must ensure that pswzPrefix points to a buffer large
// enough to hold START_PREFIX and szStart together.
//
// Neither TRACE nor TEXT levels are tallied.
//
//----------------------------------------------------------------------------
VOID CLog::_LogPrefix(ULONG flLevel, const CHAR *szStart, CHAR *pszPrefix) { if (flLevel & LOG_PASS) { _cLogPass++; strcpy(pszPrefix, PASS_PREFIX); } else if (flLevel & LOG_FAIL) { _cLogFail++; strcpy(pszPrefix, FAIL_PREFIX); } else if (flLevel & LOG_WARN) { _cLogWarn++; strcpy(pszPrefix, WARN_PREFIX); } else if (flLevel & LOG_START) { _cLogStart++; sprintf(pszPrefix, START_PREFIX, szStart); } else if (flLevel & LOG_END) { sprintf(pszPrefix, END_PREFIX, szStart); } else if (flLevel & LOG_INFO) { _cLogInfo++; strcpy(pszPrefix, INFO_PREFIX); } else if (flLevel & LOG_SKIP) { _cLogSkip++; strcpy(pszPrefix, SKIP_PREFIX); } else if (flLevel & LOG_ABORT) { _cLogAbort++; strcpy(pszPrefix, ABORT_PREFIX); } else if (flLevel & LOG_ERROR) { _cLogError++; strcpy(pszPrefix, ERROR_PREFIX); } else if (flLevel & LOG_TRACE) { strcpy(pszPrefix, TRACE_PREFIX); } else if (flLevel & LOG_PERF) { strcpy(pszPrefix, PERF_PREFIX); } else if (flLevel & LOG_DEBUG) { strcpy(pszPrefix, DEBUG_PREFIX); } else if (flLevel & LOG_TEXT) { pszPrefix[0] = '\0'; } else { _cLogOther++; pszPrefix[0] = '\0'; } }
//+---------------------------------------------------------------------------
//
// Member: CLog::_LogFooter, private
//
// Synopsis: Write a footer to the log.
//
// History: 02-11-94 DavidMun Created
//
// Notes: Called by dtor.
//
//----------------------------------------------------------------------------
VOID CLog::_LogFooter() { Write(LOG_START, "Footer"); Write(LOG_TEXT, BANNER_WIDTH_DASH); Write(LOG_TEXT, " Run of '%s' finished at %s", _szTestTitle, GetTimeStamp());
Write(LOG_TEXT, ""); Write(LOG_TEXT, " Total messages logged, by type:"); Write(LOG_TEXT, "");
if (_cLogStart) { Write(LOG_TEXT, " Start: %u", _cLogStart); }
if (_cLogPass) { Write(LOG_TEXT, " Pass: %u", _cLogPass); }
if (_cLogFail) { Write(LOG_TEXT, " Fail: %u", _cLogFail); }
if (_cLogAbort) { Write(LOG_TEXT, " Abort: %u", _cLogAbort); }
if (_cLogError) { Write(LOG_TEXT, " Error: %u", _cLogError); }
if (_cLogSkip) { Write(LOG_TEXT, " Skip: %u", _cLogSkip); }
if (_cLogWarn) { Write(LOG_TEXT, " Warning: %u", _cLogWarn); }
if (_cLogInfo) { Write(LOG_TEXT, " Information: %u", _cLogInfo); }
if (_cLogOther) { Write(LOG_TEXT, " User-defined: %u", _cLogOther); }
Write(LOG_TEXT, ""); Write(LOG_TEXT, BANNER_WIDTH_EQUALS); Write(LOG_END, ""); }
//+---------------------------------------------------------------------------
//
// Function: GetCpuStr, private
//
// Synopsis: Return a string describing CPU.
//
// Returns: Pointer to static string
//
// History: 02-11-94 DavidMun Created
// 05-01-95 DavidMun Update for change to GetSystemInfo
//
//----------------------------------------------------------------------------
static const CHAR *GetCpuStr() { static CHAR s_szCpuStr[BANNER_WIDTH]; SYSTEM_INFO siSystemInfo; CHAR *pszCpuType;
GetSystemInfo(&siSystemInfo);
switch (siSystemInfo.wProcessorArchitecture) { case PROCESSOR_ARCHITECTURE_INTEL: pszCpuType = " Intel"; break;
case PROCESSOR_ARCHITECTURE_MIPS: pszCpuType = " MIPS"; break;
case PROCESSOR_ARCHITECTURE_ALPHA: pszCpuType = " ALPHA"; break;
default: pszCpuType = "Unknown Processor(s)"; break; }
sprintf(s_szCpuStr, "%u %s", siSystemInfo.dwNumberOfProcessors, pszCpuType); return s_szCpuStr; }
//+---------------------------------------------------------------------------
//
// Function: GetVideoStr
//
// Synopsis: Return a pointer to a string describing video resolution and
// color (i.e., XxYxC).
//
// Returns: Pointer to static string.
//
// History: 02-11-94 DavidMun Created
//
//----------------------------------------------------------------------------
static const CHAR *GetVideoStr() { static CHAR s_szVideo[BANNER_WIDTH]; HDC hdcDisplay; ULONG cPlanes; ULONG cBitsPerPixel;
//
// Get a DC for the display, then find out its horizontal and vertical
// resolutions. Determine the number of colors per Petzold 3.1, p. 513.
//
hdcDisplay = CreateDC(TEXT("DISPLAY"), TEXT(""), TEXT(""), NULL); cPlanes = GetDeviceCaps(hdcDisplay, PLANES); cBitsPerPixel = GetDeviceCaps(hdcDisplay, BITSPIXEL);
sprintf(s_szVideo, "%ux%ux%u", GetDeviceCaps(hdcDisplay, HORZRES), GetDeviceCaps(hdcDisplay, VERTRES), 1 << (cPlanes * cBitsPerPixel));
DeleteDC(hdcDisplay); return s_szVideo; }
//+---------------------------------------------------------------------------
//
// Function: GetTimeStamp
//
// Synopsis: Return a pointer to a string containing current time and date.
//
// Returns: Pointer to a static string.
//
// History: 02-11-94 DavidMun Created
//
//----------------------------------------------------------------------------
static const CHAR *GetTimeStamp() { static CHAR s_szTimeStamp[20]; // space for time & date in format below
SYSTEMTIME tmStart;
GetLocalTime(&tmStart);
sprintf(s_szTimeStamp, "%02d:%02d:%02d %d/%02d/%d", tmStart.wHour, tmStart.wMinute, tmStart.wSecond, tmStart.wMonth, tmStart.wDay, tmStart.wYear);
return s_szTimeStamp; }
//+---------------------------------------------------------------------------
//
// Function: LogIt
//
// Synopsis: Log success or failure.
//
// Arguments: [hrFound] - hresult returned from some operation
// [hrExpected] - EXPECT_SUCCEEDED or a valid HRESULT
// [szFormat] - printf style format string
// [...] - args specified in [szFormat]
//
// History: 08-24-94 DavidMun Created
//
//----------------------------------------------------------------------------
VOID LogIt(HRESULT hrFound, HRESULT hrExpected, CHAR *szFormat, ...) { va_list varg;
va_start(varg, szFormat);
if (hrExpected == EXPECT_SUCCEEDED && SUCCEEDED(hrFound) || hrFound == hrExpected) { CHAR szBuf[MAX_LOGIT_MSG] = "Succeeded in "; CHAR *pBuf;
pBuf = strchr(szBuf, '\0'); _vsnprintf(pBuf, MAX_LOGIT_MSG - (pBuf - szBuf), szFormat, varg); g_Log.Write(LOG_TRACE, szBuf); } else { CHAR szBuf[MAX_LOGIT_MSG] = "Didn't succeed in "; CHAR *pBuf;
pBuf = strchr(szBuf, '\0'); _vsnprintf(pBuf, MAX_LOGIT_MSG - (pBuf - szBuf), szFormat, varg); g_Log.Write(LOG_FAIL, szBuf); } va_end( varg ); }
#if 0
void __cdecl main() { CLog Log("Unit Test", "test.log", LOG_TOCONSOLE | LOG_TOFILE);
Log.Write(LOG_START, "variation"); Log.Write(LOG_INFO, "Here is some info: %d %s", 1, "foo"); Log.Write(LOG_WARN, "a wide char warning '%S'", L"wide string"); Log.Write(LOG_TRACE, "line to trace"); Log.Write(LOG_ABORT, "Abort message"); } #endif
|