// Log.h: interface for the CLog class. // ////////////////////////////////////////////////////////////////////// #pragma once #include <assert.h> #include <fstream> #include <string> #include <mutex> namespace jlib { #define MAX_OUTPUT_LEN (1024 * 64) #define MAX_FILE_LEN (1024 * 1024 * 10) #define IMPLEMENT_CLASS_LOG_STATIC_MEMBER \ CRITICAL_SECTION CLog::m_cs;\ CLock CLog::m_lockForSingleton;\ char CLog::m_ansiBuf[MAX_OUTPUT_LEN];\ wchar_t CLog::m_utf16Buf[MAX_OUTPUT_LEN];\ CLog* CLog::m_pInstance = NULL;\ TCHAR CLog::g_szFileName[1024] = { 0 }; class CLog { private: #pragma region private members double exe_time; bool m_bOutputLogFile; bool m_bOutputDbgView; bool m_bOutputConsole; bool m_bConsoleOpened; bool m_bDbgviewOpened; bool m_bLogFileOpened; bool m_bRunning; FILE *m_pLogFile; static std::wstring _log_file_path; static std::mutex _lock; static std::mutex _lock_for_singleton; static char m_ansiBuf[MAX_OUTPUT_LEN]; static wchar_t m_utf16Buf[MAX_OUTPUT_LEN]; static CLog* m_pInstance; #pragma endregion public: #pragma region public functions static std::wstring GetLogFilePath() { return _log_file_path; } static CLog* GetInstance() { std::lock_guard<std::mutex> lg(_lock_for_singleton); if (CLog::m_pInstance == NULL) { static CLog log; CLog::m_pInstance = &log; } return CLog::m_pInstance; } ~CLog() { if (m_bConsoleOpened) OpenConsole(false); if (m_bDbgviewOpened) OpenDbgview(false); if (m_bLogFileOpened) CloseLogFile(); } static void SetOutputLogFilePath(const std::wstring& logFilePath) { _log_file_path = _log_file_path; } static void SetOutputConsole(bool b = true) { CLog* plog = CLog::GetInstance(); plog->m_bOutputConsole = b; plog->OpenConsole(b); } static void SetOutputDbgView(bool b = true) { CLog* plog = CLog::GetInstance(); plog->m_bOutputDbgView = b; } static void SetOutputLogFile(bool b = true) { CLog* plog = CLog::GetInstance(); plog->m_bOutputLogFile = b; if (plog->m_bOutputLogFile && !_log_file_path.empty()) plog->CreateFileName(); } static void Dump(const char* buff, size_t buff_len) { try { CLog* plog = CLog::GetInstance(); if (plog == NULL) return; std::lock_guard<std::mutex> lg(_lock); if (plog->m_bOutputLogFile || plog->m_bOutputDbgView || plog->m_bOutputConsole) { size_t output_len = buff_len * 6 + 64; wchar_t* output = new wchar_t[output_len]; output[0] = 0; wchar_t c[64] = { 0 }; swprintf_s(c, L"len %d\n", buff_len); wcscat_s(output, output_len, c); for (size_t i = 0; i < buff_len; i++) { swprintf_s(c, L"%02X ", static_cast<unsigned char>(buff[i])); wcscat_s(output, output_len, c); if (i > 0 && (i + 1) % 16 == 0) { wcscat_s(output, output_len, L"\n"); } } wcscat_s(output, output_len, L"\n"); plog->Output(output); delete[] output; } } catch (...) { assert(0); } } static void DumpAsc(const char* buff, size_t buff_len) { try { CLog* plog = CLog::GetInstance(); if (plog == NULL) return; std::lock_guard<std::mutex> lg(_lock); if (plog->m_bOutputLogFile || plog->m_bOutputDbgView || plog->m_bOutputConsole) { size_t output_len = buff_len * 6 + 64; wchar_t* output = new wchar_t[output_len]; output[0] = 0; wchar_t c[64] = { 0 }; swprintf_s(c, L"len %d\n", buff_len); wcscat_s(output, output_len, c); for (size_t i = 0; i < buff_len; i++) { swprintf_s(c, L"%C ", static_cast<unsigned char>(buff[i])); wcscat_s(output, output_len, c); if (i > 0 && (i + 1) % 16 == 0) { wcscat_s(output, output_len, L"\n"); } } wcscat_s(output, output_len, L"\n"); plog->Output(output); delete[] output; } } catch (...) { assert(0); } } static void WriteLog(const wchar_t* format, ...) { try { CLog* plog = CLog::GetInstance(); if (plog == NULL) return; std::lock_guard<std::mutex> lg(_lock); if (plog->m_bOutputLogFile || plog->m_bOutputDbgView || plog->m_bOutputConsole) { static wchar_t buf[MAX_OUTPUT_LEN], output[MAX_OUTPUT_LEN], *p; p = buf; va_list args; va_start(args, format); p += _vsntprintf_s(p, sizeof(buf) - 1, sizeof(buf) - 1, format, args); va_end(args); while ((p > buf) && _istspace(*(p - 1))) *--p = _T('\0'); *p++ = _T('\r'); *p++ = _T('\n'); *p = _T('\0'); plog->FormatBuf(buf, output); plog->Output(output); } } catch (...) { assert(0); } } static void WriteLogW(const wchar_t* format, ...) { try { CLog* plog = CLog::GetInstance(); if (plog == NULL) return; std::lock_guard<std::mutex> lg(_lock); if (plog->m_bOutputLogFile || plog->m_bOutputDbgView || plog->m_bOutputConsole) { static wchar_t buf[MAX_OUTPUT_LEN], *p; p = buf; va_list args; va_start(args, format); p += _vsnwprintf_s(p, sizeof(buf) - 1, sizeof(buf) - 1, format, args); va_end(args); while ((p > buf) && iswspace(*(p - 1))) *--p = '\0'; *p++ = '\r'; *p++ = '\n'; *p = '\0'; #if defined(_UNICODE) || defined(UNICODE) wchar_t output[MAX_OUTPUT_LEN]; plog->FormatBuf(buf, output); plog->Output(output); #else char output[MAX_OUTPUT_LEN]; if(Utf16ToAnsiUseCharArray(buf, m_ansiBuf, MAX_OUTPUT_LEN)){ plog->FormatBuf(m_ansiBuf, output); plog->Output(output); }else{ char *ansiOut = Utf16ToAnsi(buf); plog->FormatBuf(ansiOut, output); plog->Output(output); SAFEDELETEARR(ansiOut); } #endif } } catch (...) { assert(0); } } static void WriteLogA(const char* format, ...) { try { CLog* plog = CLog::GetInstance(); if (plog == NULL) return; std::lock_guard<std::mutex> lg(_lock); if (plog->m_bOutputLogFile || plog->m_bOutputDbgView || plog->m_bOutputConsole) { static char buf[MAX_OUTPUT_LEN], *p; p = buf; va_list args; va_start(args, format); p += _vsnprintf_s(p, sizeof(buf) - 1, sizeof(buf) - 1, format, args); va_end(args); while ((p > buf) && _istspace(*(p - 1))) *--p = '\0'; *p++ = '\r'; *p++ = '\n'; *p = '\0'; #if defined(_UNICODE) || defined(UNICODE) wchar_t output[MAX_OUTPUT_LEN]; if (AnsiToUtf16Array(buf, m_utf16Buf, MAX_OUTPUT_LEN)) { plog->FormatBuf(m_utf16Buf, output); plog->Output(output); } else { wchar_t *wBuf = AnsiToUtf16(buf); plog->FormatBuf(wBuf, output); plog->Output(output); SAFEDELETEARR(wBuf); } #else char output[MAX_OUTPUT_LEN]; plog->FormatBuf(buf, output); plog->Output(output); #endif } } catch (...) { assert(0); } } #pragma endregion protected: #pragma region protected functions CLog() : m_pLogFile(NULL), m_bOutputLogFile(FALSE), m_bOutputDbgView(FALSE), m_bOutputConsole(FALSE), m_bConsoleOpened(FALSE), m_bDbgviewOpened(FALSE), m_bLogFileOpened(FALSE), m_bRunning(TRUE) { QueryPerformanceFrequency(&freq); //OutputDebugString(_T("The frequency of your pc is 0x%X.\n"), freq.QuadPart); QueryPerformanceCounter(&start_t); memset(g_szFileName, 0, sizeof(g_szFileName)); TCHAR out[128] = { 0 }; wsprintf(out, _T("CLog construction addr: %p\n"), this); OutputDebugString(out); memset(&pi, 0, sizeof(pi)); InitializeCriticalSection(&m_cs); } LPCTSTR GetAppRunTime() { static TCHAR szTime[128]; memset(szTime, 0, sizeof szTime); QueryPerformanceCounter(&stop_t); exe_time = 1e3*(stop_t.QuadPart - start_t.QuadPart) / freq.QuadPart; WORD day, hour, min, sec; sec = static_cast<unsigned short>(static_cast<unsigned long>(exe_time / 1000) % 60); min = static_cast<unsigned short>(static_cast<unsigned long>(exe_time / 1000 / 60) % 60); hour = static_cast<unsigned short>((exe_time / 1000 / 60) / 60); day = static_cast<unsigned short>(exe_time / 1000.0 / 60.0 / 60.0 / 24); double ms = exe_time - (int)(exe_time)+((int)(exe_time) % 1000); wsprintf(szTime, _T("%dday%02d:%02d:%02ds.%3.6fms"), day, hour, min, sec, ms); return szTime; } static const wchar_t* GetPrivateLogPath() { static wchar_t strPrivateMapFolder[1024] = { 0 }; static BOOL b = TRUE; if (b) { wsprintf(strPrivateMapFolder, _T("%s\\Log"), GetModuleFilePath()); b = FALSE; } return strPrivateMapFolder; } void Output(const TCHAR *out) { if (m_bOutputLogFile) { OutputFile(out); } #ifdef _DEBUG OutputDebugString(out); #else if(m_bOutputDbgView){ OutputDebugString(out); } #endif if (m_bOutputConsole) { _tprintf(out); } } DWORD FormatBuf(const TCHAR *oldBuf, TCHAR *newBuf, DWORD max_new_buff_len = MAX_OUTPUT_LEN) { static SYSTEMTIME st; static TCHAR sztime[128]; memset(sztime, 0, sizeof sztime); GetLocalTime(&st); wsprintf(sztime, _T("HB %04d-%02d-%02d-w%d %02d:%02d:%02d.%03d ---- "), st.wYear, st.wMonth, st.wDay, (st.wDayOfWeek != 0) ? st.wDayOfWeek : 7, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); lstrcpy(newBuf, sztime); if (static_cast<DWORD>(lstrlen(sztime) + lstrlen(oldBuf)) < max_new_buff_len) { lstrcat(newBuf, oldBuf); } return static_cast<DWORD>(lstrlen(newBuf)); } void OutputFile(const TCHAR *buf) { if (m_pLogFile == NULL) m_pLogFile = OpenLogFile(); if (m_pLogFile != NULL) { fseek(m_pLogFile, 0, SEEK_END); long filelen = ftell(m_pLogFile); if (filelen >= MAX_FILE_LEN) { fclose(m_pLogFile); CreateFileName(); m_pLogFile = OpenLogFile(); } } if (m_pLogFile != NULL) { fseek(m_pLogFile, 0, SEEK_END); #if defined(_UNICODE) || defined(UNICODE) if (Utf16ToAnsiUseCharArray(buf, m_ansiBuf, MAX_OUTPUT_LEN)) { fwrite(m_ansiBuf, 1, strlen(m_ansiBuf), m_pLogFile); } else { char *ansiOut = Utf16ToAnsi(buf); fwrite(ansiOut, 1, strlen(ansiOut), m_pLogFile); SAFEDELETEARR(ansiOut); } #else fwrite(buf, 1, strlen(buf), m_pLogFile); #endif fflush(m_pLogFile); } } BOOL OpenDbgview(BOOL bOpen) { static STARTUPINFO si; static LPCTSTR lpszAppName = _T("dbgview.exe"); if (bOpen) { if (!m_bDbgviewOpened) { memset(&si, 0, sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO); si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_SHOW; if (CreateProcess(lpszAppName, NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) { m_bDbgviewOpened = TRUE; } } } else if (m_bDbgviewOpened) { CloseHandle(pi.hThread); CloseHandle(pi.hProcess); m_bDbgviewOpened = FALSE; } return bOpen ? m_bDbgviewOpened : !m_bDbgviewOpened; } FILE* OpenLogFile() { FILE *pfile = NULL; _tfopen_s(&pfile, g_szFileName, _T("r")); if (pfile == NULL) { _tfopen_s(&pfile, g_szFileName, _T("wb")); if (pfile == NULL) { MessageBox(NULL, _T("Create log file failed."), NULL, 0); ASSERT(0); return NULL; } } else { fclose(pfile); _tfopen_s(&pfile, g_szFileName, _T("ab")); if (pfile == NULL) { MessageBox(NULL, _T("Open log file failed."), NULL, 0); ASSERT(0); return NULL; } } if (pfile != NULL) m_bLogFileOpened = TRUE; return pfile; } void CreateFileName() { SYSTEMTIME st; GetLocalTime(&st); const wchar_t* path = GetPrivateLogPath(); CreateDirectory(path, NULL); wchar_t exe[1024] = { 0 }; GetModuleFileName(NULL, exe, 1023); wsprintf(g_szFileName, _T("%s\\%s-%04d-%02d-%02d-w%d-%02d-%02d-%02d"), path, CFileOper::GetFileNameFromPathName(exe), st.wYear, st.wMonth, st.wDay, (st.wDayOfWeek != 0) ? st.wDayOfWeek : 7, st.wHour, st.wMinute, st.wSecond); lstrcat(g_szFileName, _T(".log")); } void CloseLogFile() { if (m_pLogFile != NULL) { fflush(m_pLogFile); fclose(m_pLogFile); m_pLogFile = NULL; } } BOOL OpenConsole(BOOL bOpen) { static FILE *pConsole = NULL; if (bOpen) { if (!m_bConsoleOpened) { if (AllocConsole()) { SetConsoleTitle(_T("output")); /*COORD coord; coord.X = 120; coord.Y = 60; HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); FillConsoleOutputAttribute(hStdOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE, 120, coord, NULL); SetConsoleScreenBufferSize(hStdOut, coord); SMALL_RECT se; se.Left = 0; se.Top = 0; se.Right = 100; se.Bottom = 100; SetConsoleWindowInfo(hStdOut, FALSE, &se); */ _tfreopen_s(&pConsole, _T("CONOUT$"), _T("w"), stdout); m_bConsoleOpened = TRUE; } } return m_bConsoleOpened; } else { if (m_bConsoleOpened) { fclose(pConsole); if (FreeConsole()) { m_bConsoleOpened = FALSE; } } return !m_bConsoleOpened; } } LPCTSTR GetLastTimeLogFileName() { return g_szFileName; } }; #pragma endregion }; //CRITICAL_SECTION CLog::m_cs; //int CLog::m_nRef = 0; //char CLog::m_ansiBuf[MAX_OUTPUT_LEN] = {0}; //CLock CLog::m_lockForSingleton; #endif // !defined(AFX_ERRORLOG_H__46D664F1_E737_46B7_9813_2EF1415FF884__INCLUDED_)