/*++ Copyright (c) 1997 Microsoft Corporation Module Name: stilog.cpp Abstract: Class to handle file based logging Author: Vlad Sadovsky (VladS) 01-Sep-1997 History: --*/ // // Include Headers // #include "cplusinc.h" #include "sticomm.h" // // Static definitions and variables // static const TCHAR szDefaultName[] = TEXT("Sti_Trace.log"); static const TCHAR szDefaultTracerName[] = TEXT("STI"); static const TCHAR szMutexNamePrefix[] = TEXT("StiTraceMutex"); static const TCHAR szColumns[] = TEXT("Severity TracerName [Process::ThreadId] Time MessageText\r\n\r\n"); static const TCHAR szOpenedLog[] = TEXT("\n****** Opened file log at %s %s .Tracer (%s) , called from [%#s::%#lx]\n"); static const TCHAR szClosedLog[] = TEXT("\n******Closed trace log on %s %s Tracer (%s) , called from [%#s::%#lx]\n"); // // Functions // // inline BOOL FormatStdTime( IN const SYSTEMTIME * pstNow, IN OUT TCHAR * pchBuffer, IN int cbBuffer ) { return ( GetTimeFormat( LOCALE_SYSTEM_DEFAULT, ( LOCALE_NOUSEROVERRIDE | TIME_FORCE24HOURFORMAT| TIME_NOTIMEMARKER), pstNow, NULL, pchBuffer, cbBuffer) != 0); } // FormatStdTime() inline BOOL FormatStdDate( IN const SYSTEMTIME * pstNow, IN OUT TCHAR * pchBuffer, IN int cbBuffer) { return ( GetDateFormat( LOCALE_SYSTEM_DEFAULT, LOCALE_NOUSEROVERRIDE, pstNow, NULL, pchBuffer, cbBuffer) != 0); } // FormatStdDate() inline TCHAR TraceFlagToLetter( IN DWORD dwType ) { if (dwType & STI_TRACE_ERROR) { return TEXT('e'); } else if (dwType & STI_TRACE_WARNING) { return TEXT('w'); } else if (dwType & STI_TRACE_INFORMATION) { return TEXT('i'); } else { return TEXT('t'); } } STI_FILE_LOG::STI_FILE_LOG( IN LPCTSTR lpszTracerName, IN LPCTSTR lpszLogName, IN DWORD dwFlags, // = 0 IN HMODULE hMessageModule // =NULL ) : m_hMutex(INVALID_HANDLE_VALUE) /*++ Description Constructor function for given log object. Arguments: lpszSource: file name for log. Relative to Windows directory Note: --*/ { LPCTSTR lpActualLogName; TCHAR szTempName[MAX_PATH]; LPTSTR pszFilePart; DWORD dwError; DWORD cbName; DWORD dwLevel; DWORD dwMode; BOOL fTruncate = FALSE; m_dwSignature = SIGNATURE_FILE_LOG; lpActualLogName = szDefaultName; m_hLogWindow = NULL; m_lWrittenHeader = FALSE; m_hDefaultMessageModule = hMessageModule ? hMessageModule : GetModuleHandle(NULL); dwLevel = STI_TRACE_ERROR; dwMode = STI_TRACE_ADD_TIME | STI_TRACE_ADD_THREAD; m_hLogFile = INVALID_HANDLE_VALUE; *m_szLogFilePath = TEXT('\0'); m_dwMaxSize = STI_MAX_LOG_SIZE; ReportError(NO_ERROR); // // Read global settings from the registry // RegEntry re(REGSTR_PATH_STICONTROL REGSTR_PATH_LOGGING,HKEY_LOCAL_MACHINE); if (re.IsValid()) { m_dwMaxSize = re.GetNumber(REGSTR_VAL_LOG_MAXSIZE,STI_MAX_LOG_SIZE); // // If we need to check append flag, read it from the registry // if (dwFlags & STIFILELOG_CHECK_TRUNCATE_ON_BOOT) { fTruncate = re.GetNumber(REGSTR_VAL_LOG_TRUNCATE_ON_BOOT,FALSE); } } // // Open file for logging // if (lpszLogName && *lpszLogName ) { lpActualLogName = lpszLogName; } // // lpszTracerName is ANSI. // if (lpszTracerName && *lpszTracerName ) { lstrcpyn(m_szTracerName,lpszTracerName,sizeof(m_szTracerName) / sizeof(m_szTracerName[0])); m_szTracerName[sizeof(m_szTracerName) / sizeof(m_szTracerName[0]) -1] = TEXT('\0'); } else { lstrcpy(m_szTracerName,szDefaultTracerName); } // // Approximate process name with name of the executable binary used to create process // *szTempName = TEXT('\0'); ::GetModuleFileName(NULL,szTempName,sizeof(szTempName) / sizeof(szTempName[0])); pszFilePart = _tcsrchr(szTempName,TEXT('\\')); pszFilePart = (pszFilePart && *pszFilePart) ? ::CharNext(pszFilePart) : szTempName; lstrcpyn(m_szProcessName,pszFilePart,sizeof(m_szProcessName)/sizeof(TCHAR)); m_szProcessName[sizeof(m_szProcessName)/sizeof(TCHAR) - 1] = TEXT('\0'); // // Read flags for this logger // re.MoveToSubKey(lpszTracerName); if (re.IsValid()) { dwLevel = re.GetNumber(REGSTR_VAL_LOG_LEVEL,STI_TRACE_ERROR) & STI_TRACE_MESSAGE_TYPE_MASK; dwMode = re.GetNumber(REGSTR_VAL_LOG_MODE,STI_TRACE_ADD_THREAD) & STI_TRACE_MESSAGE_FLAGS_MASK; } m_dwReportMode = dwLevel | dwMode; // // Open log file // cbName = ::GetWindowsDirectory(m_szLogFilePath,sizeof(m_szLogFilePath)/sizeof(m_szLogFilePath[0])); if (( cbName == 0) || !*m_szLogFilePath ) { ReportError(::GetLastError()); return; } lstrcat(lstrcat(m_szLogFilePath,TEXT("\\")),lpActualLogName); m_hLogFile = ::CreateFile(m_szLogFilePath, GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, // security attributes OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); // template file handle // if ( m_hLogFile!= INVALID_HANDLE_VALUE) { if (IS_VALID_HANDLE(m_hLogFile)) { if(fTruncate) { ::SetFilePointer( m_hLogFile, 0, NULL, FILE_BEGIN ); ::SetEndOfFile( m_hLogFile ); } ::SetFilePointer( m_hLogFile, 0, NULL, FILE_END); lstrcpy(szTempName,szMutexNamePrefix); lstrcat(szTempName,lpActualLogName); m_hMutex = ::CreateMutex(NULL, FALSE, szTempName ); if ( !IS_VALID_HANDLE(m_hMutex ) ) { // ASSERT } } if (!IS_VALID_HANDLE(m_hLogFile) || !IS_VALID_HANDLE(m_hMutex )) { ReportError(::GetLastError()); } else { // // Success // } } /* STI_FILE_LOG::STI_FILE_LOG() */ STI_FILE_LOG::~STI_FILE_LOG( VOID) /*++ Description: Destructor function for given STI_FILE_LOG object. --*/ { SYSTEMTIME stCurrentTime; TCHAR szFmtDate[20]; TCHAR szFmtTime[32]; TCHAR szTextBuffer[128]; if(m_lWrittenHeader) { GetLocalTime(&stCurrentTime); FormatStdDate( &stCurrentTime, szFmtDate, 15); FormatStdTime( &stCurrentTime, szFmtTime, sizeof(szFmtTime) / sizeof(szFmtTime[0])); ::wsprintf(szTextBuffer,szClosedLog, szFmtDate,szFmtTime, m_szTracerName, m_szProcessName, ::GetCurrentThreadId() ); WriteStringToLog(szTextBuffer); } if (IS_VALID_HANDLE(m_hMutex)) { ::CloseHandle(m_hMutex); m_hMutex = INVALID_HANDLE_VALUE; } if (IS_VALID_HANDLE(m_hLogFile)) { ::FlushFileBuffers( m_hLogFile); ::CloseHandle(m_hLogFile); m_hLogFile = INVALID_HANDLE_VALUE; } m_dwSignature = SIGNATURE_FILE_LOG_FREE; } /* STI_FILE_LOG::~STI_FILE_LOG() */ // // IUnknown methods. Used only for reference counting // STDMETHODIMP STI_FILE_LOG::QueryInterface( REFIID riid, LPVOID * ppvObj) { return E_FAIL; } STDMETHODIMP_(ULONG) STI_FILE_LOG::AddRef( void) { ::InterlockedIncrement(&m_cRef); return m_cRef; } STDMETHODIMP_(ULONG) STI_FILE_LOG::Release( void) { LONG cNew; if(!(cNew = ::InterlockedDecrement(&m_cRef))) { delete this; } return cNew; } void STI_FILE_LOG:: WriteLogSessionHeader( VOID ) /*++ Description Arguments: Note: --*/ { SYSTEMTIME stCurrentTime; TCHAR szFmtDate[32]; TCHAR szFmtTime[32]; TCHAR szTextBuffer[128]; GetLocalTime(&stCurrentTime); FormatStdDate( &stCurrentTime, szFmtDate, sizeof(szFmtDate) / sizeof(szFmtDate[0])); FormatStdTime( &stCurrentTime, szFmtTime, sizeof(szFmtTime) / sizeof(szFmtTime[0])); ::wsprintf(szTextBuffer,szOpenedLog, szFmtDate,szFmtTime, m_szTracerName, m_szProcessName, ::GetCurrentThreadId() ); WriteStringToLog(szTextBuffer); WriteStringToLog(szColumns); } void STI_FILE_LOG:: ReportMessage( DWORD dwType, LPCTSTR pszMsg, ... ) /*++ Description Arguments: Note: --*/ { va_list list; TCHAR *pchBuff = NULL; DWORD cch; va_start (list, pszMsg); pchBuff = NULL; cch = ::FormatMessage(FORMAT_MESSAGE_MAX_WIDTH_MASK | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING, pszMsg, 0, 0, (LPTSTR) &pchBuff, 1024, &list ); vReportMessage(dwType,pszMsg,list); va_end(list); } void STI_FILE_LOG:: ReportMessage( DWORD dwType, DWORD idMessage, ... ) /*++ Description Arguments: Note: --*/ { va_list list; TCHAR *pchBuff = NULL; DWORD cch; va_start (list, idMessage); // // Read message and add inserts // pchBuff = NULL; cch = ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_MAX_WIDTH_MASK | FORMAT_MESSAGE_FROM_HMODULE, m_hDefaultMessageModule , idMessage, 0, (LPTSTR) &pchBuff, 1024, &list ); if (pchBuff && cch) { vReportMessage(dwType,pchBuff,list); LocalFree(pchBuff); } va_end(list); } void STI_FILE_LOG:: vReportMessage( DWORD dwType, LPCTSTR pszMsg, va_list arglist ) /*++ Description Arguments: Note: --*/ { TCHAR achTextBuffer[1024]; DWORD dwReportMode; if ((QueryError() != NOERROR) || (!m_hLogFile || m_hLogFile == INVALID_HANDLE_VALUE)) { return; } // Do we need to display this message ? if (!((dwType & m_dwReportMode) & STI_TRACE_MESSAGE_TYPE_MASK)) { return; } // Start message with type *achTextBuffer = TraceFlagToLetter(dwType); *(achTextBuffer+1) = TEXT(' '); *(achTextBuffer+2) = TEXT('\0'); // Add name of tracer source lstrcat(lstrcat(achTextBuffer,m_szTracerName),TEXT(" "));; dwReportMode = m_dwReportMode | (dwType & STI_TRACE_MESSAGE_FLAGS_MASK); // // Prepare header if needed // if (dwReportMode & STI_TRACE_MESSAGE_FLAGS_MASK ) { if (dwReportMode & STI_TRACE_ADD_THREAD ) { // // Add process::thread ID // TCHAR szThreadId[40]; ::wsprintf(szThreadId,TEXT("[%#s::%#lx] "),m_szProcessName,::GetCurrentThreadId()); ::lstrcat(::lstrcat(achTextBuffer,szThreadId),TEXT(" ")); } if (dwReportMode & STI_TRACE_ADD_TIME ) { // Add current time SYSTEMTIME stCurrentTime; TCHAR szFmtTime[32]; *szFmtTime = TEXT('\0'); ::GetLocalTime(&stCurrentTime); FormatStdTime( &stCurrentTime, szFmtTime, 15); lstrcat(lstrcat(achTextBuffer,szFmtTime),TEXT(" "));; } } lstrcat(achTextBuffer,TEXT("\t")); // // Now add message text itself // ::wvsprintf(achTextBuffer+::lstrlen(achTextBuffer), pszMsg, arglist); ::lstrcat(achTextBuffer , TEXT("\r\n")); // // Write to the file, flushing buffers if message type is ERROR // WriteStringToLog(achTextBuffer,(dwType & STI_TRACE_MESSAGE_TYPE_MASK) & STI_TRACE_ERROR); } VOID STI_FILE_LOG:: WriteStringToLog( LPCTSTR pszTextBuffer, BOOL fFlush // = FALSE ) { DWORD dwcbWritten; LONG lHeaderWrittenFlag; // // Integrity check. Make sure the mutex and file handles are valid - if not, // then bail. // if (!(IS_VALID_HANDLE(m_hMutex) && IS_VALID_HANDLE(m_hLogFile))) { #ifdef DEBUG //OutputDebugString(TEXT("STILOG File or Mutex handle is invalid. ")); #endif return ; } // // If had not done yet, write session header here. Note this is recursive call // and flag marking header should be set first. // lHeaderWrittenFlag = InterlockedExchange(&m_lWrittenHeader,1L); if (!lHeaderWrittenFlag) { WriteLogSessionHeader(); } TAKE_MUTEX t(m_hMutex); // // Check that log file size is not exceeding the limit. Assuming log file size is set to fit // into 32bit field, so we don't bother looking at high dword. // // Nb: We are not saving backup copy of the log, expecting that it never grows that large // BY_HANDLE_FILE_INFORMATION fi; if (!GetFileInformationByHandle(m_hLogFile,&fi)) { #ifdef DEBUG OutputDebugString(TEXT("STILOG could not get file size for log file. ")); #endif return ; } if ( fi.nFileSizeHigh !=0 || (fi.nFileSizeLow > m_dwMaxSize) ){ ::SetFilePointer( m_hLogFile, 0, NULL, FILE_BEGIN ); ::SetEndOfFile( m_hLogFile ); ::GetFileInformationByHandle(m_hLogFile,&fi); } #ifdef USE_FILE_LOCK ::LockFile(m_hLogFile,fi.nFileSizeLow,fi.nFileSizeHigh,4096,0); #endif ::SetFilePointer( m_hLogFile, 0, NULL, FILE_END); #ifdef _UNICODE UINT len = lstrlen(pszTextBuffer); CHAR *ansiBuffer = (CHAR *)alloca(len + 2); if(ansiBuffer) { ::WideCharToMultiByte(CP_ACP, 0, pszTextBuffer, -1, ansiBuffer, len + 1, NULL, NULL); ::WriteFile(m_hLogFile, ansiBuffer, len, &dwcbWritten, NULL); } #else ::WriteFile(m_hLogFile, pszTextBuffer, lstrlen(pszTextBuffer), &dwcbWritten, NULL); #endif #ifdef USE_FILE_LOCK ::UnlockFile(m_hLogFile,fi.nFileSizeLow,fi.nFileSizeHigh,4096,0); #endif if (fFlush) { // Flush buffers to disk FlushFileBuffers(m_hLogFile); } if(QueryReportMode() & STI_TRACE_LOG_TOUI) { ULONG_PTR dwResult; SendMessageTimeout(m_hLogWindow, STIMON_MSG_LOG_MESSAGE, 0, (LPARAM)pszTextBuffer, 0, 100, &dwResult ); } #ifdef DEBUG ::OutputDebugString(pszTextBuffer); #endif } // // C-APIs // HANDLE WINAPI CreateStiFileLog( IN LPCTSTR lpszTracerName, IN LPCTSTR lpszLogName, IN DWORD dwReportMode ) /*++ Description Arguments: Note: --*/ { HANDLE hRet = INVALID_HANDLE_VALUE; STI_FILE_LOG* pStiFileLog = NULL; pStiFileLog = new STI_FILE_LOG(lpszTracerName,lpszLogName); if(pStiFileLog){ if (pStiFileLog->IsValid()) { hRet = static_cast(pStiFileLog); pStiFileLog->SetReportMode(pStiFileLog->QueryReportMode() | dwReportMode); } else { // // Notice that we delete this object rather than calling // CloseStiFileLog. We do this because it the filelog is // not valid (maybe it had an internal creation error), // CloseStiFileLog won't try to delete it, hence we delete // it here. // delete pStiFileLog; pStiFileLog = NULL; hRet = INVALID_HANDLE_VALUE; } } else { ::SetLastError(ERROR_NOT_ENOUGH_MEMORY); } return hRet; } DWORD WINAPI CloseStiFileLog( IN HANDLE hFileLog ) { STI_FILE_LOG* pStiFileLog = NULL; pStiFileLog = static_cast(hFileLog); if (IsBadWritePtr(pStiFileLog,sizeof(STI_FILE_LOG)) || !pStiFileLog->IsValid()) { ::SetLastError(ERROR_INVALID_HANDLE); return ERROR_INVALID_HANDLE; } delete pStiFileLog; return NOERROR; } DWORD WINAPI ReportStiLogMessage( IN HANDLE hFileLog, IN DWORD dwType, IN LPCTSTR psz, ... ) { STI_FILE_LOG* pStiFileLog = NULL; pStiFileLog = static_cast(hFileLog); if (IsBadWritePtr(pStiFileLog,sizeof(STI_FILE_LOG)) || !pStiFileLog->IsValid()) { ::SetLastError(ERROR_INVALID_HANDLE); return ERROR_INVALID_HANDLE; } va_list list; va_start (list, psz); pStiFileLog->vReportMessage(dwType,psz,list); va_end(list); return NOERROR; } /********************************* End of File ***************************/