c++ 日志操作

时间:2023-03-09 23:05:56
c++ 日志操作

程序需要一个简单的日志类,为此简单学习了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, '\0');
DWORD dwLength = GetCurrentDirectoryA(s.size(), &s[0]);
return s.substr(0, dwLength);
} template<>
std::wstring GetCurrentDir<std::wstring>()
{
std::wstring s(256, L'\0');
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\t", 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 + "\t" + 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 + "\t" + 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 + "\t" + 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\t", 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] << "\t" << pszMessage;
}
} //添加信息日志
void CTinyLogM<char>::Info(const char* pszMessage)
{
if (m_emLogLevel == LogLevelInfo ||
m_emLogLevel == LogLevelDebug)
{
*this << pszLogLevelString[m_emLogLevel] << "\t" << pszMessage;
}
} //添加错误日志
void CTinyLogM<char>::Error(const char* pszMessage)
{
if (m_emLogLevel == LogLevelError ||
m_emLogLevel == LogLevelDebug)
{
*this << pszLogLevelString[m_emLogLevel] << "\t" << 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();