|
#include <Windows.h> #include "Log_msg.h"
int TestWinerrReturn() { LOG_WINERROR_MSG_RETURN(10, 5); return -100; }
int TestErrmsgReturn() { LOG_WINERROR_MSG_RETURN(10, 5); return -100; }
int TestCombine() { LOG_MSG_ERROR_RETURN(10, _T("ERRORNO:%d, description: %s, return val:%d"), 5, LOG_WIN32ERROR_TO_MSG_WRAPPER(5), 10); return 1; } int TestWrapper() { LOG_MSG_ERROR(LOG_WIN32ERROR_TO_MSG_WRAPPER(5)); return 0; }
int _tmain(int argc, _TCHAR* argv[]) { LOG_CONFIGURATION conf = {true, Log_entity::LL_INFO, Log_entity::LO_FILE, _T("c:\\987.log")}; LOG_MSG_INITIALIZE(conf); LOG_MSG_INFO(_T("LOG_MSG_INFO1")); LOG_MSG_INFO(_T("LOG_MSG_INFO1:%s"), _T("info2")); LOG_MSG_WARNING((_T("LOG_MSG_WARNING"))); LOG_MSG_WARNING(_T("LOG_MSG_WARNING1:%s"), _T("warning")); LOG_MSG_ERROR((_T("LOG_MSG_ERROR"))); LOG_MSG_ERROR(_T("LOG_MSG_ERROR1:%s"), _T("Error")); if(TestWinerrReturn() != 10) { printf("TestWinerrReturn failed"); } if(TestErrmsgReturn() != 10) { printf("TestErrmsgReturn failed"); } LOG_WINERROR_MSG((5)); if(TestCombine() != 10) { printf("TestCombine failed"); } system("pause"); return 0; }
/**///////////////////////////////////////////////////////////////////////////// file Log_msg.h // // Copyright (c) 2010 robert xiao. All Rights Reserved. robert.xiao2010@gmail.com // // Permission to use, copy, modify, and distribute this software and its // documentation for any purpose, without fee, and without a written // agreement, is hereby granted, provided that the above copyright notice, // this paragraph and the following two paragraphs appear in all copies, // modifications, and distributions. // // // IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, // INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST // PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, // EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A // PARTICULAR PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF // ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". THE AUTHOR HAS NO OBLIGATION // TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. // /**/////////////////////////////////////////////////////////////////////////// /**//** * @file: Log_msg.h * * @brief: provide write log message function, macro, and assistant class. * * @author: Robert xiao * */
#pragma once
#if !defined (__TFILE__) #define __TFILE__ _T(__FILE__) #endif /**//* get description from a windows error. */ #if !defined LOG_WIN32ERROR_TO_MSG_WRAPPER #define LOG_WIN32ERROR_TO_MSG_WRAPPER(x) \ Log_winErrorMsg(x) #endif
/**//* Intend to initialize the logger instance */ #if !defined (LOG_MSG_INITIALIZE) #define LOG_MSG_INITIALIZE(conf) \ { Log_entity::Initialize(conf);} #endif
/**//* Intend to print information */ #if !defined (LOG_MSG_INFO) #define LOG_MSG_INFO(x, ) \ do{ \ Log_entity::GetInstance()->Log(Log_entity::LL_INFO, __TFILE__, __LINE__, x, __VA_ARGS__);\ }while(0) #endif
/**//* print warning message */ #if !defined (LOG_MSG_WARNING) #define LOG_MSG_WARNING(x,) \ do{\ Log_entity::GetInstance()->Log(Log_entity::LL_WARNING, __TFILE__, __LINE__, x, __VA_ARGS__); \ }while(0) #endif
/**//* print error message */ #if !defined (LOG_MSG_ERROR) #define LOG_MSG_ERROR(x,) \ do{\ Log_entity::GetInstance()->Log(Log_entity::LL_ERROR, __TFILE__, __LINE__, x, __VA_ARGS__); \ }while(0) #endif
/**//* print error message and return y */ #if !defined (LOG_MSG_ERROR_RETURN) #define LOG_MSG_ERROR_RETURN(y, x,) \ do{\ Log_entity::GetInstance()->Log(Log_entity::LL_ERROR, __TFILE__, __LINE__, x, __VA_ARGS__); \ return y;\ }while(0) #endif
/**//* print fatal message */ #if !defined (LOG_MSG_FATAL) #define LOG_MSG_FATAL(x,) \ do{\ Log_entity::GetInstance()->Log(Log_entity::LL_FATAL, __TFILE__, __LINE__, x, __VA_ARGS__); \ }while(0) #endif
/**//* print fatal message and return */ #if !defined (LOG_MSG_FATAL_RETURN) #define LOG_MSG_FATAL_RETURN(y, x, ) \ do{\ Log_entity::GetInstance()->Log(Log_entity::LL_FATAL, __TFILE__, __LINE__, x , __VA_ARGS__); \ return y;\ }while(0) #endif
/**//* print error no. description */ #if !defined (LOG_WINERROR_MSG) #define LOG_WINERROR_MSG(x)\ do{ \ Log_entity::GetInstance()->Log(Log_entity::LL_ERROR, __TFILE__, __LINE__, Log_winErrorMsg(x));\ }while(0) #endif
/**//* print error no. description and return */ #if !defined (LOG_WINERROR_MSG_RETURN) #define LOG_WINERROR_MSG_RETURN(y, x)\ do{ \ Log_entity::GetInstance()->Log(Log_entity::LL_ERROR, __TFILE__, __LINE__, Log_winErrorMsg(x));\ return y; \ }while(0) #endif
/**//* print COM result description */ #if !defined (LOG_HRESULT_MSG) #define LOG_HRESULT_MSG(x) \ do {\ Log_entity::GetInstance()->Log(Log_entity::LL_ERROR, __TFILE__, __LINE__, Log_winErrorMsg(x & 0x0000ffff));\ }while(0) #endif
/**//* print COM result description and return */ #if !defined (LOG_HRESULT_MSG_RETURN) #define LOG_HRESULT_MSG_RETURN(y, x) \ do {\ Log_entity::GetInstance()->Log(Log_entity::LL_ERROR, __TFILE__, __LINE__, Log_winErrorMsg(x & 0x0000ffff));\ return y; \ }while(0) #endif
#include <Windows.h>
#define LOG_API
class LOG_API Log_critical{ public: Log_critical(){ m_lEntered = 0; ::InitializeCriticalSection(&m_sec); }
~Log_critical(){ ::DeleteCriticalSection(&m_sec); } void Lock(DWORD){ ::EnterCriticalSection(&m_sec); ++m_lEntered; }
void UnLock(){ if(m_lEntered > 0) { --m_lEntered; ::LeaveCriticalSection(&m_sec); } } private: CRITICAL_SECTION m_sec; LONG m_lEntered; };
template<typename T, DWORD dwWaitTime = INFINITE> class LOG_API Log_manualLock{
public:
Log_manualLock(T& t, bool bLock) :m_lock(t) ,m_bLocked(false){ m_lock = t; if(bLock){ Lock(); } }
~Log_manualLock(){ if(m_bLocked){ UnLock(); } }
void Lock(){ m_lock->Lock(dwWaitTime); m_bLocked = true; }
void UnLock(){ if(m_bLocked){ m_lock->UnLock(); m_bLocked = false; } }
private: T &m_lock; bool m_bLocked; };
template<typename T, DWORD dwWaitTimeOut = INFINITE> class LOG_API Log_autoLock{ public: Log_autoLock(T &t) :m_lock(t){ m_lock.Lock(dwWaitTimeOut); }
~Log_autoLock(){ m_lock.UnLock(); }
private: T &m_lock; };
class LOG_API Log_winErrorMsg{
typedef TCHAR _character_type; typedef _character_type* _p_character_type; typedef const TCHAR* _pc_character_type;
public: Log_winErrorMsg(int nError) :_szBuf(NULL){ ::FormatMessage ( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_MAX_WIDTH_MASK | FORMAT_MESSAGE_FROM_SYSTEM, NULL, nError, MAKELANGID (LANG_NEUTRAL,SUBLANG_DEFAULT), (LPTSTR ) &_szBuf, 0, NULL); } ~Log_winErrorMsg(){ if(_szBuf) LocalFree(_szBuf); }
operator _pc_character_type(){ return _szBuf; };
_pc_character_type c_str(){ return _szBuf; }
private: _p_character_type _szBuf; }; struct _LOG_CONFIGURATION; /**//** * @class Log_entity * * @brief provide a variable length argument message logging * * @This class is liable for write the log message to standard output, * DebugView tool , File. * * */ class LOG_API Log_entity { typedef Log_autoLock< Log_critical > _AutoCritical; public: // embed custom type enum LOG_LEVEL{ LL_INFO = 0, LL_WARNING, LL_ERROR, LL_FATAL };
enum LOG_OUTPUT{ LO_NONE = 0x0, LO_DEBUG = 0x2, LO_STANDARD = 0x4, LO_STANDARD_DEBUG = 0X6, LO_FILE = 0x8, LO_FILE_DEBUG = 0X0A, LO_FILE_STANDARD =0X0C, };
public: static Log_entity* GetInstance(); static void Initialize(struct _LOG_CONFIGURATION& con); void Log(LOG_LEVEL le, LPCTSTR szMsg, ); void Log(LOG_LEVEL le, LPCTSTR szFile, int nLine, LPCTSTR szMsg, );
static void Dumb(LPCTSTR szMsg, ){} private: void _write(LPCTSTR szMsg);
/**//* Do nothing */ void _writeNone( LPCTSTR){}
/**//* Write the message to DebugView */ void _writeDebug( LPCTSTR szMsg);
/**//* Write the message to stderr */ void _writeStandard( LPCTSTR szMsg);
/**//* Write the message to the file */ void _writeFile( LPCTSTR szMsg);
bool CanLog(LOG_LEVEL le); private: static Log_critical m_sCritical; };
typedef struct _LOG_CONFIGURATION{ bool _bNeedTime; Log_entity::LOG_LEVEL _lowerLevel; Log_entity::LOG_OUTPUT _output; TCHAR _szLogFile[MAX_PATH]; }LOG_CONFIGURATION, *LPLOG_CONFIGURATION;
/**///////////////////////////////////////////////////////////////////////////// file Log_msg.cpp // // Copyright (c) 2010 robert xiao. All Rights Reserved. robert.xiao2010@gmail.com // // Permission to use, copy, modify, and distribute this software and its // documentation for any purpose, without fee, and without a written // agreement, is hereby granted, provided that the above copyright notice, // this paragraph and the following two paragraphs appear in all copies, // modifications, and distributions. // // // IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, // INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST // PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, // EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A // PARTICULAR PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF // ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". THE AUTHOR HAS NO OBLIGATION // TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. // /**/////////////////////////////////////////////////////////////////////////// /**//** * @file: Log_msg.cpp * * @author: Robert xiao * */
#include "stdafx.h" #include <io.h> #include "Log_msg.h"
/**//** * @global variable * * Log_entity g_logger * * g_config * * DEBUGFUFFERLEN */ static Log_entity g_logger; static LOG_CONFIGURATION g_config = {true, Log_entity::LL_ERROR, Log_entity::LO_NONE, _T('\0')}; const USHORT DEBUGFUFFERLEN = 4096;
#define LOG_MSG_SECTION "log_msg" #define LOG_LEVEL_CAPTION "log_level" #define LOG_OUTPUT_CAPTION "log_output" #define LOG_FILE_CAPTION "log_file"
namespace{
/**//* if szPath is file name, then convert it to full path name */ bool _ValidateLogFilePath(LPTSTR szPath){ if(!_tcslen(szPath)){ return false; }
if(TCHAR *p = _tcsrchr(szPath, _T('\\'))){ TCHAR szDirectory[MAX_PATH] = {_T('\0')}; _tcsncpy_s(szDirectory, MAX_PATH, szPath, p - szPath); WIN32_FIND_DATA data; HANDLE h; if((h = FindFirstFile(szDirectory, &data)) != INVALID_HANDLE_VALUE){ FindClose(h); if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){ return true; } } return false; } TCHAR szDirectory[MAX_PATH] = {_T('\0')}; GetModuleFileName(NULL, szDirectory, MAX_PATH); _tcscat_s(szDirectory, MAX_PATH, szPath); _tcscpy_s(szPath, MAX_PATH, szDirectory); return true; }
int PrintCurrentTimeString(LPTSTR szTime, int nBufferLen) { SYSTEMTIME stLocalTime; FILETIME ftLocalTime; GetSystemTime(&stLocalTime); GetSystemTimeAsFileTime(&ftLocalTime);
return _stprintf_s(szTime, nBufferLen, _T("thread--0x%04x %02d:%02d:%02d.%03d%s %02d-%02d-%4d: "), GetCurrentThreadId(), (stLocalTime.wHour <= 12)?stLocalTime.wHour:stLocalTime.wHour-12, stLocalTime.wMinute, stLocalTime.wSecond, stLocalTime.wMilliseconds, (stLocalTime.wHour <= 12)?_T("AM"):_T("PM"), stLocalTime.wMonth, stLocalTime.wDay, stLocalTime.wYear); }
int PrintMsgTypeString(Log_entity::LOG_LEVEL le, LPTSTR szBuf){ if(!szBuf){ return 0; } static TCHAR msgTopics[][20] = { _T("INFO: "), _T("WARNING: "), _T("ERROR: "), _T("FATAL: ") }; _tcscpy_s(szBuf, 20, msgTopics[le]); return static_cast<int>(_tcslen(szBuf)); }
class Log_file{ public: Log_file(){ pf = 0; }
int Open(LPCTSTR szFileName){ if(!szFileName){ return EXIT_FAILURE; }
LPCTSTR szParams = _T("a+,ccs=UTF-8"); #if defined (_MSC_VER) && (_MSC_VER >= 1500) int err = _tfopen_s(&pf, szFileName, szParams); #else pf = _tfopen(szFileName, szParams); #endif if(pf){ return EXIT_SUCCESS; } else { //File IO failed - complain - not using error macros since we may not have debug output here TCHAR szErr[256]; DWORD dwErr = GetLastError(); HRESULT hRes = HRESULT_FROM_WIN32(dwErr); LPTSTR szSysErr = NULL; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM, 0, dwErr, 0, (LPTSTR)&szSysErr, 0, 0);
_stprintf_s(szErr,sizeof(szErr) / sizeof(TCHAR), _T("_tfopen failed, hRes = 0x%08X, dwErr = 0x%08X = \"%s\"\n"), HRESULT_FROM_WIN32(dwErr), dwErr, szSysErr);
OutputDebugString(szErr); LocalFree(szSysErr); return NULL; } return EXIT_FAILURE; }
int WriteLine(LPCTSTR szMsg){ if(pf && szMsg){ if(_fputts(szMsg, pf) >= 0){ _fputts(_T("\n"), pf); return EXIT_SUCCESS; } } return EXIT_FAILURE; }
~Log_file() { if(pf){ fclose(pf); } } private: FILE *pf; }; }
Log_critical Log_entity::m_sCritical;
Log_entity* Log_entity::GetInstance(){ return &g_logger; }
void Log_entity::Initialize( struct _LOG_CONFIGURATION& con ){ if(con._output & LO_FILE){ TCHAR szTemp[MAX_PATH]; _tcscpy_s(szTemp, MAX_PATH, con._szLogFile); if(!_ValidateLogFilePath(szTemp)){ return ; } _tcscpy_s(g_config._szLogFile, MAX_PATH, szTemp); } g_config._lowerLevel = con._lowerLevel; g_config._output = con._output; g_config._bNeedTime = con._bNeedTime; }
void Log_entity::Log( LOG_LEVEL le, LPCTSTR szMsg, ){ if(!CanLog(le)){ return; }
if(!szMsg){ return; }
TCHAR szDebugBuffer[DEBUGFUFFERLEN] = {_T('\0')}; TCHAR *p = szDebugBuffer; int nRemainBuffer = DEBUGFUFFERLEN; int nUse = PrintMsgTypeString(le, p); p += nUse; if(g_config._bNeedTime){ nUse = PrintCurrentTimeString(p, DEBUGFUFFERLEN - (p -szDebugBuffer)); if(nUse >= 0){ p += nUse; } }
nRemainBuffer = DEBUGFUFFERLEN - (p - szDebugBuffer); if(_tcsrchr(szMsg, _T('%'))){ va_list argList = NULL; va_start(argList, szMsg); int nWrite = _vsntprintf_s(p, nRemainBuffer - 1, nRemainBuffer, szMsg, argList); if(nWrite >= nRemainBuffer){ szDebugBuffer[DEBUGFUFFERLEN - 1] = _T('\0'); } va_end(argList); } else{ _tcscat_s(p, nRemainBuffer, szMsg); }
_write(szDebugBuffer); }
void Log_entity::Log( LOG_LEVEL le, LPCTSTR szFile, int nLine, LPCTSTR szMsg, ){ if(!CanLog(le)){ return; }
if(!szMsg){ return; }
TCHAR szDebugBuffer[DEBUGFUFFERLEN] = {_T('\0')}; TCHAR *p = szDebugBuffer; int nRemainBuffer = DEBUGFUFFERLEN; int nUse = PrintMsgTypeString(le, p); p += nUse; if(szFile){ TCHAR szSourceBuffer[MAX_PATH * 2]; _stprintf_s(szSourceBuffer, MAX_PATH * 2, _T("File:'%s' Line: %d; "), szFile, nLine); _tcscat_s(szDebugBuffer, nRemainBuffer, szSourceBuffer); p += _tcslen(szSourceBuffer); } if(g_config._bNeedTime){ nUse = PrintCurrentTimeString(p, DEBUGFUFFERLEN - (p - szDebugBuffer)); if(nUse >= 0){ p += nUse; } }
nRemainBuffer = DEBUGFUFFERLEN - (p - szDebugBuffer); if(_tcsrchr(szMsg, _T('%'))){ va_list argList; va_start(argList, szMsg); int nWrite = _vsntprintf_s(p, nRemainBuffer - 1, nRemainBuffer, szMsg, argList); if(nWrite >= nRemainBuffer){ szDebugBuffer[DEBUGFUFFERLEN - 1] = _T('\0'); } va_end(argList); } else{ _tcscat_s(p, nRemainBuffer, szMsg); } _write(szDebugBuffer); }
void Log_entity::_write(LPCTSTR szMsg ){ if(g_config._output & LO_FILE){ _writeFile(szMsg); }
if(g_config._output & LO_STANDARD){ _writeStandard(szMsg); }
if(g_config._output & LO_DEBUG){ _writeDebug(szMsg); } }
bool Log_entity::CanLog( LOG_LEVEL le ){ return le >= g_config._lowerLevel && g_config._output != LO_NONE; }
void Log_entity::_writeDebug( LPCTSTR szMsg ){ OutputDebugString(szMsg); }
void Log_entity::_writeStandard( LPCTSTR szMsg ){ _tprintf(szMsg); _tprintf(_T("\n")); }
void Log_entity::_writeFile( LPCTSTR szMsg ){ _AutoCritical singleLock(m_sCritical); Log_file logFile; if(logFile.Open(g_config._szLogFile) == EXIT_SUCCESS){ logFile.WriteLine(szMsg); } }
|