目录
在前文“利用c语言中宏定义及宏替换特性构建自己的log输出_py_free的博客-CSDN博客”,利用宏定义及宏替换,可以实现日志信息屏幕输出,但通常我们在实际项目中:
【1】需要将日志信息记录在文件中
【2】日志记录顺序需要得到保证
【3】日志信息是不确定的,需要支持到不确定参数集输入
鉴于以上三点,来设计一个日志小系统
在c/c++中VA_LIST是解决变参问题的一组宏,所在头文件:#include
(1)首先在函数里定义一具VA_LIST型的变量,这个变量是指向参数的指针;
(2)然后用VA_START宏初始化刚定义的VA_LIST变量;
(3)然后用VA_ARG返回可变的参数,VA_ARG的第二个参数是你要返回的参数的类型(如果函数有多个可变参数的,依次调用VA_ARG获取各个参数);
(4)最后用VA_END宏结束可变参数的获取。
- void myprint(const char* lpszFormat, ...)
- {
- va_list args;
- char szBuffer[2048] = {0};
- va_start(args, lpszFormat);
- #ifdef WIN32
- vsnprintf_s(szBuffer,sizeof(szBuffer), lpszFormat, args);
- #else
- vsnprintf(szBuffer, sizeof(szBuffer), lpszFormat, args);
- #endif
- va_end(args);
- //printf(szBuffer);
- }
在c/c++中,单体实例实现主要就是将类的构造、拷贝构造函数等隐藏起来,使其仅在第一次实例化时可以创建实例,后续其每次实例化或使用时,仅提供或指向其第一次实例化创建的唯一实例指针。
- class SingleClass
- {
- public:
- static SingleClass* createInstance( void )
- {
- if (m_Instance== NULL)
- {
- m_Instance= new SingleClass();
- return m_Instance;
- }
- else
- return m_Instance;
- }
- private:
- SingleClass(){};
- ~SingleClass(){};
- private:
- static SingleClass* m_Instance;
- };
鉴于以上的要求,计划设计一个单体类CLogger,该类具备独立线程能力,采用一个队列来接收来自各个线程、各个功能模块的日志推送,CLogger实例负责将加入到队列的日志,添加一些附加信息后,逐条记录在文件中。
为了区分日志事件,通过枚举创建日志类型;针对日志运行是一个长期的过程,不可能全部存储在一个日志文件中,因此指定一个文件目录,并按天来存放.
- log_test
- bin #编译目标输出目录
- build_win #win编译文件及中间文件目录
- build_linux #Linux编译文件及中间文件目录
- src #源码
- log.h #单体实例,继承自定义基类MyThread线程类
- log.cpp
- Mutex.h
- Mutex.cpp
- myThread.h #linux下线程类实现
- myThread.cpp
- win32Thread.h #win下线程类实现
- win32Thread.cpp
- CMakeLists.txt #cmake工程配置文件
log.h:
- #ifndef CHANNELLOG_H
- #define CHANNELLOG_H
-
- #include
- #include
- #include
-
- #ifdef WIN32
- #include "win32Thread.h"
- #endif
-
- #ifdef linux
- #include "myThread.h"
- #include
- #endif
-
- #include
- #include "Mutex.h"
-
- enum eLogType
- {
- eHardError = 1,
- eSoftError = 2,
- eConfigError = 3,
- eParameterError = 4,
- eReadError = 5,
- eWriteError = 6,
- eControlMessage = 7,
- eResponseMessage = 8,
- eTipMessage = 9
- };
-
- struct MyLogStruct
- {
- MyLogStruct():type(0)
- {
- memset(szBuffer, 0, 1024);
- };
- int type;
- char szBuffer[1024];
- };
-
- class CLogger : public MyThread
- {
- public:
- void Log(const eLogType type, const char* lpszFormat, ...);
- static CLogger* createInstance( void );
- private:
- CLogger();
- ~CLogger();
- int Run();
- bool getFirstLog(MyLogStruct &it);
- void addLog(MyLogStruct it);
- private:
- static CLogger* m_pLogInstance;
- bool running;
- //for cache
- std::queue
mylogs; - PYMutex m_Mutex_Log;
- int i_log_over;
- };
- #endif
log.cpp
- #include "Log.h"
- #include
- #include
-
- #ifdef __linux__
- #ifndef sprintf_s
- #define sprintf_s sprintf
- #endif
- #include
- #include
- #include
- #else
- #include
- #endif
-
- namespace GlobalVar
- {
- std::string logdir = "log"; //后期记录日志目录重配置文件读取
- std::string logname = "pyfree"; //可设置为服务名
- };
-
- CLogger* CLogger::m_pLogInstance = NULL;
-
- void WriteLogToFile( std::string strPath,std::string strTime,const char * strMsg)
- {
- FILE * fpLog = NULL;
- //open
- #ifdef WIN32
- fopen_s(&fpLog, strPath.c_str(), "a+");
- #else
- fpLog = fopen(strPath.c_str(), "a+");
- #endif
- if (NULL != fpLog)
- {
- fseek(fpLog, 0, SEEK_END);
- fwrite(strTime.c_str(), strTime.length(), 1, fpLog);
- fwrite(strMsg, strlen(strMsg), 1, fpLog);
- fwrite("\n", 1, 1, fpLog);
- fclose(fpLog);
- }
- }
-
- std::string createFileName(std::string strTime_)
- {
- //file name
- std::string strPath = GlobalVar::logdir;
- #ifdef WIN32
- strPath += "\\";
- #else
- strPath += "/";
- #endif
- strPath += strTime_;
- strPath += "_";
- strPath += GlobalVar::logname;
- strPath += ".log";
- return strPath;
- }
-
- std::string strTime()
- {
- time_t tt;
- struct timeb tm0;
- struct tm tm1;
- char buf[512];
-
- //time(&tt);
- ftime(&tm0);
- tt = tm0.time;
- #ifdef WIN32
- localtime_s(&tm1, &tt);
- #else
- localtime_r(&tt, &tm1);
- #endif
- sprintf_s(buf, "%04d-%02d-%02d %02d:%02d:%02d.%03d "
- , tm1.tm_year + 1900, tm1.tm_mon + 1, tm1.tm_mday
- , tm1.tm_hour, tm1.tm_min, tm1.tm_sec, tm0.millitm);
- std::string strTime_ = buf;
- buf[10] = '\0';
- return strTime_;
- }
-
- std::string createLogTimeAndTypeDesc(const int iMsgType, std::string strTime_)
- {
- std::string strTimeAndTypeDesc = strTime_;
- //
- switch (iMsgType)
- {
- case eHardError:
- strTimeAndTypeDesc += "[HardErrorIMsg] ";
- break;
- case eSoftError:
- strTimeAndTypeDesc += "[SoftErrorIMsg] ";
- break;
- case eConfigError:
- strTimeAndTypeDesc += "[ConfErrorIMsg] ";
- break;
- case eParameterError:
- strTimeAndTypeDesc += "[ParamErrorMsg] ";
- break;
- case eReadError:
- strTimeAndTypeDesc += "[ReadErrorIMsg] ";
- break;
- case eWriteError:
- strTimeAndTypeDesc += "[WriteErrorMsg] ";
- break;
- case eControlMessage:
- strTimeAndTypeDesc += "[ControlExeMsg] ";
- break;
- case eResponseMessage:
- strTimeAndTypeDesc += "[ResponseUpMsg] ";
- break;
- case eTipMessage:
- strTimeAndTypeDesc += "[NoticeTipIMsg] ";
- break;
- default:
- strTimeAndTypeDesc += "[PromptUnNoMsg] ";
- break;
- }
- return strTimeAndTypeDesc;
- }
-
- void WriteLog( const int iMsgType, const char * strMsg)
- {
- try {
- //system time to string
- std::string strTime_ = strTime();
- //file name
- std::string strPath = createFileName(strTime_.substr(0,10));
- // log time and type desc
- std::string strTimeAndTypeDesc = createLogTimeAndTypeDesc(iMsgType, strTime_);
- WriteLogToFile(strPath,strTimeAndTypeDesc,strMsg);
- }
- catch (...) {
- printf("write log[%d]{%s}error\n", iMsgType, strMsg);
- }
- }
-
- CLogger::CLogger()
- : running(true)
- , i_log_over(0)
- {
- char buf[256] = {0};
- sprintf_s(buf,"mkdir %s",GlobalVar::logdir.c_str());
- system(buf);
- this->start();
- };
-
- CLogger::~CLogger()
- {
- running = false;
- };
-
- CLogger* CLogger::createInstance( void )
- {
- if (m_pLogInstance == NULL)
- {
- m_pLogInstance = new CLogger();
- return m_pLogInstance;
- }
- else
- return m_pLogInstance;
- };
-
- int CLogger::Run()
- {
- MyLogStruct log_;
- while (running) {
- if (getFirstLog(log_))
- {
- WriteLog(log_.type, log_.szBuffer);
- #ifndef WIN32
- printf("Log::[%d]-->%s\n", getpid(), log_.szBuffer);
- #else
- printf("Log::-->%s\n", log_.szBuffer);
- #endif
- }
- #ifdef WIN32
- Sleep(10);
- #else
- usleep(10000);
- #endif
- }
- return 0;
- };
-
- void CLogger::Log(const eLogType type, const char* lpszFormat, ...)
- {
- va_list args;
- //char szBuffer[2048] = {0};
- MyLogStruct log_;
- log_.type = static_cast<int>(type);
- va_start(args, lpszFormat);
- #ifdef WIN32
- vsnprintf_s(log_.szBuffer,sizeof(log_.szBuffer), lpszFormat, args);
- #else
- vsnprintf(log_.szBuffer, sizeof(log_.szBuffer), lpszFormat, args);
- #endif
- va_end(args);
- addLog(log_);
- }
-
- bool CLogger::getFirstLog(MyLogStruct &it) {
- bool ret = false;
- m_Mutex_Log.Lock();
- if (!mylogs.empty()) {
- it = mylogs.front();
- mylogs.pop();
- ret = true;
- }
- m_Mutex_Log.Unlock();
- return ret;
- }
-
- void CLogger::addLog(MyLogStruct it) {
- m_Mutex_Log.Lock();
- if (mylogs.size() > 100) {
- i_log_over++;
- mylogs.pop();
- }
- mylogs.push(it);
- m_Mutex_Log.Unlock();
- if (i_log_over >= 100) {//每溢出100次,报告一次
- MyLogStruct log_;
- log_.type = static_cast<int>(eTipMessage);
- sprintf(log_.szBuffer,"the size of mylogs queue is up to limmit size[100],[%s %s %d]."
- , __FILE__, __FUNCTION__, __LINE__);
- m_Mutex_Log.Lock();
- mylogs.push(log_);
- m_Mutex_Log.Unlock();
- i_log_over = 0;
- }
- }
Mutex.h
- #if _MSC_VER > 1000
- #pragma once
- #endif // _MSC_VER > 1000
-
- #ifndef PYMUTEX_H
- #define PYMUTEX_H
-
- #ifdef WIN32
- //#include
- #else
- #include
- #endif
-
- typedef void *HANDLE;
-
- class IMutex
- {
- public:
- virtual ~IMutex() {}
-
- virtual void Lock() const = 0;
- virtual bool TryLock() const = 0;
- virtual void Unlock() const = 0;
- };
-
- class PYMutex : public IMutex
- {
- public:
- PYMutex();
- ~PYMutex();
-
- virtual void Lock() const;
- virtual bool TryLock() const;
- virtual void Unlock() const;
- private:
- #ifdef _WIN32
- HANDLE m_mutex;
- #else
- mutable pthread_mutex_t m_mutex;
- #endif
- };
-
- #endif //PYMUTEX_H
Mutex.cpp
- #include "Mutex.h"
-
- #ifdef WIN32
- #include
- #endif
- #include
-
- PYMutex::PYMutex()
- {
- #ifdef _WIN32
- m_mutex = ::CreateMutex(NULL, FALSE, NULL);
- #else
- pthread_mutex_init(&m_mutex, NULL);
- #endif
- }
-
-
- PYMutex::~PYMutex()
- {
- #ifdef _WIN32
- ::CloseHandle(m_mutex);
- #else
- pthread_mutex_destroy(&m_mutex);
- #endif
- }
-
-
- void PYMutex::Lock() const
- {
- #ifdef _WIN32
- WaitForSingleObject(m_mutex, INFINITE);
- #else
- pthread_mutex_lock(&m_mutex);
- #endif
- }
-
- bool PYMutex::TryLock() const
- {
- #ifdef _WIN32
- DWORD dwWaitResult = WaitForSingleObject(m_mutex, 0);
- if (dwWaitResult != WAIT_OBJECT_0 && dwWaitResult != WAIT_TIMEOUT) {
- printf("thread WARNING: bad result from try-locking mutex\n");
- }
- return (dwWaitResult == WAIT_OBJECT_0) ? true : false;
- #else
- return (0==pthread_mutex_trylock(&m_mutex))?true:false;
- #endif
- };
-
- void PYMutex::Unlock() const
- {
- #ifdef _WIN32
- ::ReleaseMutex(m_mutex);
- #else
- pthread_mutex_unlock(&m_mutex);
- #endif
- }
linux下myThread.h
- /*
- * add arg in linux system and compile: -lpthread
- *
- */
- #ifndef _MYTHREAD_H
- #define _MYTHREAD_H
-
- #include
- #include
-
- class MyThread
- {
- private:
- //current thread ID
- pthread_t tid;
- //thread status
- int threadStatus;
- //get manner pointer of execution
- static void* run0(void* pVoid);
- //manner of execution inside
- void* run1();
- public:
- //threadStatus-new create
- static const int THREAD_STATUS_NEW = 0;
- //threadStatus-running
- static const int THREAD_STATUS_RUNNING = 1;
- //threadStatus-end
- static const int THREAD_STATUS_EXIT = -1;
- // constructed function
- MyThread();
- ~MyThread();
- //the entity for thread running
- virtual int Run()=0;
- //start thread
- bool start();
- //gte thread ID
- pthread_t getThreadID();
- //get thread status
- int getState();
- //wait for thread end
- void join();
- //wait for thread end in limit time
- void join(unsigned long millisTime);
- };
-
- #endif /* _MYTHREAD_H */
linux下myThread.cpp
- #include "myThread.h"
-
- #include
-
- void* MyThread::run0(void* pVoid)
- {
- MyThread* p = (MyThread*) pVoid;
- p->run1();
- return p;
- }
-
- void* MyThread::run1()
- {
- threadStatus = THREAD_STATUS_RUNNING;
- tid = pthread_self();
- Run();
- threadStatus = THREAD_STATUS_EXIT;
- tid = 0;
- pthread_exit(NULL);
- }
-
- MyThread::MyThread()
- {
- tid = 0;
- threadStatus = THREAD_STATUS_NEW;
- }
-
- MyThread::~MyThread()
- {
- join(10);
- }
-
- int MyThread::Run()
- {
- while(true){
- printf("thread is running!\n");
- sleep(100);
- }
- return 0;
- }
-
- bool MyThread::start()
- {
- return pthread_create(&tid, NULL, run0, this) == 0;
- }
-
- pthread_t MyThread::getThreadID()
- {
- return tid;
- }
-
- int MyThread::getState()
- {
- return threadStatus;
- }
-
- void MyThread::join()
- {
- if (tid > 0)
- {
- pthread_join(tid, NULL);
- }
- }
-
- void MyThread::join(unsigned long millisTime)
- {
- if (tid == 0)
- {
- return;
- }
- if (millisTime == 0)
- {
- join();
- }else
- {
- unsigned long k = 0;
- while (threadStatus != THREAD_STATUS_EXIT && k <= millisTime)
- {
- usleep(100);
- k++;
- }
- }
- }
-
win下win32Thread.h
- #if _MSC_VER > 1000
- #pragma once
- #endif // _MSC_VER > 1000
- #ifndef WIN32THREAD_H
- #define WIN32THREAD_H
-
- #include
- #include
-
- typedef void *HANDLE;
- class MyThread
- {
- public:
- MyThread();
- ~MyThread();
- void start();
- virtual int Run();
- HANDLE getThread();
- private:
- HANDLE hThread;
- static void agent(void *p);
- };
- #endif
-
win下win32Thread.cpp
- #include "win32Thread.h"
-
- #include
-
- MyThread::MyThread()
- {
- }
-
- MyThread::~MyThread()
- {
- WaitForSingleObject(hThread, INFINITE);
- }
-
- void MyThread::start()
- {
- hThread =(HANDLE)_beginthread(agent, 0, (void *)this);
- }
- int MyThread::Run()
- {
- printf("Base Thread\n");
- return 0;
- }
- void MyThread::agent(void *p)
- {
- MyThread *agt = (MyThread *)p;
- agt->Run();
- }
- HANDLE MyThread::getThread()
- {
- return hThread;
- }
main.cpp,展示如何使用log类
- #include
- #include
-
- #ifdef WIN32
- #include
- #endif
- #include "Log.h"
-
- char LOG_FILE_NAME[128] = "pyfree_log";
- std::string logdir = "./log";
- namespace GlobalVar {
- extern std::string logname;
- };
-
- int main(int argc, char *argv[])
- {
- #ifdef WIN32
- std::string appname = std::string(argv[0]);
- GlobalVar::logname = appname.substr(0,appname.length()-4);
- #else
- GlobalVar::logname = std::string(argv[0]);
- #endif
- int i = 0;
- while(i<1)
- {
- CLogger::createInstance()->Log(eLogType(rand()%10),"log test for %d\n",i++);
- #ifdef WIN32
- Sleep(10);
- #else
- usleep(10000);
- #endif
- }
- return 0;
- }
CMakeLists.txt:
- # CMake 最低版本号要求
- cmake_minimum_required (VERSION 2.8)
- # 项目信息
- project (log_test)
- #
- if(WIN32)
- message(STATUS "windows compiling...")
- add_definitions(-D_PLATFORM_IS_WINDOWS_)
- set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT")
- set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd")
- set(WIN_OS true)
- else(WIN32)
- message(STATUS "linux compiling...")
- add_definitions( -D_PLATFORM_IS_LINUX_)
- add_definitions("-Wno-invalid-source-encoding")
- # add_definitions("-O2")
- set(UNIX_OS true)
- set(_DEBUG true)
-
- endif(WIN32)
-
- #
- set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
-
- # 指定源文件的目录,并将名称保存到变量
- SET(source_h
- #
- ${PROJECT_SOURCE_DIR}/src/Mutex.h
- ${PROJECT_SOURCE_DIR}/src/log.h
- )
-
- SET(source_cpp
- #
- ${PROJECT_SOURCE_DIR}/src/Mutex.cpp
- ${PROJECT_SOURCE_DIR}/src/log.cpp
- ${PROJECT_SOURCE_DIR}/src/main.cpp
- )
-
- #头文件目录
- include_directories(${PROJECT_SOURCE_DIR}/include)
-
- if (${UNIX_OS})
-
- SET(source_h_linux
- #
- ${PROJECT_SOURCE_DIR}/src/myThread.h
- )
-
- SET(source_cpp_linux
- ${PROJECT_SOURCE_DIR}/src/myThread.cpp
- )
-
- add_definitions(
- "-W"
- "-fPIC"
- "-Wall"
- # "-Wall -g"
- "-Werror"
- "-Wshadow"
- "-Wformat"
- "-Wpointer-arith"
- "-D_REENTRANT"
- "-D_USE_FAST_MACRO"
- "-Wno-long-long"
- "-Wuninitialized"
- "-D_POSIX_PTHREAD_SEMANTICS"
- "-DACL_PREPARE_COMPILE"
- "-Wno-unused-parameter"
- "-fexceptions"
- )
- set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0")
-
- link_directories()
- # 指定生成目标
- add_executable(log_test ${source_h} ${source_cpp} ${source_h_linux} ${source_cpp_linux})
- #link
- target_link_libraries(log_test
- -lpthread -pthread -lz -lrt -ldl
- )
-
- endif(${UNIX_OS})
-
- if (${WIN_OS})
-
- SET(source_h_win
- ${PROJECT_SOURCE_DIR}/src/win32Thread.h
- )
-
- SET(source_cpp_win
- ${PROJECT_SOURCE_DIR}/src/win32Thread.cpp
- )
-
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4819")
-
- add_definitions(
- "-D_CRT_SECURE_NO_WARNINGS"
- "-D_WINSOCK_DEPRECATED_NO_WARNINGS"
- "-DNO_WARN_MBCS_MFC_DEPRECATION"
- "-DWIN32_LEAN_AND_MEAN"
- )
-
- link_directories()
-
- if (CMAKE_BUILD_TYPE STREQUAL "Debug")
-
- set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${PROJECT_SOURCE_DIR}/bin)
- # 指定生成目标
- add_executable(log_testd ${source_h} ${source_cpp} ${source_h_win} ${source_cpp_win})
-
- else(CMAKE_BUILD_TYPE)
-
- set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${PROJECT_SOURCE_DIR}/bin)
- # 指定生成目标
- add_executable(log_test ${source_h} ${source_cpp} ${source_h_win} ${source_cpp_win})
-
- endif (CMAKE_BUILD_TYPE)
-
- endif(${WIN_OS})
-
Linux下
- cd log_test
- mkdir build_linux
- cd build_linux
- cmake ..
- make
编译及执行过程
- [py@pyfree build_linux]$ cmake ..
- -- The C compiler identification is GNU 4.8.5
- -- The CXX compiler identification is GNU 4.8.5
- -- Check for working C compiler: /usr/bin/cc
- -- Check for working C compiler: /usr/bin/cc -- works
- -- Detecting C compiler ABI info
- -- Detecting C compiler ABI info - done
- -- Detecting C compile features
- -- Detecting C compile features - done
- -- Check for working CXX compiler: /usr/bin/c++
- -- Check for working CXX compiler: /usr/bin/c++ -- works
- -- Detecting CXX compiler ABI info
- -- Detecting CXX compiler ABI info - done
- -- Detecting CXX compile features
- -- Detecting CXX compile features - done
- -- linux compiling...
- -- Configuring done
- -- Generating done
- -- Build files have been written to: /mnt/hgfs/workForMy/workspace/log_test/build_linux
- [py@pyfree build_linux]$ make
- Scanning dependencies of target log_test
- [ 20%] Building CXX object CMakeFiles/log_test.dir/src/Mutex.cpp.o
- [ 40%] Building CXX object CMakeFiles/log_test.dir/src/log.cpp.o
- [ 60%] Building CXX object CMakeFiles/log_test.dir/src/main.cpp.o
- [ 80%] Building CXX object CMakeFiles/log_test.dir/src/myThread.cpp.o
- [100%] Linking CXX executable ../bin/log_test
- [100%] Built target log_test
- [py@pyfree build_linux]$
-
- [py@pyfree bin]$ ./log_test
- mkdir: 无法创建目录"log": 文件已存在
- Log::[5153]-->log test for 0
-
- [py@pyfree bin]$
win编译时采用cmake+vs编译,具体编译版本可以依据自身电脑安装版本决定
- cd log_test && mkdir build_win && cd build_win
- cmake -G "Visual Studio 10 2010 Win64" -DCMAKE_BUILD_TYPE=Release ..
- msbuild log_test.sln /p:Configuration="Release" /p:Platform="x64"
省略编译过程,结果展示如下:

文案中已经提供完整代码信息,如果还是不知道如何组织项目代码,请这里下载: