//*************************************************************************** // // Copyright (c) 1997-2001 Microsoft Corporation, All Rights Reserved // // BrodCast.cpp // // Purpose: Logging functions // //*************************************************************************** #include "precomp.h" #include #include #include #include #include #include #include #include #include // a little something to make sure we don't try to access // instance variables that no longer exist bool bAlive = false; // we only need one of these lying around ProviderLog captainsLog; // so we'll build in a check... #ifdef _DEBUG bool ProviderLog::m_beenInitted = false; #endif /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Function: ProviderLog ctor Description: provides initial initialization Arguments: Returns: Inputs: Outputs: Caveats: Raid: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ ProviderLog::ProviderLog(void) { #ifdef _DEBUG if (m_beenInitted) ASSERT_BREAK(0); // do not instanciate one of these // - use the LogMessage macro defined in the header file! #endif m_lastLookedAtRegistry = 0; m_logLevel = None; bAlive = true; IsLoggingOn(&m_path); #ifdef _DEBUG m_beenInitted = true; #endif } ProviderLog::~ProviderLog(void) { bAlive = false; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Function: IsLoggingOn Description: determine whether logging is enabled, find path if it is caches info - it will only look at registry once every three minutes. Also enforces file size limit. Arguments: CHString ptr to recieve path (may be NULL) Returns: LogLevel Inputs: Outputs: Caveats: if return is zero, path is undefined Raid: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ ProviderLog::LogLevel ProviderLog::IsLoggingOn(CHString *pPath /* = NULL */) { if (!bAlive) return None; union { FILETIME fileTime; unsigned __int64 now; } myTime; GetSystemTimeAsFileTime(&myTime.fileTime); // if three minutes have elapsed, check again. if ((myTime.now - m_lastLookedAtRegistry) > (180 * 10000000)) { BeginWrite(); try { // somebody might have snuck in - check again! GetSystemTimeAsFileTime(&myTime.fileTime); if ((myTime.now - m_lastLookedAtRegistry) > (180 * 10000000)) { m_lastLookedAtRegistry = myTime.now; CRegistry RegInfo; CImpersonateSelf impSelf; // So our registry call works. if(RegInfo.Open(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\WBEM\\CIMOM", KEY_READ) == ERROR_SUCCESS) { DWORD flag; // see if we can find the flag if((RegInfo.GetCurrentKeyValue(L"Logging", flag) == ERROR_SUCCESS) && (flag <= (DWORD)Verbose)) { // we found one & it's true so we'll try to grab the name itself if (m_logLevel = (LogLevel)flag) { // retrieve dir name or use default CHString sTemp; if ((RegInfo.GetCurrentKeyValue(L"Logging Directory", sTemp) != ERROR_SUCCESS) || (sTemp.IsEmpty())) sTemp = L"C:\\"; ASSERT_BREAK(!sTemp.IsEmpty()); // shouldn't a got here empty! // Expand the environment string WCHAR szPath[_MAX_PATH]; if (FRExpandEnvironmentStrings(sTemp, szPath, _MAX_PATH) != 0) { sTemp = szPath; // append backslash if (sTemp[sTemp.GetLength() -1] != '\\') sTemp += '\\'; } else { sTemp = L"C:\\"; } // append file name m_path = sTemp + L"FrameWork.log"; CHString maxSizeStr; if (RegInfo.GetCurrentKeyValue(L"Log File Max Size", maxSizeStr) == ERROR_SUCCESS) { m_maxSize.QuadPart = _wtoi64(maxSizeStr); if (m_maxSize.QuadPart <= 0) m_maxSize.QuadPart = 65536; } else m_maxSize.QuadPart = 65536; } // if logging on } // if reginfo get current key else m_logLevel = None; RegInfo.Close() ; } // if reginfo open } // if three minutes have elapsed, check again. } catch ( ... ) { EndWrite(); throw; } EndWrite(); } // if three minutes have elapsed, check again. // make sure some other thread doesn't step on our logic LogLevel ret; // If we don't need the path, we don't need the crit sec if (!pPath) { ret = m_logLevel; } else { // Make sure we get both at the same time BeginRead(); if (ret = m_logLevel) *pPath = m_path; EndRead(); } return ret; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Function: void LocalLogMessage(char *pszMessageString) Description: records message in log file Arguments: Returns: Inputs: Outputs: Caveats: Raid: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void ProviderLog::LocalLogMessage(LPCWSTR pszMessageString, LPCWSTR pszFileName, int lineNo, LogLevel level) { if (!bAlive) return; #ifdef _DEBUG // *shouldn't* be able to get here before the static ctor fires! ASSERT_BREAK(m_beenInitted); #endif CHString path; LARGE_INTEGER liSize; liSize.QuadPart = 0; // Doing this call twice avoids the crit section for the most common case. Actually, for the // most common case, it only gets called once anyway. if ((level <= IsLoggingOn(NULL)) && (level <= IsLoggingOn(&path)) && !path.IsEmpty()) { BeginWrite(); try { CImpersonateSelf impSelf; // So the file calls work. SmartCloseHandle hFile; hFile = ::CreateFileW( path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if(hFile != INVALID_HANDLE_VALUE) { SYSTEMTIME localTime; GetLocalTime(&localTime); CHString chstrMsg; chstrMsg.Format( L"%s\t%02d/%02d/%d %02d:%02d:%02d.%03d\tthread:%u\t[%s.%d]\r\n", pszMessageString, localTime.wMonth, localTime.wDay, localTime.wYear, localTime.wHour, localTime.wMinute, localTime.wSecond, localTime.wMilliseconds, GetCurrentThreadId(), pszFileName, lineNo); int nLen = ::WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)chstrMsg, -1, NULL, 0, NULL, NULL); CSmartBuffer pBuff(nLen); ::WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)chstrMsg, -1, (LPSTR)(LPBYTE)pBuff, nLen, NULL, NULL); ::SetFilePointer( hFile, 0, 0, FILE_END); DWORD dwNumBytesWritten = 0L; ::WriteFile( hFile, pBuff, nLen - 1, &dwNumBytesWritten, NULL); // Save the size ::GetFileSizeEx( hFile, &liSize); // Close the file in case we need to rename hFile = INVALID_HANDLE_VALUE; // Check the size against the max CheckFileSize(liSize, m_path); } } catch ( ... ) { EndWrite(); throw; } EndWrite(); } } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Function: CheckFileSize Description: determines whether the log file has exceeded the alllowable size if it has, the old one is renamed after the old old one is deleted Arguments: CRegistry& RegInfo - open registry, full path to file Returns: usually Inputs: Outputs: Caveats: expects caller to serialize access to this function. Raid: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void ProviderLog::CheckFileSize(LARGE_INTEGER& nowSize, const CHString &path) { if (!bAlive) return; // if it's too big if (nowSize.QuadPart >= m_maxSize.QuadPart) { // generate backup file name = framework.lo_ CHString oldFilePath(path); oldFilePath.SetAt(oldFilePath.GetLength() -1, L'_'); // delete the old backup file - don't care if it fails #ifdef UNICODE _wunlink(oldFilePath); _wrename(path, oldFilePath); #else _unlink(bstr_t(oldFilePath)); rename(bstr_t(path), bstr_t(oldFilePath)); #endif } } void ProviderLog::LocalLogMessage(LPCWSTR pszFileName, int lineNo, LogLevel level, LPCWSTR pszFormatString,...) { if (level <= IsLoggingOn(NULL)) { va_list argList; va_start(argList, pszFormatString); CHString sMsg; sMsg.FormatV(pszFormatString, argList); va_end(argList); LocalLogMessage(sMsg, pszFileName, lineNo, level); } }