Windows 平台下轻量级日志记录
最近接手一个需求,编译环境为c++ 98,
我是开发一小块独立功能,日志还要自己打
头文件 Logger.h
#ifndef LOGGER_H_ #define LOGGER_H_ #include <Windows.h> #include <stdio.h> #include <time.h> #include <functional> #pragma warning(disable:4996) #pragma warning(disable:4482) /* * 类名:Logger * 作用:提供写日志功能,支持多线程,支持可变形参数操作,支持写日志级别的设置 * 接口:SetLogLevel:设置写日志级别 TraceKeyInfo:忽略日志级别,写关键信息 TraceError:写错误信息 TraceWarning:写警告信息 TraceInfo:写一般信息 *使用: #include "Common/Logger/Logger.h" Logger log; log.SetLogLevel(LogLevelAll); log.TraceInfo("Ready Go"); */ //日志级别的提示信息 static const char * KEYINFOPREFIX = " [Key]:"; static const char * ERRORPREFIX = " [Error]:"; static const char * WARNINGPREFIX = " [Warning]:"; static const char * INFOPREFIX = " [Info]:"; static const int MAX_STR_LEN = 20480; //日志级别枚举 enum EnumLogLevel { LogLevelAll = 0, //所有信息都写日志 LogLevelMid, //写错误、警告信息 LogLevelNormal, //只写错误信息 LogLevelStop //不写日志 }; class Logger { public: //默认构造函数 Logger(); //构造函数 Logger(const char * strLogPath, EnumLogLevel nLogLevel = EnumLogLevel::LogLevelNormal); //析构函数 virtual ~Logger(); public: //写关键信息 void TraceKeyInfo(const char * strInfo, ...); //写错误信息 void TraceError(const char* strInfo, ...); //写警告信息 void TraceWarning(const char * strInfo, ...); //写一般信息 void TraceInfo(const char * strInfo, ...); //设置写日志级别 void SetLogLevel(EnumLogLevel nLevel); //是否设置控制台输出 void SetConsoleOutput(bool flag){ m_isStandOut = flag; } private: //写文件操作 void Trace(const char * strInfo); //获取当前系统时间 char * GetCurrentTime(); //创建日志文件名称 void GenerateLogName(); //创建日志路径 void CreateLogPath(); private: //是否同步控制台输出 bool m_isStandOut; //日志切割的时间 tm m_pTimeInfo; //写日志文件流 FILE * m_pFileStream; //写日志级别 EnumLogLevel m_nLogLevel; //日志的路径 char m_strLogPath[MAX_STR_LEN]; //日志的名称 char m_strCurLogName[MAX_STR_LEN]; //线程同步的临界区变量 CRITICAL_SECTION m_cs; }; #endif
实现文件 Logger.cpp
#include "Logger.h" #include <time.h> #include <string.h> #include <stdarg.h> #include <imagehlp.h> #include <stdio.h> #include <direct.h> #pragma comment(lib, "DbgHelp.lib") Logger AILog; #include <iostream> //默认构造函数 Logger::Logger() { //初始化 memset(m_strLogPath, 0, MAX_STR_LEN); memset(m_strCurLogName, 0, MAX_STR_LEN); m_pFileStream = NULL; //设置默认的写日志级别 m_nLogLevel = EnumLogLevel::LogLevelNormal; //初始化临界区变量 InitializeCriticalSection(&m_cs); //创建日志文件名 CreateLogPath(); GenerateLogName(); SetLogLevel(LogLevelAll); m_isStandOut = false; } //构造函数 Logger::Logger(const char * strLogPath, EnumLogLevel nLogLevel) :m_nLogLevel(nLogLevel) { //初始化 m_pFileStream = NULL; strcpy(m_strLogPath, strLogPath); InitializeCriticalSection(&m_cs); CreateLogPath(); GenerateLogName(); SetLogLevel(LogLevelAll); m_isStandOut = false; } //析构函数 Logger::~Logger() { //释放临界区 DeleteCriticalSection(&m_cs); //关闭文件流 if (m_pFileStream) fclose(m_pFileStream); } //写关键信息接口 void Logger::TraceKeyInfo(const char * strInfo, ...) { if (!strInfo) return; char pTemp[MAX_STR_LEN] = { 0 }; char *pTime = GetCurrentTime(); strcpy(pTemp, pTime); strcat(pTemp, KEYINFOPREFIX); //获取可变形参 va_list arg_ptr = NULL; va_start(arg_ptr, strInfo); vsprintf(pTemp + strlen(pTemp), strInfo, arg_ptr); va_end(arg_ptr); //写日志文件 Trace(pTemp); arg_ptr = NULL; delete pTime; } //写错误信息 void Logger::TraceError(const char* strInfo, ...) { //判断当前的写日志级别,若设置为不写日志则函数返回 if (m_nLogLevel >= EnumLogLevel::LogLevelStop) return; if (!strInfo) return; char pTemp[MAX_STR_LEN] = { 0 }; char *pTime = GetCurrentTime(); strcpy(pTemp, pTime); strcat(pTemp, ERRORPREFIX); va_list arg_ptr = NULL; va_start(arg_ptr, strInfo); vsprintf(pTemp + strlen(pTemp), strInfo, arg_ptr); va_end(arg_ptr); Trace(pTemp); arg_ptr = NULL; delete pTime; } //写警告信息 void Logger::TraceWarning(const char * strInfo, ...) { //判断当前的写日志级别,若设置为只写错误信息则函数返回 if (m_nLogLevel >= EnumLogLevel::LogLevelNormal) return; if (!strInfo) return; char pTemp[MAX_STR_LEN] = { 0 }; char *pTime = GetCurrentTime(); strcpy(pTemp, pTime); strcat(pTemp, WARNINGPREFIX); va_list arg_ptr = NULL; va_start(arg_ptr, strInfo); vsprintf(pTemp + strlen(pTemp), strInfo, arg_ptr); va_end(arg_ptr); Trace(pTemp); arg_ptr = NULL; delete pTime; } //写一般信息 void Logger::TraceInfo(const char * strInfo, ...) { //判断当前的写日志级别,若设置只写错误和警告信息则函数返回 if (m_nLogLevel >= EnumLogLevel::LogLevelMid) return; if (!strInfo) return; char pTemp[MAX_STR_LEN] = { 0 }; char *pTime = GetCurrentTime(); strcpy(pTemp, pTime); strcat(pTemp, INFOPREFIX); va_list arg_ptr = NULL; va_start(arg_ptr, strInfo); vsprintf(pTemp + strlen(pTemp), strInfo, arg_ptr); va_end(arg_ptr); Trace(pTemp); arg_ptr = NULL; delete pTime; } //获取系统当前时间 char * Logger::GetCurrentTime() { time_t curTime; struct tm * pTimeInfo = NULL; time(&curTime); pTimeInfo = localtime(&curTime); char *temp = new char[MAX_STR_LEN]; sprintf(temp, "%04d-%02d-%02d %02d:%02d:%02d", pTimeInfo->tm_year + 1990, pTimeInfo->tm_mon + 1, pTimeInfo->tm_mday, pTimeInfo->tm_hour, pTimeInfo->tm_min, pTimeInfo->tm_sec); char * pTemp = temp; return pTemp; } //设置写日志级别 void Logger::SetLogLevel(EnumLogLevel nLevel) { m_nLogLevel = nLevel; } //写文件操作 void Logger::Trace(const char * strInfo) { if (!strInfo) return; try { //进入临界区 EnterCriticalSection(&m_cs); time_t curTime; struct tm * pTimeInfo = NULL; time(&curTime); pTimeInfo = localtime(&curTime); if ( pTimeInfo->tm_year != m_pTimeInfo.tm_year || pTimeInfo->tm_mon != m_pTimeInfo.tm_mon || pTimeInfo->tm_mday != m_pTimeInfo.tm_mday // || pTimeInfo->tm_hour != m_pTimeInfo.tm_hour // || pTimeInfo->tm_min != m_pTimeInfo.tm_min ){ //std::cout << "日志需要切割了" << std::endl; GenerateLogName(); } //若文件流没有打开,则重新打开 if (!m_pFileStream) { char temp[1024] = { 0 }; strcat(temp, m_strLogPath); strcat(temp, m_strCurLogName); m_pFileStream = fopen(temp, "a+"); if (!m_pFileStream) { return; } } //写日志信息到文件流 fprintf(m_pFileStream, "%s\n", strInfo); fflush(m_pFileStream); //写日志到控制台 if (m_isStandOut) { printf("%s\n",strInfo); } //离开临界区 LeaveCriticalSection(&m_cs); } //若发生异常,则先离开临界区,防止死锁 catch (...) { LeaveCriticalSection(&m_cs); } } //创建日志文件的名称 void Logger::GenerateLogName() { time_t curTime; struct tm * pTimeInfo = NULL; time(&curTime); pTimeInfo = localtime(&curTime); char temp[1024] = { 0 }; //日志的名称如:2013-01-01.log sprintf(temp, "%04d-%02d-%02d-%02d_AI.log", (pTimeInfo->tm_year + 1900), (pTimeInfo->tm_mon + 1), pTimeInfo->tm_mday, pTimeInfo->tm_hour); if (0 != strcmp(m_strCurLogName, temp)) { strcpy(m_strCurLogName, temp); if (m_pFileStream) { fflush(m_pFileStream); fclose(m_pFileStream); } char newTemp[1024] = { 0 }; strcat(newTemp, m_strLogPath); strcat(newTemp, m_strCurLogName); //以追加的方式打开文件流 m_pFileStream = fopen(newTemp, "a+"); } m_pTimeInfo = *(pTimeInfo); //std::cout << m_pTimeInfo.tm_year << "-" << m_pTimeInfo.tm_mon << "-" << m_pTimeInfo.tm_mday << " " << m_pTimeInfo.tm_hour << ":" << m_pTimeInfo.tm_min << std::endl; } //创建日志文件的路径 void Logger::CreateLogPath() { //if (0 != strlen(m_strLogPath)) //{ // strcat(m_strLogPath, "\\"); //} //MakeSureDirectoryPathExists(m_strLogPath); memset(m_strLogPath, 0, sizeof(m_strLogPath)); char *buffer; //也可以将buffer作为输出参数 if ((buffer = getcwd(NULL, 0)) == NULL) { strcat(m_strLogPath, "\\"); } else { strcat(m_strLogPath, buffer); strcat(m_strLogPath, "\\LOGAI\\"); free(buffer); } MakeSureDirectoryPathExists(m_strLogPath); }
简单测试一下
#include <iostream> #include "Logger.h" using namespace std; extern Logger AILog;//使用已经生成对象,也可以构建自己的对象 int main(int argc, char** argv){ AILog.SetConsoleOutput(false);//控制是否控制台也同步输出
log.TraceInfo("Ready Go");//打印一行日志试一试
system("pause"); return 0; }

浙公网安备 33010602011771号