Qt实现log打印

 本文章实现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;
		}
	}
}

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值