Sync DrDump crash handler with TortoiseSVN codebase
[TortoiseGit.git] / ext / CrashServer / CommonLibs / Log / log.cpp
blob771f263cc6f87410ef06e3c8b44a8436c7afb7d4
1 // Copyright 2014 Idol Software, Inc.
2 //
3 // This file is part of Doctor Dump SDK.
4 //
5 // Doctor Dump SDK is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU Lesser General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU Lesser General Public License for more details.
15 // You should have received a copy of the GNU Lesser General Public License
16 // along with this program. If not, see <http://www.gnu.org/licenses/>.
18 #define WIN32_LEAN_AND_MEAN
19 #include <windows.h>
20 #include <string>
21 #include <tchar.h>
22 #include <assert.h>
23 #include <time.h>
24 #include <crtdbg.h>
25 #include "log_media.h"
27 #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
28 #include <VersionHelpers.h>
29 #endif
31 #define ASSERT(f) assert(f)
33 #ifndef VERIFY
34 # ifdef _DEBUG
35 # define VERIFY(f) ASSERT(f)
36 # else
37 # define VERIFY(f) ((void)(f))
38 # endif
39 #endif
41 static DWORD g_dwThreadNameTlsId = TLS_OUT_OF_INDEXES;
42 static LPTSTR pszUnknownThreadName = _T("Unknown");
43 void InitializeLog()
45 static bool bInitialized = false;
46 if (bInitialized)
47 return;
48 bInitialized = true;
49 g_dwThreadNameTlsId = TlsAlloc();
50 ASSERT(g_dwThreadNameTlsId != TLS_OUT_OF_INDEXES);
52 LPCTSTR GetLogThreadName()
54 ASSERT(g_dwThreadNameTlsId != TLS_OUT_OF_INDEXES);
55 if (g_dwThreadNameTlsId == TLS_OUT_OF_INDEXES)
56 return NULL;
57 return static_cast<LPCTSTR>(TlsGetValue(g_dwThreadNameTlsId));
59 void FreeLogThreadName()
61 ASSERT(g_dwThreadNameTlsId != TLS_OUT_OF_INDEXES);
62 if (g_dwThreadNameTlsId == TLS_OUT_OF_INDEXES)
63 return;
64 LPTSTR pThreadName = static_cast<LPTSTR>(TlsGetValue(g_dwThreadNameTlsId));
65 if (pThreadName != pszUnknownThreadName)
67 free(pThreadName);
68 VERIFY(TlsSetValue(g_dwThreadNameTlsId, pszUnknownThreadName));
71 void SetLogThreadName(LPCTSTR pszThreadName)
73 ASSERT(g_dwThreadNameTlsId != TLS_OUT_OF_INDEXES);
74 if (g_dwThreadNameTlsId == TLS_OUT_OF_INDEXES)
75 return;
76 FreeLogThreadName();
77 VERIFY(TlsSetValue(g_dwThreadNameTlsId, LPVOID(pszThreadName != NULL ? _tcsdup(pszThreadName) : pszUnknownThreadName)));
78 // SetDebuggerThreadName(-1, __LPCSTR(pszThreadName));
81 //! Создаёт путь к файлу.
82 bool CreateFileDir(LPCTSTR pszFilePath)
84 // Найдём полный путь
85 TCHAR szBuffer[1024];
86 LPTSTR pszFilePart;
87 DWORD dwRes = GetFullPathName(pszFilePath, sizeof(szBuffer)/sizeof(*szBuffer), szBuffer, &pszFilePart);
88 if (dwRes == 0
89 || dwRes >= sizeof(szBuffer)/sizeof(*szBuffer)
90 || pszFilePart == NULL)
91 return false;
93 // Отрежем имя файла
94 *pszFilePart = _T('\0');
96 CString sPath(szBuffer);
97 sPath.Replace(_T('/'), _T('\\'));
98 ASSERT(sPath.Right(1) == _T("\\"));
100 int nPos;
101 if (sPath.Left(2) == _T("\\\\"))
102 nPos = sPath.Find(_T("\\"), sPath.Find(_T("\\"), 2) + 1) + 1; // Пропустим имя компьютера и шары
103 else
104 nPos = 4; // Пропустим имя диска
105 while ( (nPos = sPath.Find(_T('\\'), nPos + 1)) != -1)
107 if (!CreateDirectory(sPath.Left(nPos), NULL) && GetLastError() != ERROR_ALREADY_EXISTS)
108 return false;
110 return true;
113 void LogBase::SetThreadName(LPCTSTR pszThreadName)
115 SetLogThreadName(pszThreadName);
118 LPCTSTR LogBase::GetThreadName()
120 LPCTSTR pszName = GetLogThreadName();
121 return pszName ? pszName : _T("Unknown");
125 LogMediaPtr LogBase::GetAppLogMedia()
127 static LogMediaPtr pAppLog(new LogMediaProxy());
128 return pAppLog;
131 void LogBase::SetAppLogMedia(LogMediaPtr pLog)
133 ASSERT(pLog != GetAppLogMedia());
134 if (pLog == GetAppLogMedia())
135 return;
136 std::static_pointer_cast<LogMediaProxy>(GetAppLogMedia())->SetLog(pLog);
139 LogMediaPtr LogBase::CreateConsoleMedia()
141 static LogMediaPtr pConsoleMedia(new ConsoleMedia());
142 if (pConsoleMedia->IsWorking())
143 return pConsoleMedia;
144 return LogMediaPtr();
147 LogMediaPtr LogBase::CreateFileMedia(LPCTSTR pszFilename, bool bAppend, bool bFlush, bool bNewFileDaily)
149 LogMediaPtr pLog(new FileMedia(pszFilename, bAppend, bFlush, bNewFileDaily));
150 if (pLog->IsWorking())
151 return pLog;
152 return LogMediaPtr();
155 LogMediaPtr LogBase::CreateDebugMedia(DWORD dwParam)
157 LogMediaPtr pDebugMedia(new DebugMedia(dwParam));
158 if (pDebugMedia->IsWorking())
159 return pDebugMedia;
160 return LogMediaPtr();
164 //////////////////////////////////////////////////////////////////////////
166 //////////////////////////////////////////////////////////////////////////
168 void FormatLogMessage(ELogMessageType type,
169 ELogMessageLevel nLevel,
170 LPCTSTR pszDate,
171 LPCTSTR pszTime,
172 LPCTSTR pszThreadId,
173 LPCTSTR pszThreadName,
174 LPCTSTR pszModule,
175 LPCTSTR pszMessage,
176 CString& output)
178 #if 1
179 output.Empty();
180 output.Preallocate(1024);
182 if (type != eLM_DirectOutput)
184 output += pszDate;
185 output += _T(' ');
186 output += pszTime;
187 #if defined(LOG_THREAD_NAME)
188 output += _T(" [");
189 output += pszThreadId;
190 output += _T(":");
191 size_t nThreadNameLen = _tcslen(pszThreadName);
192 if (nThreadNameLen > 12)
193 output.append(pszThreadName, 12);
194 else
196 output.append(12 - nThreadNameLen, _T(' '));
197 output += pszThreadName;
199 output += _T("] ");
200 #else
201 output += _T(" ");
202 output += pszThreadId;
203 output += _T(" ");
204 #endif
206 switch (type)
208 case eLM_Info:
209 output += _T('I');
210 break;
211 case eLM_Debug:
212 output += _T('-');
213 break;
214 case eLM_Warning:
215 output += _T('W');
216 break;
217 case eLM_Error:
218 output += _T('E');
219 break;
220 default:
221 ASSERT(false);
224 if (nLevel > 0)
225 output.AppendFormat(_T("%i"), nLevel);
226 else
227 output += _T(' ');
228 #if 0
229 output += _T(" : [");
230 output += pszModule;
231 output += _T("] ");
232 #else
233 output += _T(" : ");
234 #endif
236 output += pszMessage;
237 output.TrimRight();
238 #else
239 output.Empty();
240 output.reserve(1024);
242 output += pszDate;
243 output += _T(' ');
244 output += pszTime;
245 output += _T("\t");
246 output += pszThreadId;
247 output += _T("\t");
248 output += pszThreadName;
249 output += _T("\t");
250 output += pszModule;
251 output += _T("\t");
253 switch (type)
255 case eLM_Info:
256 output += _T("Inf");
257 break;
258 case eLM_Debug:
259 output += _T("Dbg");
260 break;
261 case eLM_Warning:
262 output += _T("Wrn");
263 break;
264 case eLM_Error:
265 output += _T("Err");
266 break;
267 default:
268 ASSERT(false);
271 if (nLevel > 0)
272 output.AppendFormat(_T("%i"), nLevel);
273 output += _T('\t');
274 output += pszMessage;
275 output.TrimRight();
276 #endif
279 CString GetSystemInformation()
281 CString info = _T("Microsoft Windows ");
283 #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
284 if (IsWindows8Point1OrGreater())
285 info += _T(">= 8.1");
286 else if (IsWindows8OrGreater())
287 info += _T("8.1");
288 else if (IsWindows7SP1OrGreater())
289 info += _T("7 SP1");
290 else if (IsWindows7OrGreater())
291 info += _T("7");
292 else if (IsWindowsVistaSP2OrGreater())
293 info += _T("Vista SP2");
294 else if (IsWindowsVistaSP1OrGreater())
295 info += _T("Vista SP1");
296 else if (IsWindowsVistaOrGreater())
297 info += _T("Vista");
299 if (IsWindowsServer())
300 info += _T(" Server");
301 else
302 info += _T(" Client");
303 #else
304 OSVERSIONINFOEX rcOS;
305 ZeroMemory(&rcOS, sizeof(rcOS));
306 rcOS.dwOSVersionInfoSize = sizeof(rcOS);
308 GetVersionEx((OSVERSIONINFO*)&rcOS);
310 if (rcOS.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
312 switch (rcOS.dwMinorVersion)
314 case 0: info += _T("95"); break;
315 case 10: info += _T("98"); break;
316 case 90: info += _T("Me"); break;
318 if (!_tcscmp(rcOS.szCSDVersion, _T(" A")))
319 info += _T(" Second Edition");
320 else
321 if (!_tcscmp(rcOS.szCSDVersion, _T(" C")))
322 info += _T(" OSR2");
324 else if (rcOS.dwPlatformId == VER_PLATFORM_WIN32_NT)
326 switch (rcOS.dwMajorVersion)
328 case 3: info += _T("NT 3.51"); break;
329 case 4: info += _T("NT 4.0"); break;
330 case 5:
331 switch (rcOS.dwMinorVersion)
333 case 0: info += _T("2000"); break;
334 case 1: info += _T("XP"); break;
335 case 2: info += _T("2003 Server"); break;
336 default: ASSERT(false); info += _T("future version"); break;
338 break;
339 case 6:
340 switch (rcOS.dwMinorVersion)
342 case 0: info += rcOS.wProductType == VER_NT_WORKSTATION ? _T("Vista") : _T("Server 2008"); break;
343 case 1: info += rcOS.wProductType == VER_NT_WORKSTATION ? _T("7") : _T("Server 2008 R2"); break;
344 case 2: info += rcOS.wProductType == VER_NT_WORKSTATION ? _T("8") : _T("Server 2012"); break;
345 case 3: info += rcOS.wProductType == VER_NT_WORKSTATION ? _T("8.1") : _T("Server 2012 R2"); break;
346 default:
348 CString future;
349 future.Format(_T("%ld.%ld%s"), rcOS.dwMajorVersion, rcOS.dwMinorVersion, VER_NT_WORKSTATION ? _T("") : _T(" Server"));
350 info += future;
351 break;
354 break;
355 default:
357 CString future;
358 future.Format(_T("%ld.%ld%s"), rcOS.dwMajorVersion, rcOS.dwMinorVersion, VER_NT_WORKSTATION ? _T("") : _T(" Server"));
359 info += future;
360 break;
363 if (_tcslen(rcOS.szCSDVersion) > 0)
365 info += _T(", ");
366 info += rcOS.szCSDVersion;
369 #endif
370 return info;
373 CString GetModuleInformation()
375 CString info;
376 TCHAR szApp[MAX_PATH];
377 if (!GetModuleFileName(NULL, szApp, _countof(szApp)))
378 return info;
380 info = szApp;
382 DWORD dwDummy;
383 DWORD dwInfoSize = GetFileVersionInfoSize(szApp, &dwDummy);
384 if (dwInfoSize)
386 LPVOID pInfoBuffer = _alloca(dwInfoSize);
387 if (GetFileVersionInfo(szApp, 0, dwInfoSize, pInfoBuffer))
389 VS_FIXEDFILEINFO *pInfo = NULL;
390 UINT nInfoSize = 0;
391 if (VerQueryValue (pInfoBuffer, _T("\\"), (LPVOID *)&pInfo, &nInfoSize) && nInfoSize == sizeof(VS_FIXEDFILEINFO))
393 info.AppendFormat(_T(" (%d.%d.%d.%d)"),
394 HIWORD(pInfo->dwFileVersionMS),
395 LOWORD(pInfo->dwFileVersionMS),
396 HIWORD(pInfo->dwFileVersionLS),
397 LOWORD(pInfo->dwFileVersionLS)
403 info.AppendFormat(_T(" PID: %d (0x%x)"), GetCurrentProcessId(), GetCurrentProcessId());
405 return info;
408 //////////////////////////////////////////////////////////////////////////
409 // CConsoleMedia
410 //////////////////////////////////////////////////////////////////////////
412 ConsoleMedia::ConsoleMedia()
414 m_bRedirectedToFile = true;
416 m_hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
417 if (m_hConsole == NULL)
419 if (!AllocConsole() //|| !SetConsoleTitle(_T("Log"))
420 || (m_hConsole = GetStdHandle(STD_OUTPUT_HANDLE)) == NULL)
421 return;
423 GetConsoleScreenBufferInfo(m_hConsole, &m_info);
425 DWORD t;
426 m_bRedirectedToFile = GetConsoleMode(m_hConsole, &t) == FALSE;
429 void ConsoleMedia::Write(ELogMessageType type,
430 ELogMessageLevel nLevel,
431 LPCTSTR pszDate,
432 LPCTSTR pszTime,
433 LPCTSTR pszThreadId,
434 LPCTSTR pszThreadName,
435 LPCTSTR pszModule,
436 LPCTSTR pszMessage)
438 if (m_hConsole==NULL)
439 return;
440 WORD color = FOREGROUND_GREEN|FOREGROUND_INTENSITY;
441 //FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_INTENSITY|BACKGROUND_RED|BACKGROUND_GREEN|BACKGROUND_BLUE|BACKGROUND_INTENSITY;
442 switch (type)
444 case eLM_Debug:
445 color = FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE;
446 break;
447 case eLM_Warning:
448 color = FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_INTENSITY;
449 break;
450 case eLM_Error:
451 color = FOREGROUND_RED|FOREGROUND_INTENSITY;
452 break;
455 CString output;
456 FormatLogMessage(type, nLevel, pszDate, pszTime, pszThreadId, pszThreadName, pszModule, pszMessage, output);
458 if (m_bRedirectedToFile)
460 output += _T("\n");
461 CStringA outputA = output;
462 outputA.AnsiToOem();
464 CriticalSection::SyncLock lock(m_cs);
465 DWORD dwWritten;
466 WriteFile(m_hConsole, (LPCSTR)outputA, (DWORD)outputA.GetLength(), &dwWritten, NULL);
468 else
470 #ifndef _UNICODE
471 output.AnsiToOem();
472 #endif
473 CriticalSection::SyncLock lock(m_cs);
474 SetConsoleTextAttribute(m_hConsole, color);
475 DWORD dwWritten;
476 WriteConsole(m_hConsole, (LPCTSTR)output, (DWORD)output.GetLength(), &dwWritten, NULL);
478 SetConsoleTextAttribute(m_hConsole, m_info.wAttributes);
479 WriteConsole(m_hConsole, _T("\n"), 1, &dwWritten, NULL);
483 //////////////////////////////////////////////////////////////////////////
484 // CFileMedia
485 //////////////////////////////////////////////////////////////////////////
487 FileMedia::FileMedia(LPCTSTR pszFilename, bool bAppend, bool bFlush, bool bNewFileDaily)
488 : m_bAppend(bAppend), m_bFlush(bFlush), m_bNewFileDaily(bNewFileDaily), m_wLogYear(0), m_wLogMonth(0), m_wLogDay(0),
489 m_sOrigFilename(pszFilename)
491 OpenLogFile();
494 FileMedia::~FileMedia()
496 CloseLogFile();
499 void FileMedia::CloseLogFile()
501 if (m_hFile != INVALID_HANDLE_VALUE && WaitForSingleObject(m_hMutex, 1000) != WAIT_TIMEOUT)
503 SYSTEMTIME st;
504 GetLocalTime(&st);
505 CString header;
506 header.Format(
507 _T("=================================================\r\n")
508 _T("=== Trace Log Finished on %i-%02i-%02i %02i:%02i:%02i ===\r\n")
509 _T("=================================================\r\n")
510 , st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
512 LONG dwDistanceToMoveHigh = 0; // Нужен, чтобы писать в файлы больше 4Гб
513 VERIFY(SetFilePointer(m_hFile, 0, &dwDistanceToMoveHigh, FILE_END) != INVALID_SET_FILE_POINTER || GetLastError() == NO_ERROR);
514 CStringA headerA = header;
515 DWORD dwWritten;
516 // VERIFY(WriteFile(m_hFile, (LPCTSTR)header, (DWORD) header.size() * sizeof(TCHAR), &dwWritten, NULL));
517 VERIFY(WriteFile(m_hFile, (LPCSTR)headerA, (DWORD) headerA.GetLength() * sizeof(CHAR), &dwWritten, NULL));
518 VERIFY(ReleaseMutex(m_hMutex));
522 bool FileMedia::IsWorking() const
524 return m_hFile != INVALID_HANDLE_VALUE;
527 void FileMedia::OpenLogFile()
529 SYSTEMTIME st;
530 GetLocalTime(&st);
532 m_sFilename = m_sOrigFilename;
533 m_sFilename.Replace(_T("%DATETIME%"), _T("%DATE% %TIME%"));
534 if (m_sFilename.Find(_T("%DATE%")) != -1)
536 TCHAR bufdate[128];
537 _stprintf_s(bufdate, _T("%i-%02i-%02i"), st.wYear, st.wMonth, st.wDay);
538 m_sFilename.Replace(_T("%DATE%"), bufdate);
539 m_wLogYear = st.wYear;
540 m_wLogMonth = st.wMonth;
541 m_wLogDay = st.wDay;
543 if (m_sFilename.Find(_T("%TIME%")) != -1)
545 GetLocalTime(&st);
546 TCHAR buftime[128];
547 _stprintf_s(buftime, _T("%02i-%02i"), st.wHour, st.wMinute);
548 m_sFilename.Replace(_T("%TIME%"), buftime);
551 if (!CreateFileDir(m_sFilename))
553 _RPT1(_CRT_ERROR, "FileMedia: Can't create folder '%S'", (LPCWSTR) CStringW(m_sFilename));
554 return;
558 // Создадим для доступа к этому файлу мьютекс
559 CString sMtx(m_sFilename);
560 sMtx.MakeUpper();
561 for (int i = 0, size = sMtx.GetLength(); i < size; ++i)
563 if (!_istalnum(sMtx[i]))
564 sMtx.SetAt(i, _T('_'));
566 m_hMutex = CreateMutex(NULL, TRUE, (LPCTSTR)sMtx);
567 DWORD dwMtxError = GetLastError();
569 m_hFile = CreateFile(m_sFilename,
570 GENERIC_WRITE,
571 FILE_SHARE_READ | FILE_SHARE_WRITE,
572 NULL,
573 (m_bAppend || dwMtxError == ERROR_ALREADY_EXISTS) ? OPEN_ALWAYS : CREATE_ALWAYS,
574 FILE_ATTRIBUTE_NORMAL,
575 NULL);
577 if (m_hFile == INVALID_HANDLE_VALUE)
579 if (dwMtxError != ERROR_ALREADY_EXISTS)
580 VERIFY(ReleaseMutex(m_hMutex));
581 m_hMutex.Close();
582 return;
585 if (dwMtxError != ERROR_ALREADY_EXISTS)
587 CString header;
588 header.Format(
589 _T("================================================\r\n")
590 _T("=== Trace Log Started on %i-%02i-%02i %02i:%02i:%02i ===\r\n")
591 _T("=== %s ===\r\n")
592 _T("================================================\r\n")
593 _T("\r\n%s\r\n\r\n")
594 , st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond
595 , (LPCTSTR)GetSystemInformation()
596 , (LPCTSTR)GetModuleInformation());
598 LONG dwDistanceToMoveHigh = 0; // Нужен, чтобы писать в файлы больше 4Гб
599 VERIFY(SetFilePointer(m_hFile, 0, &dwDistanceToMoveHigh, FILE_END) != INVALID_SET_FILE_POINTER || GetLastError() == NO_ERROR);
600 CStringA headerA = header;
601 DWORD dwWritten;
602 VERIFY(WriteFile(m_hFile, (LPCSTR)headerA, (DWORD)headerA.GetLength() * sizeof(CHAR), &dwWritten, NULL));
603 if (m_hMutex != NULL)
604 VERIFY(ReleaseMutex(m_hMutex));
608 void FileMedia::Write(ELogMessageType type, ELogMessageLevel nLevel, LPCTSTR pszDate, LPCTSTR pszTime, LPCTSTR pszThreadId, LPCTSTR pszThreadName, LPCTSTR pszModule, LPCTSTR pszMessage)
610 if (m_bNewFileDaily && m_wLogYear != 0)
612 SYSTEMTIME st;
613 GetLocalTime(&st);
614 if (st.wDay != m_wLogDay || st.wMonth != m_wLogMonth || st.wDay != m_wLogDay)
616 CloseLogFile();
617 OpenLogFile();
621 if (WaitForSingleObject(m_hMutex, 1000) == WAIT_TIMEOUT)
622 return;
624 // trying to restore access
625 if (m_hFile == INVALID_HANDLE_VALUE)
627 m_hFile = CreateFile(m_sFilename,
628 GENERIC_WRITE,
629 FILE_SHARE_READ | FILE_SHARE_WRITE,
630 NULL,
631 OPEN_ALWAYS,
632 FILE_ATTRIBUTE_NORMAL,
633 NULL);
634 if (m_hFile == INVALID_HANDLE_VALUE)
636 VERIFY(ReleaseMutex(m_hMutex));
637 return;
641 LONG dwDistanceToMoveHigh = 0; // Нужен, чтобы писать в файлы больше 4Гб
642 if (SetFilePointer(m_hFile, 0, &dwDistanceToMoveHigh, FILE_END) == INVALID_SET_FILE_POINTER
643 && GetLastError() != NO_ERROR)
645 _RPT0(_CRT_ERROR, "FileMedia: Can't set file pointer");
646 m_hFile.Close();
647 VERIFY(ReleaseMutex(m_hMutex));
648 return;
651 CString output;
652 FormatLogMessage(type, nLevel, pszDate, pszTime, pszThreadId, pszThreadName, pszModule, pszMessage, output);
653 output += _T("\r\n");
655 CStringA outputA = output;
657 DWORD dwWritten;
658 VERIFY(WriteFile(m_hFile, (LPCSTR)outputA, (DWORD) outputA.GetLength() * sizeof(CHAR), &dwWritten, NULL));
659 if (m_bFlush)
660 VERIFY(FlushFileBuffers(m_hFile));
661 VERIFY(ReleaseMutex(m_hMutex));
664 //////////////////////////////////////////////////////////////////////////
665 // CDebugMedia
666 //////////////////////////////////////////////////////////////////////////
668 DebugMedia::DebugMedia(DWORD dwParam)
669 : m_dwParam(dwParam)
673 extern "C" WINBASEAPI BOOL WINAPI IsDebuggerPresent ( VOID );
675 bool DebugMedia::IsWorking() const
677 return (m_dwParam & LogBase::DEBUGMEDIA_FORCE) ? true : IsDebuggerPresent() != FALSE;
680 void DebugMedia::Write(ELogMessageType type,
681 ELogMessageLevel nLevel,
682 LPCTSTR pszDate,
683 LPCTSTR pszTime,
684 LPCTSTR pszThreadId,
685 LPCTSTR pszThreadName,
686 LPCTSTR pszModule,
687 LPCTSTR pszMessage)
689 CString output;
690 FormatLogMessage(type, nLevel, pszDate, pszTime, pszThreadId, pszThreadName, pszModule, pszMessage, output);
691 output += _T('\n');
693 OutputDebugString(output);
694 if (type == eLM_Error && (m_dwParam & LogBase::DEBUGMEDIA_REPORTERRORS))
696 _RPT1(_CRT_ERROR, "%ws", static_cast<LPCWSTR>(output));
700 //////////////////////////////////////////////////////////////////////////
701 // LogMediaProxy
702 //////////////////////////////////////////////////////////////////////////
704 LogMediaProxy::LogMediaProxy(const LogMediaPtr& pLog)
705 : m_pLog(pLog)
709 LogMediaProxy::~LogMediaProxy()
713 void LogMediaProxy::SetLog(const LogMediaPtr& pLog)
715 CriticalSection::SyncLock lock(m_cs);
716 m_pLog = pLog;
719 void LogMediaProxy::Write(ELogMessageType type, ELogMessageLevel nLevel, LPCTSTR pszDate, LPCTSTR pszTime, LPCTSTR pszThreadId, LPCTSTR pszThreadName, LPCTSTR pszModule, LPCTSTR pszMessage)
721 CriticalSection::SyncLock lock(m_cs);
722 if (m_pLog)
723 m_pLog->Write(type, nLevel, pszDate, pszTime, pszThreadId, pszThreadName, pszModule, pszMessage);
726 bool LogMediaProxy::Check(ELogMessageType type, ELogMessageLevel nLevel, LPCTSTR pszModule)
728 CriticalSection::SyncLock lock(m_cs);
729 if (m_pLog)
730 return m_pLog->Check(type, nLevel, pszModule);
731 return false;
734 //////////////////////////////////////////////////////////////////////////
735 // LogMediaColl
736 //////////////////////////////////////////////////////////////////////////
738 LogMediaColl::LogMediaColl()
742 LogMediaColl::~LogMediaColl()
746 void LogMediaColl::Add(const LogMediaPtr& pMedia)
748 if (!pMedia)
749 return;
750 CriticalSection::SyncLock lock(m_cs);
751 m_MediaColl.push_back(pMedia);
754 void LogMediaColl::Write(ELogMessageType type, ELogMessageLevel nLevel, LPCTSTR pszDate, LPCTSTR pszTime, LPCTSTR pszThreadId, LPCTSTR pszThreadName, LPCTSTR pszModule, LPCTSTR pszMessage)
756 CriticalSection::SyncLock lock(m_cs);
757 for (MediaColl::iterator it = m_MediaColl.begin(), end = m_MediaColl.end(); it != end; ++it)
758 (*it)->Write(type, nLevel, pszDate, pszTime, pszThreadId, pszThreadName, pszModule, pszMessage);
761 bool LogMediaColl::Check(ELogMessageType type, ELogMessageLevel nLevel, LPCTSTR pszModule)
763 CriticalSection::SyncLock lock(m_cs);
764 // Если хотя бы один лог пропускает, то и мы пропускаем.
765 for (MediaColl::iterator it = m_MediaColl.begin(), end = m_MediaColl.end(); it != end; ++it)
766 if ((*it)->Check(type, nLevel, pszModule))
767 return true;
768 return false;
771 //////////////////////////////////////////////////////////////////////////
772 // CFilterLogMedia
773 //////////////////////////////////////////////////////////////////////////
775 FilterLogMedia::FilterLogMedia(const LogMediaPtr& pMedia)
776 : m_pMedia(pMedia)
780 FilterLogMedia::~FilterLogMedia()
784 void FilterLogMedia::AddFilter(FilterPtr pFilter)
786 CriticalSection::SyncLock lock(m_cs);
787 m_FilterColl.push_back(pFilter);
790 void FilterLogMedia::Write(ELogMessageType type, ELogMessageLevel nLevel, LPCTSTR pszDate, LPCTSTR pszTime, LPCTSTR pszThreadId, LPCTSTR pszThreadName, LPCTSTR pszModule, LPCTSTR pszMessage)
792 if (!m_pMedia)
793 return;
795 CriticalSection::SyncLock lock(m_cs);
796 for (FilterColl::iterator it = m_FilterColl.begin(), end = m_FilterColl.end(); it != end; ++it)
797 if (!(*it)->Check(type, nLevel, pszModule))
798 return;
800 m_pMedia->Write(type, nLevel, pszDate, pszTime, pszThreadId, pszThreadName, pszModule, pszMessage);
803 bool FilterLogMedia::Check(ELogMessageType type, ELogMessageLevel nLevel, LPCTSTR pszModule)
805 if (!m_pMedia)
806 return false;
808 CriticalSection::SyncLock lock(m_cs);
809 for (FilterColl::iterator it = m_FilterColl.begin(), end = m_FilterColl.end(); it != end; ++it)
810 if (!(*it)->Check(type, nLevel, pszModule))
811 return false;
813 return m_pMedia->Check(type, nLevel, pszModule);
816 //////////////////////////////////////////////////////////////////////////
817 // LogBase
818 //////////////////////////////////////////////////////////////////////////
820 LogBase::LogBase(LogMediaPtr pMedia, LPCTSTR pszModule)
821 : m_szModule(pszModule)
822 , m_pMedia(pMedia)
824 ASSERT(pszModule != NULL);
827 LogBase::~LogBase()
831 template <typename T, size_t nSize>
832 class StackResizableBuf
834 T loc_data[nSize];
835 void Clear() { if (data != loc_data) delete[] data; }
836 public:
837 StackResizableBuf() { data = loc_data; size = nSize; }
838 ~StackResizableBuf() { Clear(); }
839 void Resize(size_t nNewSize) { Clear(); data = new T[nNewSize]; size = nNewSize; }
841 T* data;
842 size_t size;
845 bool LogBase::IsFiltered(ELogMessageType type, ELogMessageLevel nLevel)
847 return !m_pMedia || !m_pMedia->Check(type, nLevel, m_szModule.c_str());
850 void LogBase::WriteVA(ELogMessageType type,
851 ELogMessageLevel nLevel,
852 LPCTSTR pszModule,
853 LPCTSTR pszMessage,
854 va_list args) throw()
856 if (IsFiltered(type, nLevel)) // не обрабатываем сообщение, если оно не попадает в лог
857 return;
858 // 75% времени тратится на new и delete, поэтому первую попытку попробуем сделать без него.
859 StackResizableBuf<TCHAR, 16*1024> buf;
860 int pos;
861 for (;;)
863 pos = _vsntprintf_s(buf.data, buf.size, buf.size - 1, pszMessage, args);
864 if (pos != -1)
865 break;
866 // BUG 16456 FIX, см. в конец WriteVAW почему
867 //if (buf.size >= 1024 * 256)
868 if (buf.size >= 1024 * 1024 * 10) //Increased limit for DumpServer
870 pos = (int)buf.size - 1;
871 break;
873 buf.Resize(buf.size * 2);
875 if (pos >= 0)
877 buf.data[pos] = 0;
878 SYSTEMTIME st;
879 GetLocalTime(&st);
880 TCHAR bufdate[128], buftime[128], bufthread[128];
881 _stprintf_s(bufdate, _T("%i-%02i-%02i"), st.wYear, st.wMonth, st.wDay);
882 _stprintf_s(buftime, _T("%02i:%02i:%02i.%03d"), st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
883 _stprintf_s(bufthread, _T("%4x"), GetCurrentThreadId());
884 m_pMedia->Write(type, nLevel, bufdate, buftime, bufthread, GetThreadName()
885 , pszModule ? pszModule : m_szModule.c_str(), buf.data);
889 void LogBase::WriteVAW(ELogMessageType type,
890 ELogMessageLevel nLevel,
891 LPCWSTR pszModule,
892 LPCWSTR pszMessage,
893 va_list args) throw()
895 #if defined(_UNICODE) || defined(UNICODE)
896 WriteVA(type, nLevel, pszModule, pszMessage, args);
897 #else
899 if (IsFiltered(type, nLevel)) // не обрабатываем сообщение, если оно не попадает в лог
900 return;
901 // 75% времени тратится на new и delete, поэтому первую попытку попробуем сделать без него.
902 StackResizableBuf<WCHAR, 1024> buf;
903 int pos;
904 for (;;)
906 pos = _vsnwprintf(buf.data, buf.size - 1, pszMessage, args);
907 if (pos != -1)
908 break;
909 // BUG 16456 FIX, см. в конец WriteVAW почему
910 if (buf.size >= 1024 * 256)
912 pos = buf.size - 1;
913 break;
915 buf.Resize(buf.size * 2);
917 if (pos >= 0)
919 buf.data[pos] = 0;
920 LPTSTR pszStr = static_cast<LPTSTR>(_alloca(buf.size));
921 if (0 == WideCharToMultiByte(CP_ACP, 0/*WC_DEFAULTCHAR*/, buf.data, pos + 1, pszStr, buf.size, NULL, NULL))
923 _RPT1(_CRT_ERROR, "Can't convert Unicode string (error #0x%08X)", GetLastError());
924 return;
927 LPCTSTR pszMod;
928 if (pszModule == NULL)
929 pszMod = m_szModule.c_str();
930 else
932 size_t nModuleLen = wcslen(pszModule) + 1;
933 LPTSTR pszModBuf = static_cast<LPTSTR>(_alloca(nModuleLen));
934 if (0 == WideCharToMultiByte(CP_ACP, 0/*WC_DEFAULTCHAR*/, pszModule, nModuleLen, pszModBuf, nModuleLen, NULL, NULL))
936 _RPT1(_CRT_ERROR, "Can't convert Unicode string (error #0x%08X)", GetLastError());
937 return;
939 pszMod = pszModBuf;
942 SYSTEMTIME st;
943 GetLocalTime(&st);
944 TCHAR bufdate[128], buftime[128], bufthread[128];
945 _stprintf(bufdate, _T("%i-%02i-%02i"), st.wYear, st.wMonth, st.wDay);
946 _stprintf(buftime, _T("%02i:%02i:%02i.%03d"), st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
947 _stprintf(bufthread, _T("%03x"), GetCurrentThreadId());
948 m_pMedia->Write(type, nLevel, bufdate, buftime, bufthread, GetThreadName(), pszMod, pszStr);
950 #endif
951 /* BUG 16456 FIX
952 _vsnwprintf спотыкается на строках с символом 0xFFFF,
953 возвращает -1 при любой длине буфера. Соответсвтвенно
954 буфер увеличивается (Resize) пока не хватит памяти,
955 а потом вызывается _vsnwprintf на NULL.
956 Решение: Т.к. _vsnwprintf, даже если нехватает буфера, начало заполняет,
957 то просто ограничим буфер 256кб (а 0 на конце и так ставим на всякий случай)
959 #include <stdarg.h>
960 #include <stdio.h>
962 void WriteVAW(wchar_t* pszMessage, va_list args)
964 wchar_t buf[1000];
965 int n = _vsnwprintf(buf, 1000, pszMessage, args);
966 printf("Return %i\nBuf is: \"%ls\"", n, buf); // n будет -1, хотя буфера хватает !!!
968 void WriteW(wchar_t* pszMessage, ...)
970 va_list ap;
971 va_start(ap, pszMessage);
972 WriteVAW(pszMessage, ap);
973 va_end(ap);
975 void main()
977 WriteW(L"%ls!", L"123\xffff");
983 //////////////////////////////////////////////////////////////////////////
984 // CLocalLog
985 //////////////////////////////////////////////////////////////////////////
987 LocalLog::LocalLog(const LogParam& param, LPCTSTR pszModule)
988 : LogBase(param.m_pMedia, param.m_pszModule ? param.m_pszModule : pszModule)
992 LocalLog::LocalLog(LogMediaPtr pMedia, LPCTSTR pszModule)
993 : LogBase(pMedia, pszModule)
995 ASSERT(pszModule != NULL);
998 LocalLog::LocalLog(const LogBase& log, LPCTSTR pszModule)
999 : LogBase(log.GetMedia(), pszModule)
1003 LocalLog::~LocalLog()
1007 //////////////////////////////////////////////////////////////////////////
1008 // Log
1009 //////////////////////////////////////////////////////////////////////////
1011 Log::Log(const LogParam& param, LPCTSTR pszModule)
1012 : LogBase(param.m_pMedia, param.m_pszModule ? param.m_pszModule : pszModule)
1016 Log::Log(LogMediaPtr pMedia, LPCTSTR pszModule)
1017 : LogBase(pMedia, pszModule)
1021 Log::Log(const LogBase& log, LPCTSTR pszModule)
1022 : LogBase(log.GetMedia(), pszModule)
1026 Log::~Log()
1030 void Log::SetParams(LogMediaPtr pMedia, LPCTSTR pszModule)
1032 CriticalSection::SyncLock lock(m_cs);
1033 m_pMedia = pMedia;
1034 if (pszModule != NULL)
1035 m_szModule = pszModule;
1038 void Log::WriteVA(ELogMessageType type,
1039 ELogMessageLevel nLevel,
1040 LPCTSTR pszModule,
1041 LPCTSTR pszMessage,
1042 va_list args) throw()
1044 CriticalSection::SyncLock lock(m_cs);
1045 LogBase::WriteVA(type, nLevel, pszModule, pszMessage, args);
1048 void Log::WriteVAW(ELogMessageType type,
1049 ELogMessageLevel nLevel,
1050 LPCWSTR pszModule,
1051 LPCWSTR pszMessage,
1052 va_list args) throw()
1054 CriticalSection::SyncLock lock(m_cs);
1055 LogBase::WriteVAW(type, nLevel, pszModule, pszMessage, args);
1058 //////////////////////////////////////////////////////////////////////////
1059 // CFilterLog
1060 //////////////////////////////////////////////////////////////////////////
1062 FilterLog::~FilterLog()