工作几年后,发现自己从来不写博客,不是一个好习惯。往往总结的东西不记录下来,下次再来用时,总感觉疲态百出。所以吧,是该总结总结一点东西了。进入正题吧!
很多程序员不太注重日志记录,其实不是一个好的习惯。有些人觉得用DebugViewer就可以查看一些打印信息了,写日志有什么用呢? 但是,当我们发布的产品给用户使用时,出现异常时如何处理呢?难道需要远程TeamViewer去用DebugViewer查看Debug?肯定不太可能吧?这个时候,如果让客服人员跟客户拿日志还是可以实现的,日志记录了客户的一些操作手法,帮助重现bug有很重要的作用。下面提供一套QT的日志记录类,有需要的可以直接拿去用!
CLogLockWiper.h头文件如下:
// 记录log信息的类.
#ifndef _CLOGLOCKWIPER_H_
#define _CLOGLOCKWIPER_H_
#include
#include
#include
#include
#include
#include
#include
#include
/// 日志管理类:
//
//
定义为全局变量,支持多线程的记录
用法:
调用InitFolder初始化日志
调用KeepDays备份或者清空nDay前的日志
尽量保存7天的日志吧
//
//
class CLogLockWiper : public QObject
{
Q_OBJECT
public:
CLogLockWiper();
~CLogLockWiper();
bool InitFolder( const char* pszFileFolder, int nMaxFileSize=1024*500, const char* pszFileNamePrefix=NULL, const char* pszFileExt=".log"); /// 初始化日志
QString GetFileName(const char* szFileFolder, const char* szFileNamePrefix, const char* szTime, const char* szFileNameExt); /// 获取完整文件名
QString GetCurrDate(); /// 获取当前时间
bool WriteLog(QString strMsg); /// 写入文件
bool Log(QString str); /// 不带日期的记录
bool LogExt(QString str); /// 带日期的记录
void Close();
/// 注意: InitFolder接口进行初始化,必须初始化文件前缀pszFileNamePrefix
/// @param: nDays, 清空nDays之前的所有日志
/// @param: szBackupFolder, 指定清空的日志的备份目录,为空表示不备份直接删除
void KeepDays(int nDays, QString szBackupFolder);
/// 删除过期日志并备份
bool ClearLogFolder(QString szBackupFolder = QString(""));
public:
QFile m_File;
QTextStream m_logTextStream;
QMutex m_Mutex;
QString m_strFileFolder; /// 文件夹名称
QString m_strFileNamePrefix; /// 文件名
QString m_strFileNameExt; /// 文件名后缀
QString m_strFileName; /// 完成路径+文件名
QString m_strCreateDate; /// 文件创建时间
quint64 m_nMaxFileSize; /// 写入的最大字节
int m_nKeepDays; /// 保留前多少天的日志
} ;
extern CLogLockWiper g_lockWiperLog;
#endif
CLogLockWiper.cpp如下:
/// 记录log信息的类.
#include "CLogLockWiper.h"
CLogLockWiper g_lockWiperLog;
CLogLockWiper::CLogLockWiper()
{
m_strFileFolder = QString("");
m_strFileNamePrefix = QString("");
m_strFileNameExt = QString("");
m_strFileName = QString("");
m_strCreateDate = QString("");
m_nMaxFileSize = 0;
m_nKeepDays = 0;
}
CLogLockWiper::~CLogLockWiper()
{
Close();
}
void CLogLockWiper::Close()
{
bool hasOepned = m_File.isOpen();
if ( true == hasOepned )
{
m_File.flush();
m_File.close();
}
}
void CLogLockWiper::KeepDays(int nDays, QString szBackupFolder)
{
/// 没有文件前缀,无法实现功能
if (m_strFileNamePrefix.isEmpty())
return;
if (0 > m_nKeepDays)
return;
m_nKeepDays = nDays;
if (0 == m_nKeepDays) return;
LogExt(QString("Process KeepDays(%1), begin....").arg(m_nKeepDays));
/// 判断目录是否存在,不存在就创建备份目录
if (!szBackupFolder.isEmpty())
{
QDir dir(szBackupFolder);
if (false == dir.exists())
{
dir.mkpath(szBackupFolder);
}
}
ClearLogFolder(szBackupFolder);
LogExt("Process KeepDays end.");
}
bool CLogLockWiper::ClearLogFolder(QString szBackupFolder)
{
if (0 >= m_nKeepDays)
return false;
QString strFile = m_strFileFolder;
QDir dir(strFile);
QDate curDate = QDate::currentDate();
m_nKeepDays = - m_nKeepDays;
QDate startCurDate = curDate.addDays(m_nKeepDays);
//qDebug() << startCurDate.toString(QString("yyyy-MM-dd"));
QStringList strFindNameList;
strFindNameList.clear();
foreach (QFileInfo fileInfo, dir.entryInfoList(QDir::Files))
{
QString strName = fileInfo.fileName();
/// 如果是目录的话,不处理吧
if (fileInfo.isDir() || strName == QString(".") || strName == QString(".."))
{
continue;
}
/// 如果比文件名的长度小也不处理
if (strName.size() < m_strFileNamePrefix.size() + m_strCreateDate.size())
continue;
/// 如果不是日志的格式文件不处理
QString strFileStartDate = strName.mid(m_strFileNamePrefix.size() + QString("/").size(), m_strCreateDate.size());
strFileStartDate.insert(6, "-");
strFileStartDate.insert(4, "-");
QDate fileDateTime = QDate::fromString(strFileStartDate, "yyyy-MM-dd");
//qDebug() << fileDateTime.toString(QString("yyyy-MM-dd"));
if (!fileDateTime.isValid())
continue;
if (fileDateTime < startCurDate)
{
strFindNameList.append(strName);
}
}
QString strBackup(szBackupFolder);
for (int i = 0; i < strFindNameList.size(); i++)
{
QString strBackOldPath = m_strFileFolder + QString("/");
strBackOldPath += strFindNameList.at(i);
bool bSuccess = false;
if (strBackup.isEmpty())
{
bSuccess = QFile::remove(strBackOldPath);
LogExt(QString("No Backup delete outdate file(%1):(%2)").arg(strFindNameList.at(i), bSuccess == true ? QString("true") : QString("false")));
}
else
{
bSuccess = QFile::copy(strBackOldPath, strBackup + "/" + strFindNameList.at(i));
LogExt(QString("First Backup copy outdate file(%1):(%2)").arg(strFindNameList.at(i), bSuccess == true ? QString("true") : QString("false")));
if (!bSuccess)
{
QString strBackupFile;
strBackupFile = strFindNameList.at(i) + QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
bSuccess = QFile::copy(strBackOldPath, strBackup + "/" + strBackupFile);
LogExt(QString("Sencond Backup copy outdate file(%1):(%2)").arg(strFindNameList.at(i), bSuccess == true ? QString("true") : QString("false")));
bSuccess = QFile::remove(strBackOldPath);
LogExt(QString("Sencond Backup delete outdate file(%1):(%2)").arg(strFindNameList.at(i), bSuccess == true ? QString("true") : QString("false")));
}
else
{
bSuccess = QFile::remove(strBackOldPath);
LogExt(QString("First Backup delete outdate file(%1):(%2)").arg(strFindNameList.at(i), bSuccess == true ? QString("true") : QString("false")));
}
}
}
return true;
}
bool CLogLockWiper::InitFolder(const char *pszFileFolder, int nMaxFileSize, const char *pszFileNamePrefix, const char *pszFileExt)
{
if( pszFileFolder==NULL || strlen(pszFileFolder)<=0 )
return false ;
QString logPath = qApp->applicationDirPath() +"/" + QString(pszFileFolder);
QDir dir(logPath);
if (false == dir.exists())
{
dir.mkpath(logPath);
}
m_Mutex.lock();
m_strFileFolder = logPath;
m_strFileNamePrefix = QString(pszFileNamePrefix);
m_strFileNameExt = QString(pszFileExt);
m_nMaxFileSize = nMaxFileSize > 1024 ? nMaxFileSize : 1024;
m_strCreateDate = GetCurrDate();
m_strFileName = m_strFileFolder + "/" + m_strFileNamePrefix + "_" + m_strCreateDate + m_strFileNameExt;
m_Mutex.unlock();
return true ;
}
bool CLogLockWiper::WriteLog(QString strMsg)
{
m_File.setFileName(m_strFileName);
bool openFlag = m_File.open(QIODevice::Text | QIODevice::WriteOnly | QIODevice::Append);
if (false == openFlag)
{
return false;
}
QFileInfo qFileInfo(m_strFileName);
if (qFileInfo.size() > m_nMaxFileSize)
{
/// 重命名:备份
QString strTmpCurTime = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
strTmpCurTime.remove(QString("-"));
strTmpCurTime.remove(QString(" "));
strTmpCurTime.remove(QString(":"));
/// 获取文件的创建时间
QString strCreateTime = qFileInfo.created().toString("yyyy-MM-dd hh:mm:ss");
strCreateTime.remove(QString("-"));
strCreateTime.remove(QString(" "));
strCreateTime.remove(QString(":"));
m_strCreateDate = GetCurrDate();
QString strNewName = m_strFileFolder + "/" + m_strFileNamePrefix + "_" + strCreateTime + "_" + strTmpCurTime + m_strFileNameExt + ".bak";
m_File.rename(strNewName);
m_File.flush();
m_File.close();
/// 在继续写第二个文件
///
m_strFileName = m_strFileFolder + "/" + m_strFileNamePrefix + "_" + m_strCreateDate + m_strFileNameExt;
m_File.setFileName(m_strFileName);
bool openFlag = m_File.open(QIODevice::Text | QIODevice::WriteOnly | QIODevice::Append);
if (false == openFlag)
{
return false;
}
}
m_logTextStream.setDevice(&m_File);
m_logTextStream << strMsg << endl;
m_logTextStream.flush();
m_File.close();
return true;
}
QString CLogLockWiper::GetCurrDate()
{
QString strData = QString(QDateTime::currentDateTime().toString("yyyy-MM-dd"));
strData.remove(QString("-"));
return strData;
}
bool CLogLockWiper::Log(QString str)
{
QString strResult = str;
bool bWriteSuccess = false;
m_Mutex.lock();
bWriteSuccess = WriteLog(strResult);
m_Mutex.unlock();
return bWriteSuccess;
}
bool CLogLockWiper::LogExt(QString str)
{
QString date = QString(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz")) + QString(": ");
QString strResult = date + str;
bool bWriteSuccess = false;
m_Mutex.lock();
bWriteSuccess = WriteLog(strResult);
m_Mutex.unlock();
return bWriteSuccess;
}
这个日志类用法很简单,在程序启动的时候,初始化即可:
/// 初始化记录文件夹,在应用程序当前目录下
g_lockWiperLog.InitFolder("Log", 5 * 1024 * 1024, "TestLog");
/// 保留7天的记录
g_lockWiperLog.KeepDays(7, "");
/// 普通记录
g_lockWiperLog.LogExt(QString("================start TestLog================"));
/// 带参数记录
g_lockWiperLog.LogExt(QString("================param1:%1,param2:%2================").arg(param1).arg(param2));
日志记录的效果:
日志记录的优点:
1.可以按每天的日期记录,带时间和不带时间的记录日志。
2.有清理日志的功能,例如,保留7天,3天等。
3.可以备份日志,文件后缀结尾是.bak。
日志的缺点:没有使用单实例来打印。