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.

773 lines
17 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. stilog.cpp
  5. Abstract:
  6. Class to handle file based logging
  7. Author:
  8. Vlad Sadovsky (VladS) 01-Sep-1997
  9. History:
  10. --*/
  11. //
  12. // Include Headers
  13. //
  14. #include "cplusinc.h"
  15. #include "sticomm.h"
  16. //
  17. // Static definitions and variables
  18. //
  19. static const TCHAR szDefaultName[] = TEXT("Sti_Trace.log");
  20. static const TCHAR szDefaultTracerName[] = TEXT("STI");
  21. static const TCHAR szMutexNamePrefix[] = TEXT("StiTraceMutex");
  22. static const TCHAR szColumns[] = TEXT("Severity TracerName [Process::ThreadId] Time MessageText\r\n\r\n");
  23. static const TCHAR szOpenedLog[] = TEXT("\n****** Opened file log at %s %s .Tracer (%s) , called from [%#s::%#lx]\n");
  24. static const TCHAR szClosedLog[] = TEXT("\n******Closed trace log on %s %s Tracer (%s) , called from [%#s::%#lx]\n");
  25. //
  26. // Functions
  27. //
  28. //
  29. inline BOOL
  30. FormatStdTime(
  31. IN const SYSTEMTIME * pstNow,
  32. IN OUT TCHAR * pchBuffer,
  33. IN int cbBuffer
  34. )
  35. {
  36. return ( GetTimeFormat( LOCALE_SYSTEM_DEFAULT,
  37. ( LOCALE_NOUSEROVERRIDE | TIME_FORCE24HOURFORMAT|
  38. TIME_NOTIMEMARKER),
  39. pstNow, NULL, pchBuffer, cbBuffer)
  40. != 0);
  41. } // FormatStdTime()
  42. inline BOOL
  43. FormatStdDate( IN const SYSTEMTIME * pstNow,
  44. IN OUT TCHAR * pchBuffer,
  45. IN int cbBuffer)
  46. {
  47. return ( GetDateFormat( LOCALE_SYSTEM_DEFAULT, LOCALE_NOUSEROVERRIDE,
  48. pstNow, NULL, pchBuffer, cbBuffer)
  49. != 0);
  50. } // FormatStdDate()
  51. inline TCHAR
  52. TraceFlagToLetter(
  53. IN DWORD dwType
  54. )
  55. {
  56. if (dwType & STI_TRACE_ERROR) {
  57. return TEXT('e');
  58. }
  59. else if (dwType & STI_TRACE_WARNING) {
  60. return TEXT('w');
  61. }
  62. else if (dwType & STI_TRACE_INFORMATION) {
  63. return TEXT('i');
  64. }
  65. else {
  66. return TEXT('t');
  67. }
  68. }
  69. STI_FILE_LOG::STI_FILE_LOG(
  70. IN LPCTSTR lpszTracerName,
  71. IN LPCTSTR lpszLogName,
  72. IN DWORD dwFlags, // = 0
  73. IN HMODULE hMessageModule // =NULL
  74. ) :
  75. m_hMutex(INVALID_HANDLE_VALUE)
  76. /*++
  77. Description
  78. Constructor function for given log object.
  79. Arguments:
  80. lpszSource: file name for log. Relative to Windows directory
  81. Note:
  82. --*/
  83. {
  84. LPCTSTR lpActualLogName;
  85. TCHAR szTempName[MAX_PATH];
  86. LPTSTR pszFilePart;
  87. DWORD dwError;
  88. DWORD cbName;
  89. DWORD dwLevel;
  90. DWORD dwMode;
  91. BOOL fTruncate = FALSE;
  92. m_dwSignature = SIGNATURE_FILE_LOG;
  93. lpActualLogName = szDefaultName;
  94. m_hLogWindow = NULL;
  95. m_lWrittenHeader = FALSE;
  96. m_hDefaultMessageModule = hMessageModule ? hMessageModule : GetModuleHandle(NULL);
  97. dwLevel = STI_TRACE_ERROR;
  98. dwMode = STI_TRACE_ADD_TIME | STI_TRACE_ADD_THREAD;
  99. m_hLogFile = INVALID_HANDLE_VALUE;
  100. *m_szLogFilePath = TEXT('\0');
  101. m_dwMaxSize = STI_MAX_LOG_SIZE;
  102. ReportError(NO_ERROR);
  103. //
  104. // Read global settings from the registry
  105. //
  106. RegEntry re(REGSTR_PATH_STICONTROL REGSTR_PATH_LOGGING,HKEY_LOCAL_MACHINE);
  107. if (re.IsValid()) {
  108. m_dwMaxSize = re.GetNumber(REGSTR_VAL_LOG_MAXSIZE,STI_MAX_LOG_SIZE);
  109. //
  110. // If we need to check append flag, read it from the registry
  111. //
  112. if (dwFlags & STIFILELOG_CHECK_TRUNCATE_ON_BOOT) {
  113. fTruncate = re.GetNumber(REGSTR_VAL_LOG_TRUNCATE_ON_BOOT,FALSE);
  114. }
  115. }
  116. //
  117. // Open file for logging
  118. //
  119. if (lpszLogName && *lpszLogName ) {
  120. lpActualLogName = lpszLogName;
  121. }
  122. //
  123. // lpszTracerName is ANSI.
  124. //
  125. if (lpszTracerName && *lpszTracerName ) {
  126. lstrcpyn(m_szTracerName,lpszTracerName,sizeof(m_szTracerName) / sizeof(m_szTracerName[0]));
  127. m_szTracerName[sizeof(m_szTracerName) / sizeof(m_szTracerName[0]) -1] = TEXT('\0');
  128. }
  129. else {
  130. lstrcpy(m_szTracerName,szDefaultTracerName);
  131. }
  132. //
  133. // Approximate process name with name of the executable binary used to create process
  134. //
  135. *szTempName = TEXT('\0');
  136. ::GetModuleFileName(NULL,szTempName,sizeof(szTempName) / sizeof(szTempName[0]));
  137. pszFilePart = _tcsrchr(szTempName,TEXT('\\'));
  138. pszFilePart = (pszFilePart && *pszFilePart) ? ::CharNext(pszFilePart) : szTempName;
  139. lstrcpyn(m_szProcessName,pszFilePart,sizeof(m_szProcessName)/sizeof(TCHAR));
  140. m_szProcessName[sizeof(m_szProcessName)/sizeof(TCHAR) - 1] = TEXT('\0');
  141. //
  142. // Read flags for this logger
  143. //
  144. re.MoveToSubKey(lpszTracerName);
  145. if (re.IsValid()) {
  146. dwLevel = re.GetNumber(REGSTR_VAL_LOG_LEVEL,STI_TRACE_ERROR)
  147. & STI_TRACE_MESSAGE_TYPE_MASK;
  148. dwMode = re.GetNumber(REGSTR_VAL_LOG_MODE,STI_TRACE_ADD_THREAD)
  149. & STI_TRACE_MESSAGE_FLAGS_MASK;
  150. }
  151. m_dwReportMode = dwLevel | dwMode;
  152. //
  153. // Open log file
  154. //
  155. cbName = ::GetWindowsDirectory(m_szLogFilePath,sizeof(m_szLogFilePath)/sizeof(m_szLogFilePath[0]));
  156. if (( cbName == 0) || !*m_szLogFilePath ) {
  157. ReportError(::GetLastError());
  158. return;
  159. }
  160. lstrcat(lstrcat(m_szLogFilePath,TEXT("\\")),lpActualLogName);
  161. m_hLogFile = ::CreateFile(m_szLogFilePath,
  162. GENERIC_WRITE,
  163. FILE_SHARE_WRITE | FILE_SHARE_READ,
  164. NULL, // security attributes
  165. OPEN_ALWAYS,
  166. FILE_ATTRIBUTE_NORMAL,
  167. NULL); // template file handle
  168. // if ( m_hLogFile!= INVALID_HANDLE_VALUE) {
  169. if (IS_VALID_HANDLE(m_hLogFile)) {
  170. if(fTruncate) {
  171. ::SetFilePointer( m_hLogFile, 0, NULL, FILE_BEGIN );
  172. ::SetEndOfFile( m_hLogFile );
  173. }
  174. ::SetFilePointer( m_hLogFile, 0, NULL, FILE_END);
  175. lstrcpy(szTempName,szMutexNamePrefix);
  176. lstrcat(szTempName,lpActualLogName);
  177. m_hMutex = ::CreateMutex(NULL,
  178. FALSE,
  179. szTempName
  180. );
  181. if ( !IS_VALID_HANDLE(m_hMutex ) ) {
  182. // ASSERT
  183. }
  184. }
  185. if (!IS_VALID_HANDLE(m_hLogFile) || !IS_VALID_HANDLE(m_hMutex )) {
  186. ReportError(::GetLastError());
  187. }
  188. else {
  189. //
  190. // Success
  191. //
  192. }
  193. } /* STI_FILE_LOG::STI_FILE_LOG() */
  194. STI_FILE_LOG::~STI_FILE_LOG( VOID)
  195. /*++
  196. Description:
  197. Destructor function for given STI_FILE_LOG object.
  198. --*/
  199. {
  200. SYSTEMTIME stCurrentTime;
  201. TCHAR szFmtDate[20];
  202. TCHAR szFmtTime[32];
  203. TCHAR szTextBuffer[128];
  204. if(m_lWrittenHeader) {
  205. GetLocalTime(&stCurrentTime);
  206. FormatStdDate( &stCurrentTime, szFmtDate, 15);
  207. FormatStdTime( &stCurrentTime, szFmtTime, sizeof(szFmtTime) / sizeof(szFmtTime[0]));
  208. ::wsprintf(szTextBuffer,szClosedLog,
  209. szFmtDate,szFmtTime,
  210. m_szTracerName,
  211. m_szProcessName,
  212. ::GetCurrentThreadId()
  213. );
  214. WriteStringToLog(szTextBuffer);
  215. }
  216. if (IS_VALID_HANDLE(m_hMutex)) {
  217. ::CloseHandle(m_hMutex);
  218. m_hMutex = INVALID_HANDLE_VALUE;
  219. }
  220. if (IS_VALID_HANDLE(m_hLogFile)) {
  221. ::FlushFileBuffers( m_hLogFile);
  222. ::CloseHandle(m_hLogFile);
  223. m_hLogFile = INVALID_HANDLE_VALUE;
  224. }
  225. m_dwSignature = SIGNATURE_FILE_LOG_FREE;
  226. } /* STI_FILE_LOG::~STI_FILE_LOG() */
  227. //
  228. // IUnknown methods. Used only for reference counting
  229. //
  230. STDMETHODIMP
  231. STI_FILE_LOG::QueryInterface( REFIID riid, LPVOID * ppvObj)
  232. {
  233. return E_FAIL;
  234. }
  235. STDMETHODIMP_(ULONG)
  236. STI_FILE_LOG::AddRef( void)
  237. {
  238. ::InterlockedIncrement(&m_cRef);
  239. return m_cRef;
  240. }
  241. STDMETHODIMP_(ULONG)
  242. STI_FILE_LOG::Release( void)
  243. {
  244. LONG cNew;
  245. if(!(cNew = ::InterlockedDecrement(&m_cRef))) {
  246. delete this;
  247. }
  248. return cNew;
  249. }
  250. void
  251. STI_FILE_LOG::
  252. WriteLogSessionHeader(
  253. VOID
  254. )
  255. /*++
  256. Description
  257. Arguments:
  258. Note:
  259. --*/
  260. {
  261. SYSTEMTIME stCurrentTime;
  262. TCHAR szFmtDate[32];
  263. TCHAR szFmtTime[32];
  264. TCHAR szTextBuffer[128];
  265. GetLocalTime(&stCurrentTime);
  266. FormatStdDate( &stCurrentTime, szFmtDate, sizeof(szFmtDate) / sizeof(szFmtDate[0]));
  267. FormatStdTime( &stCurrentTime, szFmtTime, sizeof(szFmtTime) / sizeof(szFmtTime[0]));
  268. ::wsprintf(szTextBuffer,szOpenedLog,
  269. szFmtDate,szFmtTime,
  270. m_szTracerName,
  271. m_szProcessName,
  272. ::GetCurrentThreadId()
  273. );
  274. WriteStringToLog(szTextBuffer);
  275. WriteStringToLog(szColumns);
  276. }
  277. void
  278. STI_FILE_LOG::
  279. ReportMessage(
  280. DWORD dwType,
  281. LPCTSTR pszMsg,
  282. ...
  283. )
  284. /*++
  285. Description
  286. Arguments:
  287. Note:
  288. --*/
  289. {
  290. va_list list;
  291. TCHAR *pchBuff = NULL;
  292. DWORD cch;
  293. va_start (list, pszMsg);
  294. pchBuff = NULL;
  295. cch = ::FormatMessage(FORMAT_MESSAGE_MAX_WIDTH_MASK |
  296. FORMAT_MESSAGE_ALLOCATE_BUFFER |
  297. FORMAT_MESSAGE_FROM_STRING,
  298. pszMsg,
  299. 0,
  300. 0,
  301. (LPTSTR) &pchBuff,
  302. 1024,
  303. &list
  304. );
  305. vReportMessage(dwType,pszMsg,list);
  306. va_end(list);
  307. }
  308. void
  309. STI_FILE_LOG::
  310. ReportMessage(
  311. DWORD dwType,
  312. DWORD idMessage,
  313. ...
  314. )
  315. /*++
  316. Description
  317. Arguments:
  318. Note:
  319. --*/
  320. {
  321. va_list list;
  322. TCHAR *pchBuff = NULL;
  323. DWORD cch;
  324. va_start (list, idMessage);
  325. //
  326. // Read message and add inserts
  327. //
  328. pchBuff = NULL;
  329. cch = ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
  330. FORMAT_MESSAGE_MAX_WIDTH_MASK |
  331. FORMAT_MESSAGE_FROM_HMODULE,
  332. m_hDefaultMessageModule ,
  333. idMessage,
  334. 0,
  335. (LPTSTR) &pchBuff,
  336. 1024,
  337. &list
  338. );
  339. if (pchBuff && cch) {
  340. vReportMessage(dwType,pchBuff,list);
  341. LocalFree(pchBuff);
  342. }
  343. va_end(list);
  344. }
  345. void
  346. STI_FILE_LOG::
  347. vReportMessage(
  348. DWORD dwType,
  349. LPCTSTR pszMsg,
  350. va_list arglist
  351. )
  352. /*++
  353. Description
  354. Arguments:
  355. Note:
  356. --*/
  357. {
  358. TCHAR achTextBuffer[1024];
  359. DWORD dwReportMode;
  360. if ((QueryError() != NOERROR) ||
  361. (!m_hLogFile || m_hLogFile == INVALID_HANDLE_VALUE)) {
  362. return;
  363. }
  364. // Do we need to display this message ?
  365. if (!((dwType & m_dwReportMode) & STI_TRACE_MESSAGE_TYPE_MASK)) {
  366. return;
  367. }
  368. // Start message with type
  369. *achTextBuffer = TraceFlagToLetter(dwType);
  370. *(achTextBuffer+1) = TEXT(' ');
  371. *(achTextBuffer+2) = TEXT('\0');
  372. // Add name of tracer source
  373. lstrcat(lstrcat(achTextBuffer,m_szTracerName),TEXT(" "));;
  374. dwReportMode = m_dwReportMode | (dwType & STI_TRACE_MESSAGE_FLAGS_MASK);
  375. //
  376. // Prepare header if needed
  377. //
  378. if (dwReportMode & STI_TRACE_MESSAGE_FLAGS_MASK ) {
  379. if (dwReportMode & STI_TRACE_ADD_THREAD ) {
  380. //
  381. // Add process::thread ID
  382. //
  383. TCHAR szThreadId[40];
  384. ::wsprintf(szThreadId,TEXT("[%#s::%#lx] "),m_szProcessName,::GetCurrentThreadId());
  385. ::lstrcat(::lstrcat(achTextBuffer,szThreadId),TEXT(" "));
  386. }
  387. if (dwReportMode & STI_TRACE_ADD_TIME ) {
  388. // Add current time
  389. SYSTEMTIME stCurrentTime;
  390. TCHAR szFmtTime[32];
  391. *szFmtTime = TEXT('\0');
  392. ::GetLocalTime(&stCurrentTime);
  393. FormatStdTime( &stCurrentTime, szFmtTime, 15);
  394. lstrcat(lstrcat(achTextBuffer,szFmtTime),TEXT(" "));;
  395. }
  396. }
  397. lstrcat(achTextBuffer,TEXT("\t"));
  398. //
  399. // Now add message text itself
  400. //
  401. ::wvsprintf(achTextBuffer+::lstrlen(achTextBuffer), pszMsg, arglist);
  402. ::lstrcat(achTextBuffer , TEXT("\r\n"));
  403. //
  404. // Write to the file, flushing buffers if message type is ERROR
  405. //
  406. WriteStringToLog(achTextBuffer,(dwType & STI_TRACE_MESSAGE_TYPE_MASK) & STI_TRACE_ERROR);
  407. }
  408. VOID
  409. STI_FILE_LOG::
  410. WriteStringToLog(
  411. LPCTSTR pszTextBuffer,
  412. BOOL fFlush // = FALSE
  413. )
  414. {
  415. DWORD dwcbWritten;
  416. LONG lHeaderWrittenFlag;
  417. //
  418. // Integrity check. Make sure the mutex and file handles are valid - if not,
  419. // then bail.
  420. //
  421. if (!(IS_VALID_HANDLE(m_hMutex) && IS_VALID_HANDLE(m_hLogFile))) {
  422. #ifdef DEBUG
  423. //OutputDebugString(TEXT("STILOG File or Mutex handle is invalid. "));
  424. #endif
  425. return ;
  426. }
  427. //
  428. // If had not done yet, write session header here. Note this is recursive call
  429. // and flag marking header should be set first.
  430. //
  431. lHeaderWrittenFlag = InterlockedExchange(&m_lWrittenHeader,1L);
  432. if (!lHeaderWrittenFlag) {
  433. WriteLogSessionHeader();
  434. }
  435. TAKE_MUTEX t(m_hMutex);
  436. //
  437. // Check that log file size is not exceeding the limit. Assuming log file size is set to fit
  438. // into 32bit field, so we don't bother looking at high dword.
  439. //
  440. // Nb: We are not saving backup copy of the log, expecting that it never grows that large
  441. //
  442. BY_HANDLE_FILE_INFORMATION fi;
  443. if (!GetFileInformationByHandle(m_hLogFile,&fi)) {
  444. #ifdef DEBUG
  445. OutputDebugString(TEXT("STILOG could not get file size for log file. "));
  446. #endif
  447. return ;
  448. }
  449. if ( fi.nFileSizeHigh !=0 || (fi.nFileSizeLow > m_dwMaxSize) ){
  450. ::SetFilePointer( m_hLogFile, 0, NULL, FILE_BEGIN );
  451. ::SetEndOfFile( m_hLogFile );
  452. ::GetFileInformationByHandle(m_hLogFile,&fi);
  453. }
  454. #ifdef USE_FILE_LOCK
  455. ::LockFile(m_hLogFile,fi.nFileSizeLow,fi.nFileSizeHigh,4096,0);
  456. #endif
  457. ::SetFilePointer( m_hLogFile, 0, NULL, FILE_END);
  458. #ifdef _UNICODE
  459. UINT len = lstrlen(pszTextBuffer);
  460. CHAR *ansiBuffer = (CHAR *)alloca(len + 2);
  461. if(ansiBuffer) {
  462. ::WideCharToMultiByte(CP_ACP,
  463. 0,
  464. pszTextBuffer,
  465. -1,
  466. ansiBuffer,
  467. len + 1,
  468. NULL,
  469. NULL);
  470. ::WriteFile(m_hLogFile,
  471. ansiBuffer,
  472. len,
  473. &dwcbWritten,
  474. NULL);
  475. }
  476. #else
  477. ::WriteFile(m_hLogFile,
  478. pszTextBuffer,
  479. lstrlen(pszTextBuffer),
  480. &dwcbWritten,
  481. NULL);
  482. #endif
  483. #ifdef USE_FILE_LOCK
  484. ::UnlockFile(m_hLogFile,fi.nFileSizeLow,fi.nFileSizeHigh,4096,0);
  485. #endif
  486. if (fFlush) {
  487. // Flush buffers to disk
  488. FlushFileBuffers(m_hLogFile);
  489. }
  490. if(QueryReportMode() & STI_TRACE_LOG_TOUI) {
  491. ULONG_PTR dwResult;
  492. SendMessageTimeout(m_hLogWindow,
  493. STIMON_MSG_LOG_MESSAGE,
  494. 0,
  495. (LPARAM)pszTextBuffer,
  496. 0,
  497. 100,
  498. &dwResult
  499. );
  500. }
  501. #ifdef DEBUG
  502. ::OutputDebugString(pszTextBuffer);
  503. #endif
  504. }
  505. //
  506. // C-APIs
  507. //
  508. HANDLE
  509. WINAPI
  510. CreateStiFileLog(
  511. IN LPCTSTR lpszTracerName,
  512. IN LPCTSTR lpszLogName,
  513. IN DWORD dwReportMode
  514. )
  515. /*++
  516. Description
  517. Arguments:
  518. Note:
  519. --*/
  520. {
  521. HANDLE hRet = INVALID_HANDLE_VALUE;
  522. STI_FILE_LOG* pStiFileLog = NULL;
  523. pStiFileLog = new STI_FILE_LOG(lpszTracerName,lpszLogName);
  524. if(pStiFileLog){
  525. if (pStiFileLog->IsValid()) {
  526. hRet = static_cast<HANDLE>(pStiFileLog);
  527. pStiFileLog->SetReportMode(pStiFileLog->QueryReportMode() | dwReportMode);
  528. } else {
  529. //
  530. // Notice that we delete this object rather than calling
  531. // CloseStiFileLog. We do this because it the filelog is
  532. // not valid (maybe it had an internal creation error),
  533. // CloseStiFileLog won't try to delete it, hence we delete
  534. // it here.
  535. //
  536. delete pStiFileLog;
  537. pStiFileLog = NULL;
  538. hRet = INVALID_HANDLE_VALUE;
  539. }
  540. } else {
  541. ::SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  542. }
  543. return hRet;
  544. }
  545. DWORD
  546. WINAPI
  547. CloseStiFileLog(
  548. IN HANDLE hFileLog
  549. )
  550. {
  551. STI_FILE_LOG* pStiFileLog = NULL;
  552. pStiFileLog = static_cast<STI_FILE_LOG*>(hFileLog);
  553. if (IsBadWritePtr(pStiFileLog,sizeof(STI_FILE_LOG)) ||
  554. !pStiFileLog->IsValid()) {
  555. ::SetLastError(ERROR_INVALID_HANDLE);
  556. return ERROR_INVALID_HANDLE;
  557. }
  558. delete pStiFileLog;
  559. return NOERROR;
  560. }
  561. DWORD
  562. WINAPI
  563. ReportStiLogMessage(
  564. IN HANDLE hFileLog,
  565. IN DWORD dwType,
  566. IN LPCTSTR psz,
  567. ...
  568. )
  569. {
  570. STI_FILE_LOG* pStiFileLog = NULL;
  571. pStiFileLog = static_cast<STI_FILE_LOG*>(hFileLog);
  572. if (IsBadWritePtr(pStiFileLog,sizeof(STI_FILE_LOG)) ||
  573. !pStiFileLog->IsValid()) {
  574. ::SetLastError(ERROR_INVALID_HANDLE);
  575. return ERROR_INVALID_HANDLE;
  576. }
  577. va_list list;
  578. va_start (list, psz);
  579. pStiFileLog->vReportMessage(dwType,psz,list);
  580. va_end(list);
  581. return NOERROR;
  582. }
  583. /********************************* End of File ***************************/