Exetools

Exetools (https://forum.exetools.com/index.php)
-   General Discussion (https://forum.exetools.com/forumdisplay.php?f=2)
-   -   implement a simple thread-safe debug printf logger under MS VC++ (https://forum.exetools.com/showthread.php?t=6269)

WhoCares 01-05-2005 21:23

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, ...));

http://forum.sources.ru/index.php?showtopic=63242&view=showall
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);
}

Anyone has any experience with suck tricks?

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();
}


nuemga2000 01-05-2005 21:55

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

WhoCares 01-06-2005 13:26

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.

new_profile 01-06-2005 15:59

Hi,
May this is helpful (or useless) : xxxx://logging.apache.org/log4cxx/
If you are familiar with Log4j you won't be disappointed.


All times are GMT +8. The time now is 14:55.

Powered by vBulletin® Version 3.8.8
Copyright ©2000 - 2026, vBulletin Solutions, Inc.
Always Your Best Friend: Aaron, JMI, ahmadmansoor, ZeNiX