本文章实现log打印是单独写的一个Log类,当然也可以封装成库,那样复用性更好,下面是部分代码示例:
1、声明
#ifndef LOG_H
#define LOG_H
#include "ComMacroDef.h"
#include <QString>
#include <QDate>
#include <QFile>
#include <QThread>
#include <QQueue>
#include <QUdpSocket>
// 日志内容格式,日志按天存放,每天生成一个新的文件,文件名以日期命名:2016-05-31.log
// 16:55:00.001 ModuleName@Test.cpp(12)$info:messge
// 16:55:00.001 ModuleName@Test.cpp(12)$warning:messge
// 16:55:00.001 ModuleName@Test.cpp(12)$error:messge
// 日志类型
enum LogType
{
LT_DEBUG,
LT_WARNING,
LT_ERROR
};
static const QString STR_LOG_TYPE[] =
{
"debug",
"warning",
"error"
};
#define INST_CLog CLog::Instance()
class CLog : public QThread
{
PATTERN_SINGLETON_DECLARE(CLog)
public:
~CLog();
// 初始化日志
bool InitLog(QString& strLogPath, int nLatestFileCount, QString strFileNamePrefix = QString());
// 设置日志保留最近天数
void SetLatestFileCount(int nLatestFileCount);
// 设置日志最大容量,单位:MB
void SetMaxLogSize(__int64 nMaxSizeMB);
// 设置连接日志实时显示界面程序IP和端口
void SetRealDisplayIPAndPort(bool bEnable, QString strIP, int nPort);
// 设置单个日志存储方式
void SetSingleLogSaveMode(bool bEnaSingleLogFileSize, int nSingleLogFileSizeMB);
// 反初始化日志
void UninitLog();
// 添加日志
bool AddLog(const QString& strMsg, LogType eLogType, QString strModuleName, QString strCppName, int nLineNo);
protected:
CLog();
void run();
// 检测是否需要创建新的日志文件,每天一个日志文件
void CreateNewLogFileCheck();
// 检测保留最近的文件个数,超出预设个数,删除最早的日志文件
void CheckLatestFileCount();
// 检测日志文件总大小
void CheckLogSize();
// 计算日志文件总大小
__int64 CalcAllLogSize(QString strLogPath);
// 根据日志容量删除旧的日志,按日期文件夹删除
__int64 DeleteOldLogByMaxSize(int nNeedDelSize);
// 删除一天的日志,返回日志总大小
__int64 DeleteOneDayLogs(QString strLogFolder);
// 检测单个文件是否需要分割
void CheckSplitFile();
private:
QMutex m_lock; // 队列锁
QQueue<QString> m_queData; // 日志队列
volatile bool m_bThreadRun;
QDate m_dateCurFile; // 当前日志文件使用的日期
QFile m_fileLog; // 写日志文件对象
QString m_strLogPath; // 存放日志文件路径
QString m_strFileNamePrefix; // 日志文件名称前缀
int m_nLatestFileCount; // 保留最近多少个日志文件
__int64 m_nMaxSizeMB; // 日志最大容量,单位:MB
__int64 m_nAllHisLogSize; // 当前日志总大小,单位:B
QString m_strIP;
int m_nPort;
bool m_bRealDisplay; // 显示日志到界面
int m_nCurDayIndex; // 当前文件索引,01 02 03...
bool m_bEnaSingleLogFileSize;
int m_nSingleLogFileSize;
};
#endif // LOG_H
2、实现
#include "stdafx.h"
#include "Log.h"
#include <QDir>
#include <QDebug>
#include <QCoreApplication>
PATTERN_SINGLETON_IMPLEMENT(CLog)
CLog::CLog()
{
m_nLatestFileCount = 7;
m_nMaxSizeMB = -1;
m_nAllHisLogSize = -1;
m_bRealDisplay = false;
m_nCurDayIndex = 1;
m_bEnaSingleLogFileSize = false;
m_nSingleLogFileSize = 1024 * 1024; // 1MB
}
CLog::~CLog()
{
}
bool CLog::InitLog(QString& strLogPath, int nLatestFileCount, QString strFileNamePrefix/* = QString()*/)
{
if (strLogPath.isEmpty())
{
QString strAppDirPath = QCoreApplication::applicationDirPath();
strLogPath = strAppDirPath + "/log";
}
// 检测路径是否存在,不存在则创建
QDir dir(strLogPath);
if (!dir.exists())
{
if (!dir.mkpath(strLogPath))
{
qDebug("InitLog:mkpath(%s) failed!", strLogPath.toLocal8Bit().data());
return false;
}
}
m_strLogPath = strLogPath;
m_nLatestFileCount = nLatestFileCount;
m_strFileNamePrefix = strFileNamePrefix;
m_dateCurFile = QDate::currentDate();
m_nCurDayIndex = 1;
// 按日期命名文件夹
QString strFolder = QString("%1\\%2").arg(strLogPath).arg(m_dateCurFile.toString("yyyy-MM-dd"));
if (!dir.exists(strFolder))
{
if (!dir.mkpath(strFolder))
{
qDebug("InitLog:mkpath(%s) failed!", strFolder.toLocal8Bit().data());
return false;
}
}
// 打开文件
QString strFileName;
// 检测文件是否存在
while (true)
{
strFileName = QString("%1\\%2%3-%4.log").arg(strFolder).arg(strFileNamePrefix).arg(m_dateCurFile.toString("yyyy-MM-dd")).arg(m_nCurDayIndex);
if (!QFile::exists(strFileName))
break; // 不存在
// 已经存在,没有启用单个文件大小,继续在已存在文件上添加日志
if (!m_bEnaSingleLogFileSize)
break;
// 已经存在,再检测文件大小是否达到单个文件上限,没有达到上限继续在已存在文件上添加日志
QFileInfo fileInfo(strFileName);
if (fileInfo.size() >= m_nSingleLogFileSize)
m_nCurDayIndex++;
else
break;
}
m_fileLog.setFileName(strFileName);
if (!m_fileLog.open(QIODevice::Append | QIODevice::Text | QIODevice::WriteOnly))
{
qDebug("InitLog:open(%s) failed!", strFileName.toLocal8Bit().data());
return false;
}
m_bThreadRun = true;
start();
return true;
}
void CLog::UninitLog()
{
m_bThreadRun = false;
m_lock.lock();
m_queData.enqueue("");
m_lock.unlock();
wait(3000);
if (m_fileLog.isOpen())
m_fileLog.close();
}
bool CLog::AddLog(const QString& strMsg, LogType eLogType, QString strModuleName, QString strCppName, int nLineNo)
{
QString strLogInfo = QString("%1 %2@%3(%4)$%5:%6\n").arg(QTime::currentTime().toString("HH:mm:ss.zzz"))
.arg(strModuleName)
.arg(strCppName)
.arg(nLineNo)
.arg(STR_LOG_TYPE[eLogType])
.arg(strMsg);
m_lock.lock();
m_queData.enqueue(strLogInfo);
m_lock.unlock();
return true;
}
void CLog::CreateNewLogFileCheck()
{
QDate dateCur = QDate::currentDate();
if (dateCur > m_dateCurFile)
{
m_dateCurFile = dateCur;
m_nCurDayIndex = 1;
// 按日期命名文件夹
QString strFolder = QString("%1\\%2").arg(m_strLogPath).arg(m_dateCurFile.toString("yyyy-MM-dd"));
QDir dir;
if (!dir.exists(strFolder))
{
if (!dir.mkpath(strFolder))
{
qDebug("InitLog:mkpath(%s) failed!", strFolder.toLocal8Bit().data());
return;
}
}
// 重新创建一个新的文件
QString strFileName = QString("%1\\%2%3-%4.log").arg(strFolder).arg(m_strFileNamePrefix).arg(m_dateCurFile.toString("yyyy-MM-dd")).arg(m_nCurDayIndex);
if (m_fileLog.isOpen())
{
qint64 nCurFileSize = m_fileLog.size();
m_fileLog.close();
m_nAllHisLogSize += nCurFileSize;
}
m_fileLog.setFileName(strFileName);
if (!m_fileLog.open(QIODevice::Append | QIODevice::Text | QIODevice::WriteOnly))
{
qDebug("InitLog:open(%s) failed!", strFileName.toLocal8Bit().data());
return;
}
// 检测保留最近的文件个数,超出预设个数,删除最早的日志文件
CheckLatestFileCount();
}
}
void CLog::CheckLatestFileCount()
{
// 获取日志文件个数
QDir dir(m_strLogPath);
QStringList lstLogFiles = dir.entryList(QStringList("*.log"), QDir::Files, QDir::Name);
if (lstLogFiles.size() == 0 || lstLogFiles.size() <= m_nLatestFileCount)
return;
// 删除最早的日志文件
while (lstLogFiles.size() > m_nLatestFileCount)
{
QString strFilePath = m_strLogPath + "\\" + lstLogFiles.takeFirst();
if (!QFile::remove(strFilePath))
{
qDebug("CheckLatestFileCount:remove(%s) failed!", strFilePath.toLocal8Bit().data());
break;
}
}
}
void CLog::SetLatestFileCount(int nLatestFileCount)
{
m_nLatestFileCount = nLatestFileCount;
}
void CLog::SetMaxLogSize(__int64 nMaxSizeMB)
{
m_nMaxSizeMB = nMaxSizeMB;
}
void CLog::CheckLogSize()
{
if (m_nMaxSizeMB == -1) // 没有启用容量检测功能
return;
if (m_nAllHisLogSize == -1) // 第一次检测需要遍历所有日志文件
{
m_nAllHisLogSize = CalcAllLogSize(m_strLogPath);
}
qint64 nCurFileSize = m_fileLog.size();
double fCurAllLogSize = (m_nAllHisLogSize + nCurFileSize) / (double)(1024 * 1024);
if (fCurAllLogSize > m_nMaxSizeMB)
{
m_nAllHisLogSize -= DeleteOldLogByMaxSize((fCurAllLogSize - m_nMaxSizeMB) * 1024 * 1024);
}
}
__int64 CLog::CalcAllLogSize(QString strLogPath)
{
__int64 nFileSize = 0;
QDir dir(strLogPath);
QFileInfoList lstLogFiles = dir.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name);
for (int i = 0; i < lstLogFiles.size(); i++)
{
if (lstLogFiles[i].isDir())
{
nFileSize += CalcAllLogSize(lstLogFiles[i].absoluteFilePath());
}
else
{
if (m_fileLog.isOpen()) // 当前打开的文件不统计
{
if (lstLogFiles[i].absoluteFilePath().compare(m_fileLog.fileName(), Qt::CaseInsensitive) == 0)
continue;
}
nFileSize += lstLogFiles[i].size();
}
}
return nFileSize;
}
__int64 CLog::DeleteOldLogByMaxSize(int nNeedDelSize)
{
__int64 nDelFileSize = 0;
QDir dir(m_strLogPath);
QFileInfoList lstLogFolders = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name);
for (int i = 0; i < lstLogFolders.size(); i++)
{
__int64 nCurDelFileSize = DeleteOneDayLogs(lstLogFolders[i].absoluteFilePath());
nDelFileSize += nCurDelFileSize;
if (nDelFileSize > nNeedDelSize)
break;
}
return nDelFileSize;
}
__int64 CLog::DeleteOneDayLogs(QString strLogFolder)
{
__int64 nFileSize = 0;
QDir dir(strLogFolder);
QFileInfoList lstLogFiles = dir.entryInfoList(QStringList("*.log"), QDir::Files, QDir::Name);
for (int i = 0; i < lstLogFiles.size(); i++)
{
qint64 nCurFileSize = lstLogFiles[i].size();
if (!QFile::remove(lstLogFiles[i].absoluteFilePath()))
{
qDebug("DeleteOneDayLogs:remove(%s) failed!", lstLogFiles[i].absoluteFilePath().toLocal8Bit().data());
break;
}
else
{
nFileSize += nCurFileSize;
}
}
if (!dir.rmdir(strLogFolder))
{
qDebug("DeleteOneDayLogs:rmdir(%s) failed!", strLogFolder.toLocal8Bit().data());
}
return nFileSize;
}
void CLog::SetRealDisplayIPAndPort(bool bEnable, QString strIP, int nPort)
{
m_strIP = strIP;
m_nPort = nPort;
m_bRealDisplay = bEnable;
}
void CLog::SetSingleLogSaveMode(bool bEnaSingleLogFileSize, int nSingleLogFileSizeMB)
{
m_bEnaSingleLogFileSize = bEnaSingleLogFileSize;
m_nSingleLogFileSize = nSingleLogFileSizeMB * 1024 * 1024;
}
void CLog::run()
{
QUdpSocket udpSocket;
if (!udpSocket.bind())
{
qDebug("m_udpSocket.bind() failed!");
}
// 检测保留最近的文件个数,超出预设个数,删除最早的日志文件
CheckLatestFileCount();
while (m_bThreadRun)
{
QString strData;
m_lock.lock();
if (m_queData.size() <= 0)
{
m_lock.unlock();
msleep(200);
continue;
}
if (!m_bThreadRun)
break;
strData = m_queData.dequeue();
m_lock.unlock();
CreateNewLogFileCheck();
QString strUdpData = m_strFileNamePrefix + " " + strData;
QByteArray byData = strUdpData.toLocal8Bit();
if (m_bRealDisplay)
{
QHostAddress host(m_strIP);
qint64 nRet = udpSocket.writeDatagram(byData, host, m_nPort);
if (nRet != byData.size())
{
qDebug("AddLog:writeDatagram failed, log:%s!", byData.data());
}
}
if (!m_fileLog.isOpen())
{
qDebug("AddLog:file is not Open, log:%s!", byData.data());
msleep(200);
continue;
}
m_fileLog.write(byData);
m_fileLog.flush();
// 检测总文件大小
CheckLogSize();
// 分割文件
CheckSplitFile();
#ifdef _DEBUG
qDebug() << strData;
#endif
}
}
void CLog::CheckSplitFile()
{
if (!m_bEnaSingleLogFileSize)
return;
if (!m_fileLog.isOpen())
return;
qint64 nCurFileSize = m_fileLog.size();
if (nCurFileSize >= m_nSingleLogFileSize)
{
// 分割文件
m_nCurDayIndex++;
QString strFolder = QString("%1\\%2").arg(m_strLogPath).arg(m_dateCurFile.toString("yyyy-MM-dd"));
// 重新创建一个新的文件
QString strFileName = QString("%1\\%2%3-%4.log").arg(strFolder).arg(m_strFileNamePrefix).arg(m_dateCurFile.toString("yyyy-MM-dd")).arg(m_nCurDayIndex);
if (m_fileLog.isOpen())
{
qint64 nCurFileSize = m_fileLog.size();
m_fileLog.close();
m_nAllHisLogSize += nCurFileSize;
}
m_fileLog.setFileName(strFileName);
if (!m_fileLog.open(QIODevice::Append | QIODevice::Text | QIODevice::WriteOnly))
{
qDebug("InitLog:open(%s) failed!", strFileName.toLocal8Bit().data());
return;
}
}
}