Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

358 lines
11 KiB

  1. //***************************************************************************
  2. //
  3. // Copyright (c) 1997-2001 Microsoft Corporation, All Rights Reserved
  4. //
  5. // BrodCast.cpp
  6. //
  7. // Purpose: Logging functions
  8. //
  9. //***************************************************************************
  10. #include "precomp.h"
  11. #include <assertbreak.h>
  12. #include <stdio.h>
  13. #include <stdarg.h>
  14. #include <conio.h>
  15. #include <Math.h>
  16. #include <multiplat.h>
  17. #include <BrodCast.h>
  18. #include <impself.h>
  19. #include <SmartPtr.h>
  20. // a little something to make sure we don't try to access
  21. // instance variables that no longer exist
  22. bool bAlive = false;
  23. // we only need one of these lying around
  24. ProviderLog captainsLog;
  25. // so we'll build in a check...
  26. #ifdef _DEBUG
  27. bool ProviderLog::m_beenInitted = false;
  28. #endif
  29. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  30. Function: ProviderLog ctor
  31. Description: provides initial initialization
  32. Arguments:
  33. Returns:
  34. Inputs:
  35. Outputs:
  36. Caveats:
  37. Raid:
  38. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  39. ProviderLog::ProviderLog(void)
  40. {
  41. #ifdef _DEBUG
  42. if (m_beenInitted)
  43. ASSERT_BREAK(0); // do not instanciate one of these
  44. // - use the LogMessage macro defined in the header file!
  45. #endif
  46. m_lastLookedAtRegistry = 0;
  47. m_logLevel = None;
  48. bAlive = true;
  49. IsLoggingOn(&m_path);
  50. #ifdef _DEBUG
  51. m_beenInitted = true;
  52. #endif
  53. }
  54. ProviderLog::~ProviderLog(void)
  55. {
  56. bAlive = false;
  57. }
  58. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  59. Function: IsLoggingOn
  60. Description: determine whether logging is enabled, find path if it is
  61. caches info - it will only look at registry once every three minutes.
  62. Also enforces file size limit.
  63. Arguments: CHString ptr to recieve path (may be NULL)
  64. Returns: LogLevel
  65. Inputs:
  66. Outputs:
  67. Caveats: if return is zero, path is undefined
  68. Raid:
  69. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  70. ProviderLog::LogLevel ProviderLog::IsLoggingOn(CHString *pPath /* = NULL */)
  71. {
  72. if (!bAlive)
  73. return None;
  74. union
  75. {
  76. FILETIME fileTime;
  77. unsigned __int64 now;
  78. } myTime;
  79. GetSystemTimeAsFileTime(&myTime.fileTime);
  80. // if three minutes have elapsed, check again.
  81. if ((myTime.now - m_lastLookedAtRegistry) > (180 * 10000000))
  82. {
  83. BeginWrite();
  84. try
  85. {
  86. // somebody might have snuck in - check again!
  87. GetSystemTimeAsFileTime(&myTime.fileTime);
  88. if ((myTime.now - m_lastLookedAtRegistry) > (180 * 10000000))
  89. {
  90. m_lastLookedAtRegistry = myTime.now;
  91. CRegistry RegInfo;
  92. CImpersonateSelf impSelf; // So our registry call works.
  93. if(RegInfo.Open(HKEY_LOCAL_MACHINE,
  94. L"SOFTWARE\\Microsoft\\WBEM\\CIMOM",
  95. KEY_READ) == ERROR_SUCCESS)
  96. {
  97. DWORD flag;
  98. // see if we can find the flag
  99. if((RegInfo.GetCurrentKeyValue(L"Logging", flag) == ERROR_SUCCESS) && (flag <= (DWORD)Verbose))
  100. {
  101. // we found one & it's true so we'll try to grab the name itself
  102. if (m_logLevel = (LogLevel)flag)
  103. {
  104. // retrieve dir name or use default
  105. CHString sTemp;
  106. if ((RegInfo.GetCurrentKeyValue(L"Logging Directory", sTemp) != ERROR_SUCCESS)
  107. || (sTemp.IsEmpty()))
  108. sTemp = L"C:\\";
  109. ASSERT_BREAK(!sTemp.IsEmpty()); // shouldn't a got here empty!
  110. // Expand the environment string
  111. WCHAR szPath[_MAX_PATH];
  112. if (FRExpandEnvironmentStrings(sTemp, szPath, _MAX_PATH) != 0)
  113. {
  114. sTemp = szPath;
  115. // append backslash
  116. if (sTemp[sTemp.GetLength() -1] != '\\')
  117. sTemp += '\\';
  118. }
  119. else
  120. {
  121. sTemp = L"C:\\";
  122. }
  123. // append file name
  124. m_path = sTemp + L"FrameWork.log";
  125. CHString maxSizeStr;
  126. if (RegInfo.GetCurrentKeyValue(L"Log File Max Size", maxSizeStr) == ERROR_SUCCESS)
  127. {
  128. m_maxSize.QuadPart = _wtoi64(maxSizeStr);
  129. if (m_maxSize.QuadPart <= 0)
  130. m_maxSize.QuadPart = 65536;
  131. }
  132. else
  133. m_maxSize.QuadPart = 65536;
  134. } // if logging on
  135. } // if reginfo get current key
  136. else
  137. m_logLevel = None;
  138. RegInfo.Close() ;
  139. } // if reginfo open
  140. } // if three minutes have elapsed, check again.
  141. }
  142. catch ( ... )
  143. {
  144. EndWrite();
  145. throw;
  146. }
  147. EndWrite();
  148. } // if three minutes have elapsed, check again.
  149. // make sure some other thread doesn't step on our logic
  150. LogLevel ret;
  151. // If we don't need the path, we don't need the crit sec
  152. if (!pPath)
  153. {
  154. ret = m_logLevel;
  155. }
  156. else
  157. {
  158. // Make sure we get both at the same time
  159. BeginRead();
  160. if (ret = m_logLevel)
  161. *pPath = m_path;
  162. EndRead();
  163. }
  164. return ret;
  165. }
  166. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  167. Function: void LocalLogMessage(char *pszMessageString)
  168. Description: records message in log file
  169. Arguments:
  170. Returns:
  171. Inputs:
  172. Outputs:
  173. Caveats:
  174. Raid:
  175. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  176. void ProviderLog::LocalLogMessage(LPCWSTR pszMessageString, LPCWSTR pszFileName, int lineNo, LogLevel level)
  177. {
  178. if (!bAlive)
  179. return;
  180. #ifdef _DEBUG
  181. // *shouldn't* be able to get here before the static ctor fires!
  182. ASSERT_BREAK(m_beenInitted);
  183. #endif
  184. CHString path;
  185. LARGE_INTEGER liSize;
  186. liSize.QuadPart = 0;
  187. // Doing this call twice avoids the crit section for the most common case. Actually, for the
  188. // most common case, it only gets called once anyway.
  189. if ((level <= IsLoggingOn(NULL)) && (level <= IsLoggingOn(&path)) && !path.IsEmpty())
  190. {
  191. BeginWrite();
  192. try
  193. {
  194. CImpersonateSelf impSelf; // So the file calls work.
  195. SmartCloseHandle hFile;
  196. hFile = ::CreateFileW(
  197. path,
  198. GENERIC_READ | GENERIC_WRITE,
  199. FILE_SHARE_READ,
  200. NULL,
  201. OPEN_ALWAYS,
  202. FILE_ATTRIBUTE_NORMAL,
  203. NULL);
  204. if(hFile != INVALID_HANDLE_VALUE)
  205. {
  206. SYSTEMTIME localTime;
  207. GetLocalTime(&localTime);
  208. CHString chstrMsg;
  209. chstrMsg.Format(
  210. L"%s\t%02d/%02d/%d %02d:%02d:%02d.%03d\tthread:%u\t[%s.%d]\r\n",
  211. pszMessageString,
  212. localTime.wMonth,
  213. localTime.wDay,
  214. localTime.wYear,
  215. localTime.wHour,
  216. localTime.wMinute,
  217. localTime.wSecond,
  218. localTime.wMilliseconds,
  219. GetCurrentThreadId(),
  220. pszFileName,
  221. lineNo);
  222. int nLen = ::WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)chstrMsg, -1, NULL, 0, NULL, NULL);
  223. CSmartBuffer pBuff(nLen);
  224. ::WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)chstrMsg, -1, (LPSTR)(LPBYTE)pBuff, nLen, NULL, NULL);
  225. ::SetFilePointer(
  226. hFile,
  227. 0,
  228. 0,
  229. FILE_END);
  230. DWORD dwNumBytesWritten = 0L;
  231. ::WriteFile(
  232. hFile,
  233. pBuff,
  234. nLen - 1,
  235. &dwNumBytesWritten,
  236. NULL);
  237. // Save the size
  238. ::GetFileSizeEx(
  239. hFile,
  240. &liSize);
  241. // Close the file in case we need to rename
  242. hFile = INVALID_HANDLE_VALUE;
  243. // Check the size against the max
  244. CheckFileSize(liSize, m_path);
  245. }
  246. }
  247. catch ( ... )
  248. {
  249. EndWrite();
  250. throw;
  251. }
  252. EndWrite();
  253. }
  254. }
  255. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  256. Function: CheckFileSize
  257. Description: determines whether the log file has exceeded the alllowable size
  258. if it has, the old one is renamed after the old old one is deleted
  259. Arguments: CRegistry& RegInfo - open registry, full path to file
  260. Returns: usually
  261. Inputs:
  262. Outputs:
  263. Caveats: expects caller to serialize access to this function.
  264. Raid:
  265. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  266. void ProviderLog::CheckFileSize(LARGE_INTEGER& nowSize, const CHString &path)
  267. {
  268. if (!bAlive)
  269. return;
  270. // if it's too big
  271. if (nowSize.QuadPart >= m_maxSize.QuadPart)
  272. {
  273. // generate backup file name = framework.lo_
  274. CHString oldFilePath(path);
  275. oldFilePath.SetAt(oldFilePath.GetLength() -1, L'_');
  276. // delete the old backup file - don't care if it fails
  277. #ifdef UNICODE
  278. _wunlink(oldFilePath);
  279. _wrename(path, oldFilePath);
  280. #else
  281. _unlink(bstr_t(oldFilePath));
  282. rename(bstr_t(path), bstr_t(oldFilePath));
  283. #endif
  284. }
  285. }
  286. void ProviderLog::LocalLogMessage(LPCWSTR pszFileName, int lineNo, LogLevel level, LPCWSTR pszFormatString,...)
  287. {
  288. if (level <= IsLoggingOn(NULL))
  289. {
  290. va_list argList;
  291. va_start(argList, pszFormatString);
  292. CHString sMsg;
  293. sMsg.FormatV(pszFormatString, argList);
  294. va_end(argList);
  295. LocalLogMessage(sMsg, pszFileName, lineNo, level);
  296. }
  297. }