Fixed issue #4126: Capitalize the first letter in the Push dialog
[TortoiseGit.git] / ext / CrashServer / CommonLibs / Log / log.cpp
blobd46190689de78a8ee8fb5e36906eb1db72a59f28
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 #ifndef WINAPI_FAMILY_PARTITION
28 #define WINAPI_FAMILY_PARTITION(Partition) 0
29 #endif
31 #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
32 #include <VersionHelpers.h>
33 #endif
35 #define ASSERT(f) assert(f)
37 #ifndef VERIFY
38 # ifdef _DEBUG
39 # define VERIFY(f) ASSERT(f)
40 # else
41 # define VERIFY(f) ((void)(f))
42 # endif
43 #endif
45 static DWORD g_dwThreadNameTlsId = TLS_OUT_OF_INDEXES;
46 static LPTSTR pszUnknownThreadName = _T("Unknown");
47 void InitializeLog()
49 static bool bInitialized = false;
50 if (bInitialized)
51 return;
52 bInitialized = true;
53 g_dwThreadNameTlsId = TlsAlloc();
54 ASSERT(g_dwThreadNameTlsId != TLS_OUT_OF_INDEXES);
56 LPCTSTR GetLogThreadName()
58 ASSERT(g_dwThreadNameTlsId != TLS_OUT_OF_INDEXES);
59 if (g_dwThreadNameTlsId == TLS_OUT_OF_INDEXES)
60 return NULL;
61 return static_cast<LPCTSTR>(TlsGetValue(g_dwThreadNameTlsId));
63 void FreeLogThreadName()
65 ASSERT(g_dwThreadNameTlsId != TLS_OUT_OF_INDEXES);
66 if (g_dwThreadNameTlsId == TLS_OUT_OF_INDEXES)
67 return;
68 LPTSTR pThreadName = static_cast<LPTSTR>(TlsGetValue(g_dwThreadNameTlsId));
69 if (pThreadName != pszUnknownThreadName)
71 free(pThreadName);
72 VERIFY(TlsSetValue(g_dwThreadNameTlsId, pszUnknownThreadName));
75 void SetLogThreadName(LPCTSTR pszThreadName)
77 ASSERT(g_dwThreadNameTlsId != TLS_OUT_OF_INDEXES);
78 if (g_dwThreadNameTlsId == TLS_OUT_OF_INDEXES)
79 return;
80 FreeLogThreadName();
81 VERIFY(TlsSetValue(g_dwThreadNameTlsId, LPVOID(pszThreadName != NULL ? _tcsdup(pszThreadName) : pszUnknownThreadName)));
82 // SetDebuggerThreadName(-1, __LPCSTR(pszThreadName));
85 //! Создаёт путь к файлу.
86 bool CreateFileDir(LPCTSTR pszFilePath)
88 // Найдём полный путь
89 TCHAR szBuffer[1024];
90 LPTSTR pszFilePart;
91 DWORD dwRes = GetFullPathName(pszFilePath, sizeof(szBuffer)/sizeof(*szBuffer), szBuffer, &pszFilePart);
92 if (dwRes == 0
93 || dwRes >= sizeof(szBuffer)/sizeof(*szBuffer)
94 || pszFilePart == NULL)
95 return false;
97 // Отрежем имя файла
98 *pszFilePart = _T('\0');
100 CString sPath(szBuffer);
101 sPath.Replace(_T('/'), _T('\\'));
102 ASSERT(sPath.Right(1) == _T("\\"));
104 int nPos;
105 if (sPath.Left(2) == _T("\\\\"))
106 nPos = sPath.Find(_T("\\"), sPath.Find(_T("\\"), 2) + 1) + 1; // Пропустим имя компьютера и шары
107 else
108 nPos = 4; // Пропустим имя диска
109 while ( (nPos = sPath.Find(_T('\\'), nPos + 1)) != -1)
111 if (!CreateDirectory(sPath.Left(nPos), NULL) && GetLastError() != ERROR_ALREADY_EXISTS)
112 return false;
114 return true;
117 void LogBase::SetThreadName(LPCTSTR pszThreadName)
119 SetLogThreadName(pszThreadName);
122 LPCTSTR LogBase::GetThreadName()
124 LPCTSTR pszName = GetLogThreadName();
125 return pszName ? pszName : _T("Unknown");
129 LogMediaPtr LogBase::GetAppLogMedia()
131 static LogMediaPtr pAppLog(new LogMediaProxy());
132 return pAppLog;
135 void LogBase::SetAppLogMedia(LogMediaPtr pLog)
137 ASSERT(pLog != GetAppLogMedia());
138 if (pLog == GetAppLogMedia())
139 return;
140 std::static_pointer_cast<LogMediaProxy>(GetAppLogMedia())->SetLog(pLog);
143 LogMediaPtr LogBase::CreateConsoleMedia()
145 static LogMediaPtr pConsoleMedia(new ConsoleMedia());
146 if (pConsoleMedia->IsWorking())
147 return pConsoleMedia;
148 return LogMediaPtr();
151 LogMediaPtr LogBase::CreateFileMedia(LPCTSTR pszFilename, bool bAppend, bool bFlush, bool bNewFileDaily)
153 LogMediaPtr pLog(new FileMedia(pszFilename, bAppend, bFlush, bNewFileDaily));
154 if (pLog->IsWorking())
155 return pLog;
156 return LogMediaPtr();
159 LogMediaPtr LogBase::CreateDebugMedia(DWORD dwParam)
161 LogMediaPtr pDebugMedia(new DebugMedia(dwParam));
162 if (pDebugMedia->IsWorking())
163 return pDebugMedia;
164 return LogMediaPtr();
168 //////////////////////////////////////////////////////////////////////////
170 //////////////////////////////////////////////////////////////////////////
172 void FormatLogMessage(ELogMessageType type,
173 ELogMessageLevel nLevel,
174 LPCTSTR pszDate,
175 LPCTSTR pszTime,
176 LPCTSTR pszThreadId,
177 LPCTSTR pszThreadName,
178 LPCTSTR pszModule,
179 LPCTSTR pszMessage,
180 CString& output)
182 #if 1
183 output.Empty();
184 output.Preallocate(1024);
186 if (type != eLM_DirectOutput)
188 output += pszDate;
189 output += _T(' ');
190 output += pszTime;
191 #if defined(LOG_THREAD_NAME)
192 output += _T(" [");
193 output += pszThreadId;
194 output += _T(":");
195 size_t nThreadNameLen = _tcslen(pszThreadName);
196 if (nThreadNameLen > 12)
197 output.append(pszThreadName, 12);
198 else
200 output.append(12 - nThreadNameLen, _T(' '));
201 output += pszThreadName;
203 output += _T("] ");
204 #else
205 output += _T(" ");
206 output += pszThreadId;
207 output += _T(" ");
208 #endif
210 switch (type)
212 case eLM_Info:
213 output += _T('I');
214 break;
215 case eLM_Debug:
216 output += _T('-');
217 break;
218 case eLM_Warning:
219 output += _T('W');
220 break;
221 case eLM_Error:
222 output += _T('E');
223 break;
224 default:
225 ASSERT(false);
228 if (nLevel > 0)
229 output.AppendFormat(_T("%i"), nLevel);
230 else
231 output += _T(' ');
232 #if 0
233 output += _T(" : [");
234 output += pszModule;
235 output += _T("] ");
236 #else
237 output += _T(" : ");
238 #endif
240 output += pszMessage;
241 output.TrimRight();
242 #else
243 output.Empty();
244 output.reserve(1024);
246 output += pszDate;
247 output += _T(' ');
248 output += pszTime;
249 output += _T("\t");
250 output += pszThreadId;
251 output += _T("\t");
252 output += pszThreadName;
253 output += _T("\t");
254 output += pszModule;
255 output += _T("\t");
257 switch (type)
259 case eLM_Info:
260 output += _T("Inf");
261 break;
262 case eLM_Debug:
263 output += _T("Dbg");
264 break;
265 case eLM_Warning:
266 output += _T("Wrn");
267 break;
268 case eLM_Error:
269 output += _T("Err");
270 break;
271 default:
272 ASSERT(false);
275 if (nLevel > 0)
276 output.AppendFormat(_T("%i"), nLevel);
277 output += _T('\t');
278 output += pszMessage;
279 output.TrimRight();
280 #endif
283 CString GetSystemInformation()
285 CString info = _T("Microsoft Windows ");
287 #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
288 if (IsWindows8Point1OrGreater())
289 info += _T(">= 8.1");
290 else if (IsWindows8OrGreater())
291 info += _T("8.1");
292 else if (IsWindows7SP1OrGreater())
293 info += _T("7 SP1");
294 else if (IsWindows7OrGreater())
295 info += _T("7");
296 else if (IsWindowsVistaSP2OrGreater())
297 info += _T("Vista SP2");
298 else if (IsWindowsVistaSP1OrGreater())
299 info += _T("Vista SP1");
300 else if (IsWindowsVistaOrGreater())
301 info += _T("Vista");
303 if (IsWindowsServer())
304 info += _T(" Server");
305 else
306 info += _T(" Client");
307 #else
308 OSVERSIONINFOEX rcOS;
309 ZeroMemory(&rcOS, sizeof(rcOS));
310 rcOS.dwOSVersionInfoSize = sizeof(rcOS);
312 GetVersionEx((OSVERSIONINFO*)&rcOS);
314 if (rcOS.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
316 switch (rcOS.dwMinorVersion)
318 case 0: info += _T("95"); break;
319 case 10: info += _T("98"); break;
320 case 90: info += _T("Me"); break;
322 if (!_tcscmp(rcOS.szCSDVersion, _T(" A")))
323 info += _T(" Second Edition");
324 else
325 if (!_tcscmp(rcOS.szCSDVersion, _T(" C")))
326 info += _T(" OSR2");
328 else if (rcOS.dwPlatformId == VER_PLATFORM_WIN32_NT)
330 switch (rcOS.dwMajorVersion)
332 case 3: info += _T("NT 3.51"); break;
333 case 4: info += _T("NT 4.0"); break;
334 case 5:
335 switch (rcOS.dwMinorVersion)
337 case 0: info += _T("2000"); break;
338 case 1: info += _T("XP"); break;
339 case 2: info += _T("2003 Server"); break;
340 default: ASSERT(false); info += _T("future version"); break;
342 break;
343 case 6:
344 switch (rcOS.dwMinorVersion)
346 case 0: info += rcOS.wProductType == VER_NT_WORKSTATION ? _T("Vista") : _T("Server 2008"); break;
347 case 1: info += rcOS.wProductType == VER_NT_WORKSTATION ? _T("7") : _T("Server 2008 R2"); break;
348 case 2: info += rcOS.wProductType == VER_NT_WORKSTATION ? _T("8") : _T("Server 2012"); break;
349 case 3: info += rcOS.wProductType == VER_NT_WORKSTATION ? _T("8.1") : _T("Server 2012 R2"); break;
350 default:
352 CString future;
353 future.Format(_T("%ld.%ld%s"), rcOS.dwMajorVersion, rcOS.dwMinorVersion, VER_NT_WORKSTATION ? _T("") : _T(" Server"));
354 info += future;
355 break;
358 break;
359 default:
361 CString future;
362 future.Format(_T("%ld.%ld%s"), rcOS.dwMajorVersion, rcOS.dwMinorVersion, VER_NT_WORKSTATION ? _T("") : _T(" Server"));
363 info += future;
364 break;
367 if (_tcslen(rcOS.szCSDVersion) > 0)
369 info += _T(", ");
370 info += rcOS.szCSDVersion;
373 #endif
374 return info;
377 CString GetModuleInformation()
379 CString info;
380 TCHAR szApp[MAX_PATH];
381 if (!GetModuleFileName(NULL, szApp, _countof(szApp)))
382 return info;
384 info = szApp;
386 DWORD dwDummy;
387 DWORD dwInfoSize = GetFileVersionInfoSize(szApp, &dwDummy);
388 if (dwInfoSize)
390 LPVOID pInfoBuffer = _alloca(dwInfoSize);
391 if (GetFileVersionInfo(szApp, 0, dwInfoSize, pInfoBuffer))
393 VS_FIXEDFILEINFO *pInfo = NULL;
394 UINT nInfoSize = 0;
395 if (VerQueryValue (pInfoBuffer, _T("\\"), (LPVOID *)&pInfo, &nInfoSize) && nInfoSize == sizeof(VS_FIXEDFILEINFO))
397 info.AppendFormat(_T(" (%d.%d.%d.%d)"),
398 HIWORD(pInfo->dwFileVersionMS),
399 LOWORD(pInfo->dwFileVersionMS),
400 HIWORD(pInfo->dwFileVersionLS),
401 LOWORD(pInfo->dwFileVersionLS)
407 info.AppendFormat(_T(" PID: %d (0x%x)"), GetCurrentProcessId(), GetCurrentProcessId());
409 return info;
412 //////////////////////////////////////////////////////////////////////////
413 // CConsoleMedia
414 //////////////////////////////////////////////////////////////////////////
416 ConsoleMedia::ConsoleMedia()
418 m_bRedirectedToFile = true;
420 m_hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
421 if (m_hConsole == NULL)
423 if (!AllocConsole() //|| !SetConsoleTitle(_T("Log"))
424 || (m_hConsole = GetStdHandle(STD_OUTPUT_HANDLE)) == NULL)
425 return;
427 GetConsoleScreenBufferInfo(m_hConsole, &m_info);
429 DWORD t;
430 m_bRedirectedToFile = GetConsoleMode(m_hConsole, &t) == FALSE;
433 void ConsoleMedia::Write(ELogMessageType type,
434 ELogMessageLevel nLevel,
435 LPCTSTR pszDate,
436 LPCTSTR pszTime,
437 LPCTSTR pszThreadId,
438 LPCTSTR pszThreadName,
439 LPCTSTR pszModule,
440 LPCTSTR pszMessage)
442 if (m_hConsole==NULL)
443 return;
444 WORD color = FOREGROUND_GREEN|FOREGROUND_INTENSITY;
445 //FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_INTENSITY|BACKGROUND_RED|BACKGROUND_GREEN|BACKGROUND_BLUE|BACKGROUND_INTENSITY;
446 switch (type)
448 case eLM_Debug:
449 color = FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE;
450 break;
451 case eLM_Warning:
452 color = FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_INTENSITY;
453 break;
454 case eLM_Error:
455 color = FOREGROUND_RED|FOREGROUND_INTENSITY;
456 break;
459 CString output;
460 FormatLogMessage(type, nLevel, pszDate, pszTime, pszThreadId, pszThreadName, pszModule, pszMessage, output);
462 if (m_bRedirectedToFile)
464 output += _T("\n");
465 CStringA outputA = output;
466 outputA.AnsiToOem();
468 CriticalSection::SyncLock lock(m_cs);
469 DWORD dwWritten;
470 WriteFile(m_hConsole, (LPCSTR)outputA, (DWORD)outputA.GetLength(), &dwWritten, NULL);
472 else
474 #ifndef _UNICODE
475 output.AnsiToOem();
476 #endif
477 CriticalSection::SyncLock lock(m_cs);
478 SetConsoleTextAttribute(m_hConsole, color);
479 DWORD dwWritten;
480 WriteConsole(m_hConsole, (LPCTSTR)output, (DWORD)output.GetLength(), &dwWritten, NULL);
482 SetConsoleTextAttribute(m_hConsole, m_info.wAttributes);
483 WriteConsole(m_hConsole, _T("\n"), 1, &dwWritten, NULL);
487 //////////////////////////////////////////////////////////////////////////
488 // CFileMedia
489 //////////////////////////////////////////////////////////////////////////
491 FileMedia::FileMedia(LPCTSTR pszFilename, bool bAppend, bool bFlush, bool bNewFileDaily)
492 : m_bAppend(bAppend), m_bFlush(bFlush), m_bNewFileDaily(bNewFileDaily), m_wLogYear(0), m_wLogMonth(0), m_wLogDay(0),
493 m_sOrigFilename(pszFilename)
495 OpenLogFile();
498 FileMedia::~FileMedia()
500 CloseLogFile();
503 void FileMedia::CloseLogFile()
505 if (m_hFile != INVALID_HANDLE_VALUE && WaitForSingleObject(m_hMutex, 1000) != WAIT_TIMEOUT)
507 SYSTEMTIME st;
508 GetLocalTime(&st);
509 CString header;
510 header.Format(
511 _T("=================================================\r\n")
512 _T("=== Trace Log Finished on %i-%02i-%02i %02i:%02i:%02i ===\r\n")
513 _T("=================================================\r\n")
514 , st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
516 LONG dwDistanceToMoveHigh = 0; // Нужен, чтобы писать в файлы больше 4Гб
517 VERIFY(SetFilePointer(m_hFile, 0, &dwDistanceToMoveHigh, FILE_END) != INVALID_SET_FILE_POINTER || GetLastError() == NO_ERROR);
518 CStringA headerA = header;
519 DWORD dwWritten;
520 // VERIFY(WriteFile(m_hFile, (LPCTSTR)header, (DWORD) header.size() * sizeof(TCHAR), &dwWritten, NULL));
521 VERIFY(WriteFile(m_hFile, (LPCSTR)headerA, (DWORD) headerA.GetLength() * sizeof(CHAR), &dwWritten, NULL));
522 VERIFY(ReleaseMutex(m_hMutex));
526 bool FileMedia::IsWorking() const
528 return m_hFile != INVALID_HANDLE_VALUE;
531 void FileMedia::OpenLogFile()
533 SYSTEMTIME st;
534 GetLocalTime(&st);
536 m_sFilename = m_sOrigFilename;
537 m_sFilename.Replace(_T("%DATETIME%"), _T("%DATE% %TIME%"));
538 if (m_sFilename.Find(_T("%DATE%")) != -1)
540 TCHAR bufdate[128];
541 _stprintf_s(bufdate, _T("%i-%02i-%02i"), st.wYear, st.wMonth, st.wDay);
542 m_sFilename.Replace(_T("%DATE%"), bufdate);
543 m_wLogYear = st.wYear;
544 m_wLogMonth = st.wMonth;
545 m_wLogDay = st.wDay;
547 if (m_sFilename.Find(_T("%TIME%")) != -1)
549 GetLocalTime(&st);
550 TCHAR buftime[128];
551 _stprintf_s(buftime, _T("%02i-%02i"), st.wHour, st.wMinute);
552 m_sFilename.Replace(_T("%TIME%"), buftime);
555 if (!CreateFileDir(m_sFilename))
557 _RPT1(_CRT_ERROR, "FileMedia: Can't create folder '%S'", (LPCWSTR) CStringW(m_sFilename));
558 return;
562 // Создадим для доступа к этому файлу мьютекс
563 CString sMtx(m_sFilename);
564 sMtx.MakeUpper();
565 for (int i = 0, size = sMtx.GetLength(); i < size; ++i)
567 if (!_istalnum(sMtx[i]))
568 sMtx.SetAt(i, _T('_'));
570 m_hMutex = CreateMutex(NULL, TRUE, (LPCTSTR)sMtx);
571 DWORD dwMtxError = GetLastError();
573 m_hFile = CreateFile(m_sFilename,
574 GENERIC_WRITE,
575 FILE_SHARE_READ | FILE_SHARE_WRITE,
576 NULL,
577 (m_bAppend || dwMtxError == ERROR_ALREADY_EXISTS) ? OPEN_ALWAYS : CREATE_ALWAYS,
578 FILE_ATTRIBUTE_NORMAL,
579 NULL);
581 if (m_hFile == INVALID_HANDLE_VALUE)
583 if (dwMtxError != ERROR_ALREADY_EXISTS)
584 VERIFY(ReleaseMutex(m_hMutex));
585 m_hMutex.Close();
586 return;
589 if (dwMtxError != ERROR_ALREADY_EXISTS)
591 CString header;
592 header.Format(
593 _T("================================================\r\n")
594 _T("=== Trace Log Started on %i-%02i-%02i %02i:%02i:%02i ===\r\n")
595 _T("=== %s ===\r\n")
596 _T("================================================\r\n")
597 _T("\r\n%s\r\n\r\n")
598 , st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond
599 , (LPCTSTR)GetSystemInformation()
600 , (LPCTSTR)GetModuleInformation());
602 LONG dwDistanceToMoveHigh = 0; // Нужен, чтобы писать в файлы больше 4Гб
603 VERIFY(SetFilePointer(m_hFile, 0, &dwDistanceToMoveHigh, FILE_END) != INVALID_SET_FILE_POINTER || GetLastError() == NO_ERROR);
604 CStringA headerA = header;
605 DWORD dwWritten;
606 VERIFY(WriteFile(m_hFile, (LPCSTR)headerA, (DWORD)headerA.GetLength() * sizeof(CHAR), &dwWritten, NULL));
607 if (m_hMutex != NULL)
608 VERIFY(ReleaseMutex(m_hMutex));
612 void FileMedia::Write(ELogMessageType type, ELogMessageLevel nLevel, LPCTSTR pszDate, LPCTSTR pszTime, LPCTSTR pszThreadId, LPCTSTR pszThreadName, LPCTSTR pszModule, LPCTSTR pszMessage)
614 if (m_bNewFileDaily && m_wLogYear != 0)
616 SYSTEMTIME st;
617 GetLocalTime(&st);
618 if (st.wDay != m_wLogDay || st.wMonth != m_wLogMonth || st.wDay != m_wLogDay)
620 CloseLogFile();
621 OpenLogFile();
625 if (WaitForSingleObject(m_hMutex, 1000) == WAIT_TIMEOUT)
626 return;
628 // trying to restore access
629 if (m_hFile == INVALID_HANDLE_VALUE)
631 m_hFile = CreateFile(m_sFilename,
632 GENERIC_WRITE,
633 FILE_SHARE_READ | FILE_SHARE_WRITE,
634 NULL,
635 OPEN_ALWAYS,
636 FILE_ATTRIBUTE_NORMAL,
637 NULL);
638 if (m_hFile == INVALID_HANDLE_VALUE)
640 VERIFY(ReleaseMutex(m_hMutex));
641 return;
645 LONG dwDistanceToMoveHigh = 0; // Нужен, чтобы писать в файлы больше 4Гб
646 if (SetFilePointer(m_hFile, 0, &dwDistanceToMoveHigh, FILE_END) == INVALID_SET_FILE_POINTER
647 && GetLastError() != NO_ERROR)
649 _RPT0(_CRT_ERROR, "FileMedia: Can't set file pointer");
650 m_hFile.Close();
651 VERIFY(ReleaseMutex(m_hMutex));
652 return;
655 CString output;
656 FormatLogMessage(type, nLevel, pszDate, pszTime, pszThreadId, pszThreadName, pszModule, pszMessage, output);
657 output += _T("\r\n");
659 CStringA outputA = output;
661 DWORD dwWritten;
662 VERIFY(WriteFile(m_hFile, (LPCSTR)outputA, (DWORD) outputA.GetLength() * sizeof(CHAR), &dwWritten, NULL));
663 if (m_bFlush)
664 VERIFY(FlushFileBuffers(m_hFile));
665 VERIFY(ReleaseMutex(m_hMutex));
668 //////////////////////////////////////////////////////////////////////////
669 // CDebugMedia
670 //////////////////////////////////////////////////////////////////////////
672 DebugMedia::DebugMedia(DWORD dwParam)
673 : m_dwParam(dwParam)
677 extern "C" WINBASEAPI BOOL WINAPI IsDebuggerPresent ( VOID );
679 bool DebugMedia::IsWorking() const
681 return (m_dwParam & LogBase::DEBUGMEDIA_FORCE) ? true : IsDebuggerPresent() != FALSE;
684 void DebugMedia::Write(ELogMessageType type,
685 ELogMessageLevel nLevel,
686 LPCTSTR pszDate,
687 LPCTSTR pszTime,
688 LPCTSTR pszThreadId,
689 LPCTSTR pszThreadName,
690 LPCTSTR pszModule,
691 LPCTSTR pszMessage)
693 CString output;
694 FormatLogMessage(type, nLevel, pszDate, pszTime, pszThreadId, pszThreadName, pszModule, pszMessage, output);
695 output += _T('\n');
697 OutputDebugString(output);
698 if (type == eLM_Error && (m_dwParam & LogBase::DEBUGMEDIA_REPORTERRORS))
700 _RPT1(_CRT_ERROR, "%ws", static_cast<LPCWSTR>(output));
704 //////////////////////////////////////////////////////////////////////////
705 // LogMediaProxy
706 //////////////////////////////////////////////////////////////////////////
708 LogMediaProxy::LogMediaProxy(const LogMediaPtr& pLog)
709 : m_pLog(pLog)
713 LogMediaProxy::~LogMediaProxy()
717 void LogMediaProxy::SetLog(const LogMediaPtr& pLog)
719 CriticalSection::SyncLock lock(m_cs);
720 m_pLog = pLog;
723 void LogMediaProxy::Write(ELogMessageType type, ELogMessageLevel nLevel, LPCTSTR pszDate, LPCTSTR pszTime, LPCTSTR pszThreadId, LPCTSTR pszThreadName, LPCTSTR pszModule, LPCTSTR pszMessage)
725 CriticalSection::SyncLock lock(m_cs);
726 if (m_pLog)
727 m_pLog->Write(type, nLevel, pszDate, pszTime, pszThreadId, pszThreadName, pszModule, pszMessage);
730 bool LogMediaProxy::Check(ELogMessageType type, ELogMessageLevel nLevel, LPCTSTR pszModule)
732 CriticalSection::SyncLock lock(m_cs);
733 if (m_pLog)
734 return m_pLog->Check(type, nLevel, pszModule);
735 return false;
738 //////////////////////////////////////////////////////////////////////////
739 // LogMediaColl
740 //////////////////////////////////////////////////////////////////////////
742 LogMediaColl::LogMediaColl()
746 LogMediaColl::~LogMediaColl()
750 void LogMediaColl::Add(const LogMediaPtr& pMedia)
752 if (!pMedia)
753 return;
754 CriticalSection::SyncLock lock(m_cs);
755 m_MediaColl.push_back(pMedia);
758 void LogMediaColl::Write(ELogMessageType type, ELogMessageLevel nLevel, LPCTSTR pszDate, LPCTSTR pszTime, LPCTSTR pszThreadId, LPCTSTR pszThreadName, LPCTSTR pszModule, LPCTSTR pszMessage)
760 CriticalSection::SyncLock lock(m_cs);
761 for (MediaColl::iterator it = m_MediaColl.begin(), end = m_MediaColl.end(); it != end; ++it)
762 (*it)->Write(type, nLevel, pszDate, pszTime, pszThreadId, pszThreadName, pszModule, pszMessage);
765 bool LogMediaColl::Check(ELogMessageType type, ELogMessageLevel nLevel, LPCTSTR pszModule)
767 CriticalSection::SyncLock lock(m_cs);
768 // Если хотя бы один лог пропускает, то и мы пропускаем.
769 for (MediaColl::iterator it = m_MediaColl.begin(), end = m_MediaColl.end(); it != end; ++it)
770 if ((*it)->Check(type, nLevel, pszModule))
771 return true;
772 return false;
775 //////////////////////////////////////////////////////////////////////////
776 // CFilterLogMedia
777 //////////////////////////////////////////////////////////////////////////
779 FilterLogMedia::FilterLogMedia(const LogMediaPtr& pMedia)
780 : m_pMedia(pMedia)
784 FilterLogMedia::~FilterLogMedia()
788 void FilterLogMedia::AddFilter(FilterPtr pFilter)
790 CriticalSection::SyncLock lock(m_cs);
791 m_FilterColl.push_back(pFilter);
794 void FilterLogMedia::Write(ELogMessageType type, ELogMessageLevel nLevel, LPCTSTR pszDate, LPCTSTR pszTime, LPCTSTR pszThreadId, LPCTSTR pszThreadName, LPCTSTR pszModule, LPCTSTR pszMessage)
796 if (!m_pMedia)
797 return;
799 CriticalSection::SyncLock lock(m_cs);
800 for (FilterColl::iterator it = m_FilterColl.begin(), end = m_FilterColl.end(); it != end; ++it)
801 if (!(*it)->Check(type, nLevel, pszModule))
802 return;
804 m_pMedia->Write(type, nLevel, pszDate, pszTime, pszThreadId, pszThreadName, pszModule, pszMessage);
807 bool FilterLogMedia::Check(ELogMessageType type, ELogMessageLevel nLevel, LPCTSTR pszModule)
809 if (!m_pMedia)
810 return false;
812 CriticalSection::SyncLock lock(m_cs);
813 for (FilterColl::iterator it = m_FilterColl.begin(), end = m_FilterColl.end(); it != end; ++it)
814 if (!(*it)->Check(type, nLevel, pszModule))
815 return false;
817 return m_pMedia->Check(type, nLevel, pszModule);
820 //////////////////////////////////////////////////////////////////////////
821 // LogBase
822 //////////////////////////////////////////////////////////////////////////
824 LogBase::LogBase(LogMediaPtr pMedia, LPCTSTR pszModule)
825 : m_szModule(pszModule)
826 , m_pMedia(pMedia)
828 ASSERT(pszModule != NULL);
831 LogBase::~LogBase()
835 template <typename T, size_t nSize>
836 class StackResizableBuf
838 T loc_data[nSize];
839 void Clear() { if (data != loc_data) delete[] data; }
840 public:
841 StackResizableBuf() { data = loc_data; size = nSize; }
842 ~StackResizableBuf() { Clear(); }
843 void Resize(size_t nNewSize) { Clear(); data = new T[nNewSize]; size = nNewSize; }
845 T* data;
846 size_t size;
849 bool LogBase::IsFiltered(ELogMessageType type, ELogMessageLevel nLevel)
851 return !m_pMedia || !m_pMedia->Check(type, nLevel, m_szModule.c_str());
854 void LogBase::WriteVA(ELogMessageType type,
855 ELogMessageLevel nLevel,
856 LPCTSTR pszModule,
857 LPCTSTR pszMessage,
858 va_list args) throw()
860 if (IsFiltered(type, nLevel)) // не обрабатываем сообщение, если оно не попадает в лог
861 return;
862 // 75% времени тратится на new и delete, поэтому первую попытку попробуем сделать без него.
863 StackResizableBuf<TCHAR, 16*1024> buf;
864 int pos;
865 for (;;)
867 pos = _vsntprintf_s(buf.data, buf.size, buf.size - 1, pszMessage, args);
868 if (pos != -1)
869 break;
870 // BUG 16456 FIX, см. в конец WriteVAW почему
871 //if (buf.size >= 1024 * 256)
872 if (buf.size >= 1024 * 1024 * 10) //Increased limit for DumpServer
874 pos = (int)buf.size - 1;
875 break;
877 buf.Resize(buf.size * 2);
879 if (pos >= 0)
881 buf.data[pos] = 0;
882 SYSTEMTIME st;
883 GetLocalTime(&st);
884 TCHAR bufdate[128], buftime[128], bufthread[128];
885 _stprintf_s(bufdate, _T("%i-%02i-%02i"), st.wYear, st.wMonth, st.wDay);
886 _stprintf_s(buftime, _T("%02i:%02i:%02i.%03d"), st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
887 _stprintf_s(bufthread, _T("%4x"), GetCurrentThreadId());
888 m_pMedia->Write(type, nLevel, bufdate, buftime, bufthread, GetThreadName()
889 , pszModule ? pszModule : m_szModule.c_str(), buf.data);
893 void LogBase::WriteVAW(ELogMessageType type,
894 ELogMessageLevel nLevel,
895 LPCWSTR pszModule,
896 LPCWSTR pszMessage,
897 va_list args) throw()
899 #if defined(_UNICODE) || defined(UNICODE)
900 WriteVA(type, nLevel, pszModule, pszMessage, args);
901 #else
903 if (IsFiltered(type, nLevel)) // не обрабатываем сообщение, если оно не попадает в лог
904 return;
905 // 75% времени тратится на new и delete, поэтому первую попытку попробуем сделать без него.
906 StackResizableBuf<WCHAR, 1024> buf;
907 int pos;
908 for (;;)
910 pos = _vsnwprintf(buf.data, buf.size - 1, pszMessage, args);
911 if (pos != -1)
912 break;
913 // BUG 16456 FIX, см. в конец WriteVAW почему
914 if (buf.size >= 1024 * 256)
916 pos = buf.size - 1;
917 break;
919 buf.Resize(buf.size * 2);
921 if (pos >= 0)
923 buf.data[pos] = 0;
924 LPTSTR pszStr = static_cast<LPTSTR>(_alloca(buf.size));
925 if (0 == WideCharToMultiByte(CP_ACP, 0/*WC_DEFAULTCHAR*/, buf.data, pos + 1, pszStr, buf.size, NULL, NULL))
927 _RPT1(_CRT_ERROR, "Can't convert Unicode string (error #0x%08X)", GetLastError());
928 return;
931 LPCTSTR pszMod;
932 if (pszModule == NULL)
933 pszMod = m_szModule.c_str();
934 else
936 size_t nModuleLen = wcslen(pszModule) + 1;
937 LPTSTR pszModBuf = static_cast<LPTSTR>(_alloca(nModuleLen));
938 if (0 == WideCharToMultiByte(CP_ACP, 0/*WC_DEFAULTCHAR*/, pszModule, nModuleLen, pszModBuf, nModuleLen, NULL, NULL))
940 _RPT1(_CRT_ERROR, "Can't convert Unicode string (error #0x%08X)", GetLastError());
941 return;
943 pszMod = pszModBuf;
946 SYSTEMTIME st;
947 GetLocalTime(&st);
948 TCHAR bufdate[128], buftime[128], bufthread[128];
949 _stprintf(bufdate, _T("%i-%02i-%02i"), st.wYear, st.wMonth, st.wDay);
950 _stprintf(buftime, _T("%02i:%02i:%02i.%03d"), st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
951 _stprintf(bufthread, _T("%03x"), GetCurrentThreadId());
952 m_pMedia->Write(type, nLevel, bufdate, buftime, bufthread, GetThreadName(), pszMod, pszStr);
954 #endif
955 /* BUG 16456 FIX
956 _vsnwprintf спотыкается на строках с символом 0xFFFF,
957 возвращает -1 при любой длине буфера. Соответсвтвенно
958 буфер увеличивается (Resize) пока не хватит памяти,
959 а потом вызывается _vsnwprintf на NULL.
960 Решение: Т.к. _vsnwprintf, даже если нехватает буфера, начало заполняет,
961 то просто ограничим буфер 256кб (а 0 на конце и так ставим на всякий случай)
963 #include <stdarg.h>
964 #include <stdio.h>
966 void WriteVAW(wchar_t* pszMessage, va_list args)
968 wchar_t buf[1000];
969 int n = _vsnwprintf(buf, 1000, pszMessage, args);
970 printf("Return %i\nBuf is: \"%ls\"", n, buf); // n будет -1, хотя буфера хватает !!!
972 void WriteW(wchar_t* pszMessage, ...)
974 va_list ap;
975 va_start(ap, pszMessage);
976 WriteVAW(pszMessage, ap);
977 va_end(ap);
979 void main()
981 WriteW(L"%ls!", L"123\xffff");
987 //////////////////////////////////////////////////////////////////////////
988 // CLocalLog
989 //////////////////////////////////////////////////////////////////////////
991 LocalLog::LocalLog(const LogParam& param, LPCTSTR pszModule)
992 : LogBase(param.m_pMedia, param.m_pszModule ? param.m_pszModule : pszModule)
996 LocalLog::LocalLog(LogMediaPtr pMedia, LPCTSTR pszModule)
997 : LogBase(pMedia, pszModule)
999 ASSERT(pszModule != NULL);
1002 LocalLog::LocalLog(const LogBase& log, LPCTSTR pszModule)
1003 : LogBase(log.GetMedia(), pszModule)
1007 LocalLog::~LocalLog()
1011 //////////////////////////////////////////////////////////////////////////
1012 // Log
1013 //////////////////////////////////////////////////////////////////////////
1015 Log::Log(const LogParam& param, LPCTSTR pszModule)
1016 : LogBase(param.m_pMedia, param.m_pszModule ? param.m_pszModule : pszModule)
1020 Log::Log(LogMediaPtr pMedia, LPCTSTR pszModule)
1021 : LogBase(pMedia, pszModule)
1025 Log::Log(const LogBase& log, LPCTSTR pszModule)
1026 : LogBase(log.GetMedia(), pszModule)
1030 Log::~Log()
1034 void Log::SetParams(LogMediaPtr pMedia, LPCTSTR pszModule)
1036 CriticalSection::SyncLock lock(m_cs);
1037 m_pMedia = pMedia;
1038 if (pszModule != NULL)
1039 m_szModule = pszModule;
1042 void Log::WriteVA(ELogMessageType type,
1043 ELogMessageLevel nLevel,
1044 LPCTSTR pszModule,
1045 LPCTSTR pszMessage,
1046 va_list args) throw()
1048 CriticalSection::SyncLock lock(m_cs);
1049 LogBase::WriteVA(type, nLevel, pszModule, pszMessage, args);
1052 void Log::WriteVAW(ELogMessageType type,
1053 ELogMessageLevel nLevel,
1054 LPCWSTR pszModule,
1055 LPCWSTR pszMessage,
1056 va_list args) throw()
1058 CriticalSection::SyncLock lock(m_cs);
1059 LogBase::WriteVAW(type, nLevel, pszModule, pszMessage, args);
1062 //////////////////////////////////////////////////////////////////////////
1063 // CFilterLog
1064 //////////////////////////////////////////////////////////////////////////
1066 FilterLog::~FilterLog()