|
#1
|
||||
|
||||
implement a simple thread-safe debug printf logger under MS VC++
Currently I need to implement a thread-safe application logger under MS Visual C++. The main purpose is to redirect debug_printf and exception strings (thown by C++ classes) to files so I can find out what's wrong with my Win32 service program, and the max log file size can be controlled so that disk space will not be exhausted by too many debug output. __FILE__ , __LINE__ and date/time are
mandatory parameters for the logger class or function. Unfortunately MS C++ compiler is not C99-compatible so it doesn't support variable-argument macros using __VA_ARGS__ as GCC does. After some search, I found some useful info, but some of those implementations are not thread-safe, for example, the following two printf() can be interrupted in multi-threaded environment, and the log file size can't be controlled. http://jxta-c.jxta.org/source/browse/jxta-c/include/Attic/jxta_debug.h?logsort=date&search=&hideattic=1&sortby=file&hidecvsroot=1&diff_format=h&r1=1.2 Code:
#define JXTA_LOG(x) \ printf("%s:%d ", __FILE__, __LINE__), printf x "Calls" to JXTA_LOG would look like: JXTA_LOG((a1, a2, a3, ...)); The above Russian post is quite good, especially the CMyPrintf and MYprintfFN implementations. But it seems that they can't be used in c++ "throw" environment. For example, I want to log the following exception string to file with __FILE__ and __LINE__. Code:
if ((f = fopen(FileName, "rb")) == NULL) { throw MyException("can't open file: %s\n", FileName); } I have implement similar logger, but it is not so elegant and can't be used in throw environment either. Code:
class Logger { public: Logger(); ~Logger(); static void LogPrefix(const TCHAR *File, int Line); static void Log(const TCHAR *Format, ...); private: #ifdef LOG_TO_FILE static void OpenFile(const char *mode); static const long MAX_LOG_FILE_SIZE; #endif static FILE *m_File; static DWORD m_LogCount; static CCriticalSection m_FileLock; }; #define LOG(x) ( \ Logger::LogPrefix(__FILE__, __LINE__), \ (void)(x) \ ) #ifdef LOG_TO_FILE const long Logger::MAX_LOG_FILE_SIZE = 2 * 1024 * 1024; FILE * Logger::m_File = NULL; #else FILE * Logger::m_File = stderr; #endif DWORD Logger::m_LogCount = 0; CCriticalSection Logger::m_FileLock; Logger::Logger() { #ifdef LOG_TO_FILE OpenFile("at"); #else m_File = stderr; #endif } Logger::~Logger() { #ifdef LOG_TO_FILE if (m_File) { fclose(m_File); m_File = NULL; } #endif } #ifdef LOG_TO_FILE void Logger::OpenFile(const char *mode) { TCHAR LogFileName[MAX_PATH]; GetModuleFileNameA(NULL, LogFileName, sizeof(LogFileName)/sizeof(LogFileName[0])); strncpy(LogFileName, g_pVar->m_AppPath.c_str(), sizeof(LogFileName) - 1); LogFileName[sizeof(LogFileName) - 1] = '\0'; strncat(LogFileName, "\\logcenter.log", sizeof(LogFileName) - g_pVar->m_AppPath.size() - 2); m_File = fopen(LogFileName, mode); } #endif void Logger::LogPrefix(const TCHAR *File, int Line) { m_FileLock.Lock(); #ifdef LOG_TO_FILE if (NULL == m_File) OpenFile("at"); if (m_LogCount > 100) { if (ftell(m_File) > MAX_LOG_FILE_SIZE) { fclose(m_File); OpenFile("wt"); } m_LogCount = 0; } #endif for(signed int k = strlen(File); k >= 0; k--) { if (File[k] == '\\') { break; } } k++; TCHAR Prefix[512]; SYSTEMTIME LocalTime; GetLocalTime(&LocalTime); wsprintf(Prefix, _T("%04d-%02d-%02d %02d:%02d:%02d %s %d"), LocalTime.wYear, LocalTime.wMonth, LocalTime.wDay, LocalTime.wHour, LocalTime.wMinute, LocalTime.wSecond, File + k, Line); fprintf(m_File, _T("%s "), Prefix); } void Logger::Log(const TCHAR *Format, ...) { TCHAR ErrorMsg[1024]; va_list ap; va_start(ap, Format); _vsntprintf(ErrorMsg, sizeof(ErrorMsg)/sizeof(ErrorMsg[0]), Format, ap); fprintf(m_File, _T("%s"), ErrorMsg); m_LogCount++; m_FileLock.Unlock(); }
__________________
AKA Solomon/blowfish. Last edited by WhoCares; 01-05-2005 at 21:34. |
#2
|
|||
|
|||
Hi,
maybe you can take a look at hxxp://www.codeproject.com/debug/dbg.asp ... ... even if this one doesn't fit your needs, you can take some ideas from there Kerstin |
#3
|
||||
|
||||
Thanks for the info.
I read the CDebugPrintf source, it doesn't embed __FILE__/__LINE__ into log lines. My conclusion is that only Macros with a variable number of arguments can do the job. That is, only C99-compatible c++ compilers. Actually I have implemented such a logger with GCC(what a splendid compiler!) I have tested Intel c++ compiler v8.1, it's a pity that its "/Qc99" option only works with pure C programs, neither c++ programs nor c/c++ mixed programs. http://softwareforums.intel.com/ids/board/message?board.id=16&message.id=1643#M1643 I will try to see whether GCC for Windows can compile my MFC program.
__________________
AKA Solomon/blowfish. Last edited by WhoCares; 01-06-2005 at 13:29. |
#4
|
|||
|
|||
Hi,
May this is helpful (or useless) : xxxx://logging.apache.org/log4cxx/ If you are familiar with Log4j you won't be disappointed. |
Thread Tools | |
Display Modes | |
|
|
Similar Threads | ||||
Thread | Thread Starter | Forum | Replies | Last Post |
[C++] Simple Anti-Debug trick | Mahmoudnia | Source Code | 1 | 07-17-2022 20:13 |