程序需要一个简单的日志类,为此简单学习了Boost.Log和google的glog,前者功能非常强大,后者非常小巧但是不够灵活,最终打算自己写一个。
环境:
win7 32位旗舰版、VS2010旗舰版
TinyLog.h文件
#ifndef _TINY_LOG_H_ #define _TINY_LOG_H_ #include <fstream> #include <list> #include <string> #define _WINDOWS namespace TinyLogHelper { template<typename T> T GetCurrentDir() { } #ifdef _WINDOWS #include <windows.h> ////////////////////////////////////////////////////////////////////////// // 线程互斥锁 ////////////////////////////////////////////////////////////////////////// class CLock { public: CLock() { InitializeCriticalSection(&m_cs);//初始化临界区 } ~CLock() { DeleteCriticalSection(&m_cs);//删除临界区 } void Lock() { EnterCriticalSection(&m_cs);//加锁 } void UnLock() { LeaveCriticalSection(&m_cs);//解锁 } private: CRITICAL_SECTION m_cs; }; ////////////////////////////////////////////////////////////////////////// // 函数功能: // 获取当前目录 ////////////////////////////////////////////////////////////////////////// template<> std::string GetCurrentDir<std::string>(); template<> std::wstring GetCurrentDir<std::wstring>(); #endif } namespace TinyLog { ////////////////////////////////////////////////////////////////////////// // CTinyLog类修改记录 // 2014-5-24 // 使用m_nLogBufferLength记录当前日志缓存的大小,而不使用GetLogBufferSize // 函数将整个日志缓存遍历一次来获取,提高运行速度。 ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// // 日志等级 // LogLevelDebug等级,会输出所有日志信息 // LogLevelInfo等级,会输出INFO日志信息 // LogLevelError等级,会输出ERROR日志信息 ////////////////////////////////////////////////////////////////////////// enum LogLevel { LogLevelDebug,//调试 LogLevelInfo,//信息 LogLevelError,//错误 }; const static char* pszLogLevelString[3] = { "Debug", "Info", "Error" }; ////////////////////////////////////////////////////////////////////////// // 类说明: // 单线程日志类,应该只在一个线程中操作日志 ////////////////////////////////////////////////////////////////////////// template<typename T> class CTinyLog { }; template<> class CTinyLog<char> { public: CTinyLog(); ~CTinyLog(); public: //添加日志记录 CTinyLog& operator <<(const char* pszMessage); //添加调试日志 void Debug(const char* pszMessage); //添加信息日志 void Info(const char* pszMessage); //添加错误日志 void Error(const char* pszMessage); //将日志缓存写入到文件中 CTinyLog& WriteToFile(); //得到第一个写入文件路径 std::string GetFirstWriteFilePath() const; //得到下一个写入文件路径 std::string GetNextWriteFilePath() const; //得到文件路径格式 std::string GetCurrentFilePathFormat() const; public://属性 //设置文件 bool SetFileDir(const std::string& strFileDir); //设置文件扩展名 bool SetFillExtendName(const std::string& strFileExtendName); //设置文件名称格式 bool SetFileNameFormat(const std::string& strFileNameFormat); //设置最大文件大小(字节) void SetMaxFileSize(std::size_t size); //设置最大写入文件大小(字节) void SetMaxWriteFileSize(std::size_t size); //设置最大文件个数 void SetMaxFileCount(std::size_t count); //设置日志等级 void SetLogLevel(LogLevel emLogLevel); private: //日志缓存 std::list<std::string> m_lstLogBuffer; std::string m_strFileDir;//文件目录 std::string m_strFileExtendName;//文件扩展名 std::string m_strFileNameFormat;//文件名格式 std::size_t m_nMaxFileSize;//最大文件大小(字节) std::size_t m_nMaxWriteFileSize;//最大写入文件大小(字节) mutable std::size_t m_nFileIndex;//文件索引 std::size_t m_nFileCount;//文件最大个数 LogLevel m_emLogLevel;//日志等级 std::size_t m_nLogBufferLength;//日志缓存长度(字节) }; template<> class CTinyLog<wchar_t> { }; ////////////////////////////////////////////////////////////////////////// // 类说明: // 多线程日志类,可以在多个线程中操作日志 ////////////////////////////////////////////////////////////////////////// template<typename T> class CTinyLogM { }; template<> class CTinyLogM<char> { public: CTinyLogM(); ~CTinyLogM(); public: //添加日志记录 CTinyLogM& operator <<(const char* pszMessage); //添加调试日志 void Debug(const char* pszMessage); //添加信息日志 void Info(const char* pszMessage); //添加错误日志 void Error(const char* pszMessage); //将日志缓存写入到文件中 CTinyLogM& WriteToFile(); //得到第一个写入文件路径 std::string GetFirstWriteFilePath() const; //得到下一个写入文件路径 std::string GetNextWriteFilePath() const; //得到文件路径格式 std::string GetCurrentFilePathFormat() const; public://属性 //设置文件目录 bool SetFileDir(const std::string& strFileDir); //设置文件扩展名 bool SetFillExtendName(const std::string& strFileExtendName); //设置文件名称格式 bool SetFileNameFormat(const std::string& strFileNameFormat); //设置最大文件大小(字节) void SetMaxFileSize(std::size_t size); //设置最大写入文件大小(字节) void SetMaxWriteFileSize(std::size_t size); //设置最大文件个数 void SetMaxFileCount(std::size_t count); //设置日志等级 void SetLogLevel(LogLevel emLogLevel); private: //日志缓存 std::list<std::string> m_lstLogBuffer; std::string m_strFileDir;//文件目录 std::string m_strFileExtendName;//文件扩展名 std::string m_strFileNameFormat;//文件名格式 std::size_t m_nMaxFileSize;//最大文件大小(字节) std::size_t m_nMaxWriteFileSize;//最大写入文件大小(字节) mutable std::size_t m_nFileIndex;//文件索引 std::size_t m_nFileCount;//文件最大个数 //线程互斥锁 mutable TinyLogHelper::CLock m_lock; LogLevel m_emLogLevel;//日志等级 std::size_t m_nLogBufferLength;//日志缓存长度(字节) }; template<> class CTinyLogM<wchar_t> { }; } #endif
TinyLog.cpp文件
#include <time.h> #include "TinyLog.h" namespace TinyLogHelper { #ifdef _WINDOWS ////////////////////////////////////////////////////////////////////////// // 函数功能: // 获取当前目录 ////////////////////////////////////////////////////////////////////////// template<> std::string GetCurrentDir<std::string>() { std::string s(256, ' '); DWORD dwLength = GetCurrentDirectoryA(s.size(), &s[0]); return s.substr(0, dwLength); } template<> std::wstring GetCurrentDir<std::wstring>() { std::wstring s(256, L' '); DWORD dwLength = GetCurrentDirectoryW(s.size(), &s[0]); return s.substr(0, dwLength); } #endif } namespace TinyLog { CTinyLog<char>::CTinyLog() { m_strFileDir = TinyLogHelper::GetCurrentDir<std::string>(); m_strFileDir += "/"; SetFillExtendName(".log"); SetFileNameFormat("_%d"); SetMaxFileSize(1 * 1024 * 1024);//1 MB SetMaxWriteFileSize(4 * 1024);//4 KB SetMaxFileCount(10); m_nFileIndex = 1; m_emLogLevel = LogLevelInfo; m_nLogBufferLength = 0; } CTinyLog<char>::~CTinyLog() { WriteToFile(); } //添加日志记录 CTinyLog<char>& CTinyLog<char>::operator <<(const char* pszMessage) { time_t timet; struct tm* pTime = NULL; char szTime[256] = {0}; int nCharNum = 0; //得到当前时间 time(&timet); pTime = localtime(&timet); nCharNum = sprintf(szTime, "%02d-%02d-%02d ", pTime->tm_hour, pTime->tm_min, pTime->tm_sec); if (nCharNum > 0) { std::string s; s = s + szTime + pszMessage; m_lstLogBuffer.push_back(s);//添加记录 m_nLogBufferLength += (s.size() + 2); } //写入缓存日志到文件 if (m_nLogBufferLength > m_nMaxWriteFileSize) { WriteToFile(); } return *this; } //添加调试日志 void CTinyLog<char>::Debug(const char* pszMessage) { if (m_emLogLevel == LogLevelDebug) { std::string s = pszLogLevelString[m_emLogLevel]; s = s + " " + pszMessage; *this << s.c_str(); } } //添加信息日志 void CTinyLog<char>::Info(const char* pszMessage) { if (m_emLogLevel == LogLevelInfo || m_emLogLevel == LogLevelDebug) { std::string s = pszLogLevelString[m_emLogLevel]; s = s + " " + pszMessage; *this << s.c_str(); } } //添加错误日志 void CTinyLog<char>::Error(const char* pszMessage) { if (m_emLogLevel == LogLevelError || m_emLogLevel == LogLevelDebug) { std::string s = pszLogLevelString[m_emLogLevel]; s = s + " " + pszMessage; *this << s.c_str(); } } //将日志缓存写入到文件中 CTinyLog<char>& CTinyLog<char>::WriteToFile() { bool bRet = false; try { std::string path; std::ofstream ofs; //打开第一个写入的文件 path = GetFirstWriteFilePath(); if (path.empty()) { bRet = false; throw bRet; } ofs.open(path, std::ios::app); if (!ofs.is_open()) { bRet = false; throw bRet; } std::size_t nFileSize = 0; std::list<std::string>::iterator beg, end; beg = m_lstLogBuffer.begin(); end = m_lstLogBuffer.end(); while (beg != end) { ofs.seekp(0, std::ios::end); nFileSize = ofs.tellp(); if (nFileSize + beg->size() + 2 < m_nMaxFileSize) { ofs << *beg << std::endl;//回车换行 m_nLogBufferLength -= (beg->size() + 2); beg = m_lstLogBuffer.erase(beg); } else { //打开下一个写入的文件 path = GetNextWriteFilePath(); if (path.empty()) { bRet = false; throw bRet; } if (ofs.is_open()) { ofs.close(); } ofs.open(path, std::ios::app); if (!ofs.is_open()) { bRet = false; throw bRet; } } } bRet = true; } catch (bool) { } return *this; } //得到第一个写入文件路径 std::string CTinyLog<char>::GetFirstWriteFilePath() const { std::string path; try { std::string strPathFromat = GetCurrentFilePathFormat(); std::size_t nPos = strPathFromat.rfind("%d"); if (nPos == strPathFromat.npos) { path = ""; throw path; } std::string strTempPath; char szNumber[32] = {0}; std::ifstream file; for (std::size_t i = m_nFileIndex; i < m_nFileCount + 1; ++i) { memset(szNumber, 0, 32); itoa(i, szNumber, 10); strTempPath = strPathFromat; strTempPath.replace(nPos, 2, szNumber); file.open(strTempPath, std::ios::app); if (file.is_open()) { file.seekg(0, std::ios::end); if (file.tellg() < m_nMaxFileSize) { m_nFileIndex = i; path = strTempPath; break; } } else { m_nFileIndex = i; path = strTempPath; break; } } } catch (const std::string&) { } return path; } //得到下一个写入文件路径 std::string CTinyLog<char>::GetNextWriteFilePath() const { std::string path; std::string strFilePathFormat; std::size_t nPos = 0; char szNum[32] = {0}; if (m_nFileIndex > m_nFileCount-1) { return path; } strFilePathFormat = GetCurrentFilePathFormat(); nPos = strFilePathFormat.rfind("%d"); if (nPos != strFilePathFormat.npos) { ++m_nFileIndex; itoa(m_nFileIndex, szNum, 10); path = strFilePathFormat; path.replace(nPos, 2, szNum); } return path; } //得到文件路径格式 std::string CTinyLog<char>::GetCurrentFilePathFormat() const { //文件路径格式 std::string strFilePathFormat; try { if (m_strFileDir.empty() || m_strFileNameFormat.empty() || m_strFileExtendName.empty()) { strFilePathFormat = ""; throw strFilePathFormat; } //获取本地时间 time_t timet; struct tm* pTime = NULL; char szTime[256] = {0}; time(&timet); pTime = localtime(&timet); sprintf(szTime, "%d-%02d-%02d", 1900 + pTime->tm_year, 1 + pTime->tm_mon, pTime->tm_mday); strFilePathFormat = m_strFileDir + szTime + m_strFileNameFormat + m_strFileExtendName; } catch (const std::string&) { } return strFilePathFormat; } //设置文件目录 bool CTinyLog<char>::SetFileDir(const std::string& strFileDir) { if (!strFileDir.empty() && (strFileDir.back() == '\' || strFileDir.back() == '/')) { m_strFileDir = strFileDir; return true; } return false; } //设置文件扩展名 bool CTinyLog<char>::SetFillExtendName(const std::string& strFileExtendName) { if (strFileExtendName.size() > 1 && strFileExtendName.front() == '.') { m_strFileExtendName = strFileExtendName; return true; } return false; } //设置文件名称格式 bool CTinyLog<char>::SetFileNameFormat(const std::string& strFileNameFormat) { if (strFileNameFormat.size() > 1 && strFileNameFormat.find("%d") != strFileNameFormat.npos) { m_strFileNameFormat = strFileNameFormat; return true; } return false; } //设置最大文件大小(字节) void CTinyLog<char>::SetMaxFileSize(std::size_t size) { m_nMaxFileSize = size; } //设置最大写入文件大小(字节) void CTinyLog<char>::SetMaxWriteFileSize(std::size_t size) { m_nMaxWriteFileSize = size; } //设置最大文件个数 void CTinyLog<char>::SetMaxFileCount(std::size_t count) { m_nFileCount = count; } //设置日志等级 void CTinyLog<char>::SetLogLevel(LogLevel emLogLevel) { m_emLogLevel = emLogLevel; } ////////////////////////////////////////////////////////////////////////// // 类说明: // 多线程日志类,可以在多个线程中操作日志 ////////////////////////////////////////////////////////////////////////// CTinyLogM<char>::CTinyLogM() { m_strFileDir = TinyLogHelper::GetCurrentDir<std::string>(); m_strFileDir += "/"; SetFillExtendName(".log"); SetFileNameFormat("_%d"); SetMaxFileSize(1 * 1024 * 1024);//1 MB SetMaxWriteFileSize(4 * 1024);//4 KB SetMaxFileCount(10); m_nFileIndex = 1; m_emLogLevel = LogLevelInfo; m_nLogBufferLength = 0; } CTinyLogM<char>::~CTinyLogM() { WriteToFile(); } //添加日志记录 CTinyLogM<char>& CTinyLogM<char>::operator <<(const char* pszMessage) { time_t timet; struct tm* pTime = NULL; char szTime[256] = {0}; int nCharNum = 0; time(&timet); pTime = localtime(&timet); nCharNum = sprintf(szTime, "%02d-%02d-%02d ", pTime->tm_hour, pTime->tm_min, pTime->tm_sec); if (nCharNum > 0) { std::string s; s = s + szTime + pszMessage; m_lock.Lock(); m_lstLogBuffer.push_back(s); m_nLogBufferLength += (s.size() + 2); m_lock.UnLock(); } if (m_nLogBufferLength > m_nMaxWriteFileSize) { WriteToFile(); } return *this; } //添加调试日志 void CTinyLogM<char>::Debug(const char* pszMessage) { if (m_emLogLevel == LogLevelDebug) { *this << pszLogLevelString[m_emLogLevel] << " " << pszMessage; } } //添加信息日志 void CTinyLogM<char>::Info(const char* pszMessage) { if (m_emLogLevel == LogLevelInfo || m_emLogLevel == LogLevelDebug) { *this << pszLogLevelString[m_emLogLevel] << " " << pszMessage; } } //添加错误日志 void CTinyLogM<char>::Error(const char* pszMessage) { if (m_emLogLevel == LogLevelError || m_emLogLevel == LogLevelDebug) { *this << pszLogLevelString[m_emLogLevel] << " " << pszMessage; } } //将日志缓存写入到文件中 CTinyLogM<char>& CTinyLogM<char>::WriteToFile() { bool bRet = false; try { std::string path; std::ofstream ofs; //打开第一个写入的文件 path = GetFirstWriteFilePath(); if (path.empty()) { bRet = false; throw bRet; } m_lock.Lock(); ofs.open(path, std::ios::app); if (!ofs.is_open()) { bRet = false; throw bRet; } std::size_t nFileSize = 0; std::list<std::string>::iterator beg, end; beg = m_lstLogBuffer.begin(); end = m_lstLogBuffer.end(); while (beg != end) { ofs.seekp(0, std::ios::end); nFileSize = ofs.tellp(); if (nFileSize + beg->size() + 2 < m_nMaxFileSize) { ofs << *beg << std::endl;//回车换行 m_nLogBufferLength -= (beg->size() + 2); beg = m_lstLogBuffer.erase(beg); } else { //打开下一个写入的文件 path = GetNextWriteFilePath(); if (path.empty()) { bRet = false; throw bRet; } if (ofs.is_open()) { ofs.close(); } ofs.open(path, std::ios::app); if (!ofs.is_open()) { bRet = false; throw bRet; } } } bRet = true; } catch (bool) { } m_lock.UnLock(); return *this; } //得到第一个写入文件路径 std::string CTinyLogM<char>::GetFirstWriteFilePath() const { std::string path; try { std::string strPathFromat = GetCurrentFilePathFormat(); std::size_t nPos = strPathFromat.rfind("%d"); if (nPos == strPathFromat.npos) { path = ""; throw path; } std::string strTempPath; char szNumber[32] = {0}; std::ifstream file; m_lock.Lock(); for (std::size_t i = m_nFileIndex; i < m_nFileCount + 1; ++i) { memset(szNumber, 0, 32); itoa(i, szNumber, 10); strTempPath = strPathFromat; strTempPath.replace(nPos, 2, szNumber); file.open(strTempPath, std::ios::app); if (file.is_open()) { file.seekg(0, std::ios::end); if (file.tellg() < m_nMaxFileSize) { m_nFileIndex = i; path = strTempPath; break; } } else { m_nFileIndex = i; path = strTempPath; break; } } m_lock.UnLock(); } catch (const std::string&) { } return path; } //得到下一个写入文件路径 std::string CTinyLogM<char>::GetNextWriteFilePath() const { std::string path; std::string strFilePathFormat; std::size_t nPos = 0; char szNum[32] = {0}; if (m_nFileIndex > m_nFileCount-1) { return path; } strFilePathFormat = GetCurrentFilePathFormat(); nPos = strFilePathFormat.rfind("%d"); if (nPos != strFilePathFormat.npos) { m_lock.Lock(); ++m_nFileIndex; m_lock.UnLock(); itoa(m_nFileIndex, szNum, 10); path = strFilePathFormat; path.replace(nPos, 2, szNum); } return path; } //得到文件路径格式 std::string CTinyLogM<char>::GetCurrentFilePathFormat() const { //文件路径格式 std::string strFilePathFormat; try { if (m_strFileDir.empty() || m_strFileNameFormat.empty() || m_strFileExtendName.empty()) { strFilePathFormat = ""; throw strFilePathFormat; } //获取本地时间 time_t timet; struct tm* pTime = NULL; char szTime[256] = {0}; time(&timet); pTime = localtime(&timet); sprintf(szTime, "%d-%02d-%02d", 1900 + pTime->tm_year, 1 + pTime->tm_mon, pTime->tm_mday); strFilePathFormat = m_strFileDir + szTime + m_strFileNameFormat + m_strFileExtendName; } catch (const std::string&) { } return strFilePathFormat; } //设置文件目录 bool CTinyLogM<char>::SetFileDir(const std::string& strFileDir) { if (!strFileDir.empty() && (strFileDir.back() == '\' || strFileDir.back() == '/')) { m_lock.Lock(); m_strFileDir = strFileDir; m_lock.UnLock(); return true; } return false; } //设置文件扩展名 bool CTinyLogM<char>::SetFillExtendName(const std::string& strFileExtendName) { if (strFileExtendName.size() > 1 && strFileExtendName.front() == '.') { m_lock.Lock(); m_strFileExtendName = strFileExtendName; m_lock.UnLock(); return true; } return false; } //设置文件名称格式 bool CTinyLogM<char>::SetFileNameFormat(const std::string& strFileNameFormat) { if (strFileNameFormat.size() > 1 && strFileNameFormat.find("%d") != strFileNameFormat.npos) { m_lock.Lock(); m_strFileNameFormat = strFileNameFormat; m_lock.UnLock(); return true; } return false; } //设置最大文件大小(字节) void CTinyLogM<char>::SetMaxFileSize(std::size_t size) { m_lock.Lock(); m_nMaxFileSize = size; m_lock.UnLock(); } //设置最大写入文件大小(字节) void CTinyLogM<char>::SetMaxWriteFileSize(std::size_t size) { m_lock.Lock(); m_nMaxWriteFileSize = size; m_lock.UnLock(); } //设置最大文件个数 void CTinyLogM<char>::SetMaxFileCount(std::size_t count) { m_nFileCount = count; } //设置日志等级 void CTinyLogM<char>::SetLogLevel(LogLevel emLogLevel) { m_emLogLevel = emLogLevel; } }
测试:
TinyLog::CTinyLog<char> log; log.SetMaxFileSize(1 * 1024 * 1024); log.SetMaxWriteFileSize(1 * 1024 * 1024); log.SetLogLevel(TinyLog::LogLevelError); for (int i = 0; i < 60000; ++i) { log.Error("Hello Word!"); } log.WriteToFile();