Qt日誌類 使用方便,支持多線程,可以註冊回調在程序異常時給出通知

 

log.h

#ifndef PLAINLOG_H
#define PLAINLOG_H

#include <QFile>
#include <QMutex>
#include <QDateTime>
#include <QTextStream>
#include <stdio.h>
#include <QObject>
#include <QTimer>
#include <QTime>
#include <QDataStream>

#include <QtCore/qglobal.h>

#ifndef BUILD_STATIC
# if defined(PLAINLOG_LIB)
#  define PLAINLOG_EXPORT Q_DECL_EXPORT
# else
#  define PLAINLOG_EXPORT Q_DECL_IMPORT
# endif
#else
# define PLAINLOG_EXPORT
#endif


/*
LOG_ERROR  << "hhehehheh" << "jjjjjj";          //記錄錯誤
LOG_WARNING  << "hhehehheh" << "jjjjjj";        //記錄警告
LOG_INFO  << "hhehehheh" << "jjjjjj";           //記錄信息

SET_LOG_SEQUENCE_BY_DATE(7) //設置文件以時間排序,7保存天數
SET_LOG_MAX_COUNT(1024)		//設置文件最大行數

class ClassName
{
public:
 void crashTips(QStringList);
}

RegisterCrashProcesProc(&ClassName::crashTips,ObjPointer)  //註冊崩潰處理函數,ObjPointer(對象實例指針)

*/

namespace PlainLog
{
#pragma execution_character_set("utf-8")  
	class LogFile;
	class /*PLAINLOG_EXPORT*/ Log
	{
	public:
		enum MsgType
		{
			MSG_INFO,
			MSG_WARNING,
			MSG_ERROR
		};
		enum OutputFlag
		{
			TO_CONSOLE = 0x00000001,
			TO_LOGFILE = 0x00000002
		};
		Q_DECLARE_FLAGS(OutputFlags, OutputFlag)

	public:
		Log(OutputFlags flags, MsgType t) :m_outputFlag(flags), m_logType(t) { m_stream.setString(&m_msg, QIODevice::ReadWrite); };
		~Log();

		template<typename T>
		inline Log& operator<<(T arg)
		{
			m_stream << arg;
			return maybeSpace();
		}

		template<>
		inline Log& operator<<(QMap<QString,QString> arg) {
			QStringList _list = arg.keys();
			QString result;;
			for (auto _key:_list)
			{
				result += _key;
				result += "=";
				result += arg[_key];
				result += ",";
			}
			m_stream << result; return maybeSpace();
		}

		static void setFileSortByTime(int SaveTime);

		static void setMaxFileLen(int count);

		static void RegisterCrashProcessProc(std::function<void(QStringList)> _proc);

	
	private:
		Log(const Log&)=delete;
		Log& operator=(const Log&)=delete;
		inline Log& space() { m_space = true; m_stream << " "; return *this; }
		inline Log& nospace() { m_space = false; return *this; }
		inline Log& maybeSpace() { if (m_space) m_stream << " "; return *this; }

		bool m_space = true;
		MsgType m_logType;
		QTextStream m_stream;
		QString m_msg;
		OutputFlags m_outputFlag;
	};
}

#define DETAIL_INFO  QString(" File:%1 -> Function:%2 -> Line:%3").arg(__FILE__).arg(__FUNCTION__).arg(__LINE__)
using namespace PlainLog;

#ifndef NDEBUG
#	define LOG_ERROR PlainLog::Log(QFlags<PlainLog::Log::OutputFlag>(Log::TO_LOGFILE) , PlainLog::Log::MSG_ERROR)<<DETAIL_INFO
#	define LOG_INFO PlainLog::Log(QFlags<PlainLog::Log::OutputFlag>(Log::TO_LOGFILE), PlainLog::Log::MSG_INFO)<<DETAIL_INFO
#	define LOG_WARNING PlainLog::Log(QFlags<PlainLog::Log::OutputFlag>(Log::TO_LOGFILE), PlainLog::Log::MSG_WARNING)<<DETAIL_INFO
#else
#	define LOG_ERROR PlainLog::Log(QFlags<PlainLog::Log::OutputFlag>(Log::TO_CONSOLE|Log::TO_LOGFILE) , PlainLog::Log::MSG_ERROR)<<DETAIL_INFO
#	define LOG_INFO PlainLog::Log(QFlags<PlainLog::Log::OutputFlag>(Log::TO_CONSOLE|Log::TO_LOGFILE), PlainLog::Log::MSG_INFO)<<DETAIL_INFO
#	define LOG_WARNING PlainLog::Log(QFlags<PlainLog::Log::OutputFlag>(Log::TO_CONSOLE|Log::TO_LOGFILE), PlainLog::Log::MSG_WARNING)<<DETAIL_INFO
#endif

#define SET_LOG_SEQUENCE_BY_DATE(SaveTime) PlainLog::Log::setFileSortByTime(SaveTime)
#define SET_LOG_MAX_COUNT(count) PlainLog::Log::setMaxFileLen(count)

//註冊崩潰通知處理例程
#define RegisterCrashProcesProc(Proc,ObjPointer)\
 PlainLog::Log::RegisterCrashProcessProc((std::function<void(QStringList)>)std::bind(Proc,ObjPointer,std::placeholders::_1))

//與以前的保持兼容性,不建議使用
#define LOGERROR(_msg) PlainLog::Log(QFlags<PlainLog::Log::OutputFlag>(Log::TO_CONSOLE|Log::TO_LOGFILE) , PlainLog::Log::MSG_ERROR)<<DETAIL_INFO << _msg
#define LOGINFO(_msg) PlainLog::Log(QFlags<PlainLog::Log::OutputFlag>(Log::TO_CONSOLE|Log::TO_LOGFILE), PlainLog::Log::MSG_INFO)<<DETAIL_INFO<<_msg
#define LOGWRANING(_msg) PlainLog::Log(QFlags<PlainLog::Log::OutputFlag>(Log::TO_CONSOLE|Log::TO_LOGFILE), PlainLog::Log::MSG_WARNING)<<DETAIL_INFO<<_msg
//---------------------------

#endif // PLAINLOG_H

 

 

 

.cpp

 

#include "Log.h"
#include <QDir>
#include <QSettings>
#include <QCoreApplication>
#include <QDebug>
#include <functional>
#include <vector>
#include <windows.h>
#include <dbghelp.h> 
#include <iostream>


using namespace std;
#pragma comment(lib,  "dbghelp.lib")
#pragma execution_character_set("utf-8")

namespace PlainLog
{
	
	//設置控制檯顏色
	struct CoutColor
	{
		CoutColor(WORD attribute) :mColor(attribute) {};
		WORD mColor;//顏色值
	};

	QDebug& operator<<(QDebug& debug, CoutColor& c)
	{
		HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
		SetConsoleTextAttribute(hStdout, c.mColor);
		return debug;
	}

	
	/*崩潰時生成棧調用信息 及 崩潰轉儲文件*/
	class StackFile
	{
		static const int MAX_ADDRESS_LENGTH = 32;
		static const int MAX_NAME_LENGTH = 1024;

		struct CrashInfo
		{
			CHAR ErrorCode[MAX_ADDRESS_LENGTH];
			CHAR Address[MAX_ADDRESS_LENGTH];
			CHAR Flags[MAX_ADDRESS_LENGTH];
		};

		// CallStack信息
		struct CallStackInfo
		{
			CHAR ModuleName[MAX_NAME_LENGTH];
			CHAR MethodName[MAX_NAME_LENGTH];
			CHAR FileName[MAX_NAME_LENGTH];
			CHAR LineNumber[MAX_NAME_LENGTH];
		};

	public:
		static StackFile* instance()
		{
			if (m_instance == nullptr)
			{
				QMutexLocker locker(&m_createMutex);
				if (m_instance == nullptr)
				{
					StackFile* ptmp = new StackFile();
					m_instance = ptmp;
				}
			}
			return m_instance;
		}

		void RegisterCrashProcessProc(std::function<void(QStringList)> _proc)
		{
			m_crashProc = _proc;
		}

		void CrashInfoTOConsole(QStringList inforlist)
		{
			for (int i = 0;i<inforlist.count();i++)
			{
				qDebug() << CoutColor(FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY)
					<< "MESSAGE : " << inforlist.at(i)<<endl;
			}	
		}

	private:
		StackFile() {};
		// 安全拷貝字符串函數
		void SafeStrCpy(char* szDest, size_t nMaxDestSize, const char* szSrc)
		{
			if (nMaxDestSize <= 0) return;
			if (strlen(szSrc) < nMaxDestSize)
			{
				strcpy_s(szDest, nMaxDestSize, szSrc);
			}
			else
			{
				strncpy_s(szDest, nMaxDestSize, szSrc, nMaxDestSize);
				szDest[nMaxDestSize - 1] = '\0';
			}
		}

		// 得到程序崩潰信息
		//
		CrashInfo GetCrashInfo(const EXCEPTION_RECORD *pRecord)
		{
			CrashInfo crashinfo;
			SafeStrCpy(crashinfo.Address, MAX_ADDRESS_LENGTH, "N/A");
			SafeStrCpy(crashinfo.ErrorCode, MAX_ADDRESS_LENGTH, "N/A");
			SafeStrCpy(crashinfo.Flags, MAX_ADDRESS_LENGTH, "N/A");

			sprintf_s(crashinfo.Address, "%08X", pRecord->ExceptionAddress);
			sprintf_s(crashinfo.ErrorCode, "%08X", pRecord->ExceptionCode);
			sprintf_s(crashinfo.Flags, "%08X", pRecord->ExceptionFlags);

			return crashinfo;
		}

		// 得到CallStack信息
//
		vector<CallStackInfo> GetCallStack(const CONTEXT *pContext)
		{
			HANDLE hProcess = GetCurrentProcess();

			SymInitialize(hProcess, NULL, TRUE);

			vector<CallStackInfo> arrCallStackInfo;

			CONTEXT c = *pContext;

			STACKFRAME64 sf;
			memset(&sf, 0, sizeof(STACKFRAME64));
			DWORD dwImageType = IMAGE_FILE_MACHINE_I386;

			// 不同的CPU類型,具體信息可查詢MSDN
			//
#ifdef _M_IX86
			sf.AddrPC.Offset = c.Eip;
			sf.AddrPC.Mode = AddrModeFlat;
			sf.AddrStack.Offset = c.Esp;
			sf.AddrStack.Mode = AddrModeFlat;
			sf.AddrFrame.Offset = c.Ebp;
			sf.AddrFrame.Mode = AddrModeFlat;
#elif _M_X64
			dwImageType = IMAGE_FILE_MACHINE_AMD64;
			sf.AddrPC.Offset = c.Rip;
			sf.AddrPC.Mode = AddrModeFlat;
			sf.AddrFrame.Offset = c.Rsp;
			sf.AddrFrame.Mode = AddrModeFlat;
			sf.AddrStack.Offset = c.Rsp;
			sf.AddrStack.Mode = AddrModeFlat;
#elif _M_IA64
			dwImageType = IMAGE_FILE_MACHINE_IA64;
			sf.AddrPC.Offset = c.StIIP;
			sf.AddrPC.Mode = AddrModeFlat;
			sf.AddrFrame.Offset = c.IntSp;
			sf.AddrFrame.Mode = AddrModeFlat;
			sf.AddrBStore.Offset = c.RsBSP;
			sf.AddrBStore.Mode = AddrModeFlat;
			sf.AddrStack.Offset = c.IntSp;
			sf.AddrStack.Mode = AddrModeFlat;
#else
#error "Platform not supported!"
#endif

			HANDLE hThread = GetCurrentThread();

			while (true)
			{
				// 該函數是實現這個功能的最重要的一個函數
				// 函數的用法以及參數和返回值的具體解釋可以查詢MSDN
				//
				if (!StackWalk64(dwImageType, hProcess, hThread, &sf, &c, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL))
				{
					break;
				}

				if (sf.AddrFrame.Offset == 0)
				{
					break;
				}

				CallStackInfo callstackinfo;
				SafeStrCpy(callstackinfo.MethodName, MAX_NAME_LENGTH, "N/A");
				SafeStrCpy(callstackinfo.FileName, MAX_NAME_LENGTH, "N/A");
				SafeStrCpy(callstackinfo.ModuleName, MAX_NAME_LENGTH, "N/A");
				SafeStrCpy(callstackinfo.LineNumber, MAX_NAME_LENGTH, "N/A");

				BYTE symbolBuffer[sizeof(IMAGEHLP_SYMBOL64) + MAX_NAME_LENGTH];
				IMAGEHLP_SYMBOL64 *pSymbol = (IMAGEHLP_SYMBOL64*)symbolBuffer;
				memset(pSymbol, 0, sizeof(IMAGEHLP_SYMBOL64) + MAX_NAME_LENGTH);

				pSymbol->SizeOfStruct = sizeof(symbolBuffer);
				pSymbol->MaxNameLength = MAX_NAME_LENGTH;

				DWORD symDisplacement = 0;

				// 得到函數名
				//
				if (SymGetSymFromAddr64(hProcess, sf.AddrPC.Offset, NULL, pSymbol))
				{
					SafeStrCpy(callstackinfo.MethodName, MAX_NAME_LENGTH, pSymbol->Name);
				}

				IMAGEHLP_LINE64 lineInfo;
				memset(&lineInfo, 0, sizeof(IMAGEHLP_LINE64));

				lineInfo.SizeOfStruct = sizeof(IMAGEHLP_LINE64);

				DWORD dwLineDisplacement;

				// 得到文件名和所在的代碼行
				//
				if (SymGetLineFromAddr64(hProcess, sf.AddrPC.Offset, &dwLineDisplacement, &lineInfo))
				{
					SafeStrCpy(callstackinfo.FileName, MAX_NAME_LENGTH, lineInfo.FileName);
					sprintf_s(callstackinfo.LineNumber, "%d", lineInfo.LineNumber);
				}

				IMAGEHLP_MODULE64 moduleInfo;
				memset(&moduleInfo, 0, sizeof(IMAGEHLP_MODULE64));

				moduleInfo.SizeOfStruct = sizeof(IMAGEHLP_MODULE64);

				// 得到模塊名
				//
				if (SymGetModuleInfo64(hProcess, sf.AddrPC.Offset, &moduleInfo))
				{
					SafeStrCpy(callstackinfo.ModuleName, MAX_NAME_LENGTH, moduleInfo.ModuleName);
				}

				arrCallStackInfo.push_back(callstackinfo);
			}

			SymCleanup(hProcess);

			return arrCallStackInfo;
		}

		LONG WINAPI CrashHandler(EXCEPTION_POINTERS *pException)
		{
			// 確保有足夠的棧空間
			//
#ifdef _M_IX86
			if (pException->ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW)
			{
				static char TempStack[1024 * 128];
				__asm mov eax, offset TempStack[1024 * 128];
				__asm mov esp, eax;
			}
#endif
			TCHAR szPath[MAX_PATH] = { 0 };
			::GetModuleFileName(NULL, szPath, MAX_PATH); //獲取應用程序路徑

			QFileInfo _fileInfo(QString::fromStdWString(szPath));
			QString _logName = _fileInfo.baseName();
			QDir _applicationDir(_fileInfo.absolutePath());

			if (!_applicationDir.exists(_applicationDir.path() + "/crash"))
			{
				_applicationDir.mkpath(_applicationDir.path() + "/crash");
			}
			_applicationDir.cd("crash");
			QString _date = QDate::currentDate().toString(Qt::ISODate);
			QString path = _applicationDir.path() + "/" + _date;
			QString statckFileName = path + ".stack";
			QString dmpFileName = path + ".dmp";
			QFile file;
			QTextStream out;
			file.setFileName(statckFileName);

			if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QFile::Truncate))
			{
				return EXCEPTION_EXECUTE_HANDLER;
			}
			out.setDevice(&file);

			CrashInfo crashinfo = GetCrashInfo(pException->ExceptionRecord);

			QStringList stackInfoList;

			// 輸出Crash信息
			out << "ErrorCode: " << crashinfo.ErrorCode << endl;
			out << "Address: " << crashinfo.Address << endl;
			out << "Flags: " << crashinfo.Flags << endl;

			stackInfoList << QString("ErrorCode: %0").arg(crashinfo.ErrorCode);
			stackInfoList << QString("Address: %0").arg(crashinfo.Address);
			stackInfoList << QString("Flags: %0").arg(crashinfo.Flags);

			vector<CallStackInfo> arrCallStackInfo = GetCallStack(pException->ContextRecord);
			// 輸出CallStack
			for (vector<CallStackInfo>::iterator i = arrCallStackInfo.begin(); i != arrCallStackInfo.end(); ++i)
			{
				CallStackInfo callstackinfo = (*i);

				out << callstackinfo.MethodName << "() : [" << callstackinfo.ModuleName << \
					"] (File: " << callstackinfo.FileName << " @Line " \
					<< callstackinfo.LineNumber << ")" << "\n";

				QString _stackInfo;
				QTextStream _stream(&_stackInfo);

				_stream << callstackinfo.MethodName << "() : [" << callstackinfo.ModuleName << \
					"] (File: " << callstackinfo.FileName << " @Line " \
					<< callstackinfo.LineNumber << ")" << "\n";

				stackInfoList << _stackInfo;
			}
			out.flush();
			file.close();
			if (m_crashProc)
			{
				m_crashProc(stackInfoList);
			}
			
			//write dump file
			HANDLE   hFile = CreateFile(dmpFileName.toStdWString().c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
			if (hFile != INVALID_HANDLE_VALUE)
			{
				MINIDUMP_EXCEPTION_INFORMATION   ExInfo;
				ExInfo.ThreadId = ::GetCurrentThreadId();
				ExInfo.ExceptionPointers = pException;
				ExInfo.ClientPointers = NULL;
				//   write   the   dump
				BOOL   bOK = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, &ExInfo, NULL, NULL);
				CloseHandle(hFile);
			}
			//end
			return EXCEPTION_EXECUTE_HANDLER;
		}

	private:
		friend LONG WINAPI GlobalCrashHandler(EXCEPTION_POINTERS*);
		std::function<void(QStringList)> m_crashProc;
		static QMutex m_createMutex;
		static StackFile* m_instance;
		
	};
	QMutex StackFile::m_createMutex;
	StackFile* StackFile::m_instance = nullptr;

	LONG WINAPI GlobalCrashHandler(EXCEPTION_POINTERS *pException)
	{
		LOG_ERROR << "GlobalCrashHandler";
		return StackFile::instance()->CrashHandler(pException);
	}

	/*記錄日誌信息到文件*/
	class LogFile
	{
	private:
		LogFile()
		{
			::SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)GlobalCrashHandler);
			m_index = 0;
			
			m_maxFileLine = 10000;
			m_bStart = false;
			m_bSortByDate = false;
		}
		static LogFile* m_instance;
	public:
		~LogFile()
		{
			endLog();
		}
		static LogFile* getInstance()
		{
			if (m_instance == nullptr)
			{

				QMutexLocker locker(&m_createMutex);
				if (m_instance == nullptr)
				{
					LogFile* ptmp = new LogFile();
					m_instance = ptmp;
				}
			}
			return m_instance;
		}

		void addMessage(const QString& msg, Log::MsgType msgType, Log::OutputFlags flags);

		//設置日誌文件
		void setMaxFileLen(int length);

		//設置日誌文件按照時間順序排列,不被清空,_fileSaveDays文件保存多久
		void setFileSortByTime(int _fileSaveDays);
	private:
		void startLog();
		void endLog();
		const QString mapToDesc(Log::MsgType msgType);
		void deleteFile(int day, QString logPath);
		void getFileDate(QString logPath);
		void deleteDirctory(QString dirctoryName);
		bool shouldRotate();
		void rotate();
	private:
		static QMutex m_createMutex;
		QFile m_logFile;
		QMutex m_mutex;
		QTextStream m_stream;

		int m_index;  //當前行數索引
		
		QStringList m_fileToRemove;

		int m_maxFileLine;   //日誌顯示的最大行數
		bool m_bStart;
		bool m_bSortByDate;  //是否按照日期排序生成日誌
	};

	QMutex LogFile::m_createMutex;
	LogFile* LogFile::m_instance = nullptr;

	void LogFile::startLog()
	{
		if (m_bStart)
		{
			return;
		}
		m_bStart = true;

		if (qApp == nullptr)
		{
			qDebug() << CoutColor(FOREGROUND_RED | FOREGROUND_INTENSITY) << "MESSAGE : " << "Log Start Shold Late Than QApplication!";
			return;
		}

		TCHAR szPath[MAX_PATH] = { 0 };
		::GetModuleFileName(NULL, szPath, MAX_PATH); //獲取應用程序路徑

		QFileInfo _fileInfo(QString::fromStdWString(szPath));

		QString _logName = _fileInfo.baseName();
		QDir _applicationDir(_fileInfo.absolutePath());

		if (!_applicationDir.exists(_applicationDir.path() + "/log"))
		{
			_applicationDir.mkpath(_applicationDir.path() + "/log");
		}
		_applicationDir.cd("log");
		QString log = _applicationDir.path() + "/" + _logName + ".html";
		m_logFile.setFileName(_applicationDir.path() + "/" + _logName + ".html");

		if (m_logFile.open(QIODevice::ReadWrite | QIODevice::Text | QFile::Truncate))
		{
			m_stream.setDevice(&m_logFile);
			m_stream << "<html><title>" << _logName << "</title><body>" << endl;
			m_stream << "<h1 align=\"center\">" << _logName << "</h1><hr>" << endl;
			m_stream << "<pre><h3 align=\"center\""
				"<b><font color=#FF0000>  Error	</font></b>"
				"<b><font color=#FF8000>  Warning	</font></b>"
				"<b><font color=#0080FF>  Message	</font></b></h3></pre><hr>"
				<< endl;
		}

		qDebug() << CoutColor(FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY) << "MESSAGE : " << "Log Start OK!";
		m_bStart = true;
	}

	void LogFile::setFileSortByTime(int _fileSaveDays)
	{
		if (m_bStart)
		{
			addMessage(DETAIL_INFO + "更改日誌文件生成模式失敗,再調用start函數之前設置!", Log::MSG_ERROR, Log::TO_CONSOLE);
			return;
		}

		if (qApp == nullptr)
		{
			addMessage("不允許在QApplication創建之前啓動日誌!", Log::MSG_ERROR, Log::TO_CONSOLE);
			return;
		}

		TCHAR szPath[MAX_PATH] = { 0 };
		::GetModuleFileName(NULL, szPath, MAX_PATH); //獲取應用程序路徑


		QFileInfo _fileInfo(QString::fromStdWString(szPath));
		QString _logName = _fileInfo.baseName();
		QDir _applicationDir(_fileInfo.absolutePath());

		if (!_applicationDir.exists(_applicationDir.path() + "/log"))
		{
			_applicationDir.mkpath(_applicationDir.path() + "/log");
		}
		_applicationDir.cd("log");

		//根據saveday,刪除過期文件
		deleteFile(_fileSaveDays, _applicationDir.path());

		QString date = QDate::currentDate().toString("yyyy-MM-dd");
		QString time = QTime::currentTime().toString("hh-mm-ss");
		time += ".html";
		QString _path = _applicationDir.path() + "/" + date;

		if (!_applicationDir.exists(_path))
		{
			_applicationDir.mkpath(_path);
		}
		_applicationDir.cd(date);
		m_logFile.setFileName(_applicationDir.path() + "/" + time);
		if (m_logFile.open(QIODevice::ReadWrite | QIODevice::Text | QFile::Truncate))
		{
			m_stream.setDevice(&m_logFile);
			m_stream.setDevice(&m_logFile);
			m_stream << "<html><title>" << _logName << "</title><body>" << endl;
			m_stream << "<h1 align=\"center\">" << _logName << "</h1><hr>" << endl;
			m_stream << "<pre><h3 align=\"center\""
				"<b><font color=#FF0000>  Error	</font></b>"
				"<b><font color=#FF8000>  Warning	</font></b>"
				"<b><font color=#0080FF>  Message	</font></b></h3></pre><hr>"
				<< endl;


		}
		else
		{
			addMessage(QString("%0打開日誌文件失敗!原因:%1").arg(DETAIL_INFO).arg(m_logFile.errorString()), Log::MSG_ERROR, Log::TO_CONSOLE);
			return;
		}

		addMessage(DETAIL_INFO + "日誌文件打開成功", Log::MSG_INFO, Log::TO_CONSOLE);

		m_bStart = true;
	}

	void LogFile::addMessage(const QString& msg, Log::MsgType msgType, Log::OutputFlags flags)
	{
		QString time = QTime::currentTime().toString("hh:mm:ss");
		QString color;
		switch (msgType)
		{
		case Log::MSG_INFO:
			color = "<p><b><font color=#0080FF>";
			break;

		case Log::MSG_ERROR:
			color = "<p><b><font color=#FF0000>";
			break;

		case Log::MSG_WARNING:
			color = "<p><b><font color=#FF8000>";
			break;
		}

		QMutexLocker locker(&m_mutex);
		//如果文件大小不是無限,判斷是否需要重寫,如果需要就覆蓋重寫

		if (shouldRotate())
		{
			rotate();
		}

		if (flags.testFlag(Log::TO_LOGFILE))
		{
			if (!m_logFile.isOpen())
			{
				startLog();
				if (!m_logFile.isOpen())
					return;
			}
			m_stream << color  << mapToDesc(msgType) << ": < " << time << ": < " << msg.toLatin1() << "</font></b></p>" << endl;
			m_stream.flush();
			m_index++;
			
		}
	

#ifndef NDEBUG
		if (flags.testFlag(Log::TO_CONSOLE))
		{
			switch (msgType)
			{
			case Log::MSG_INFO:
				qDebug() << CoutColor(FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY)
					<< "MESSAGE : " << msg.toLatin1();
				break;

			case Log::MSG_ERROR:
				qDebug() << CoutColor(FOREGROUND_RED | FOREGROUND_INTENSITY)
					<< "ERROR   : " << msg.toLatin1();
				break;

			case Log::MSG_WARNING:
				qDebug() << CoutColor(FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY)
					<< "WARNING : " << msg.toLatin1();
				break;
			}
		}
#endif
	}

	const QString LogFile::mapToDesc(Log::MsgType msgType)
	{
		QString _result;

		switch (msgType)
		{
		case Log::MSG_INFO:       _result = "MESSAGE";  break;
		case Log::MSG_ERROR:      _result = "ERROR  ";  break;
		case Log::MSG_WARNING:    _result = "WARNING";  break;

		default:
			break;
		}
		return _result;
	}


	void LogFile::endLog()
	{
		if (m_logFile.open(QIODevice::ReadWrite | QIODevice::Text | QFile::Append))
		{
			m_stream << "</body> \n";
			m_stream.flush();
			m_logFile.flush();
			m_logFile.close();
		}
	}

	void LogFile::setMaxFileLen(int count)
	{
		QMutexLocker locker(&m_mutex);
		m_maxFileLine = count;
	}

	bool LogFile::shouldRotate()
	{
		//如果日誌條數大於1000就刪除開頭行
		return m_index > m_maxFileLine;
	}

	void LogFile::rotate()
	{
		//刪除開頭行
		int row = 0;
		QString log;

		m_stream.seek(0);
		QString line = m_stream.readLine();

		while (!line.isNull())
		{
			if (row >= 3 && row <=22)
			{
				line.clear();
			}
			else
			{
				log.append(line + "\n");
			}

			line = m_stream.readLine();
			row++;
		}

		m_logFile.close();

		m_index -= 20;
		if (m_logFile.open(QFile::Truncate | QIODevice::ReadWrite | QIODevice::Text))
		{
			m_stream << log;
		}

	}

	void LogFile::getFileDate(QString logPath)
	{
		QDir dir(logPath);
		if (!dir.exists())
			return;

		dir.setFilter(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot);

		//文件夾優先
		dir.setSorting(QDir::DirsFirst);

		//轉化成一個list
		QFileInfoList list = dir.entryInfoList();
		if (list.size() < 1)
		{
			return;
		}
		int i = 0;
		do
		{
			QFileInfo fileInfo = list.at(i);
			//如果是文件夾,遞歸
			bool bisDir = fileInfo.isDir();
			if (bisDir)
			{
				QString path = fileInfo.filePath();
				QStringList dateList = path.split("/");
				QString Data = dateList.at(dateList.count() - 1);
				m_fileToRemove.append(Data);
			}
			i++;
		} while (i < list.size());
	}

	void LogFile::deleteFile(int day, QString logPath)
	{
		//先檢索log文件夾下文件日期
		getFileDate(logPath);
		//刪除過期文件
		int current_second = QDateTime::currentDateTime().toTime_t();
		current_second -= day * 86400;
		for (int i = 0; i < m_fileToRemove.count(); i++)
		{
			QString file_date = m_fileToRemove.at(i);
			QDateTime dateTime = QDateTime::fromString(file_date, "yyyy-MM-dd");
			int file_second = dateTime.toTime_t();
			if (file_second < current_second)
			{
				//刪除文件夾//先刪除文件夾下所有文件
				deleteDirctory(logPath + "/" + file_date);
			}
		}
	}



	void LogFile::deleteDirctory(QString dirctoryName)
	{
		QDir dir(dirctoryName);
		if (!dir.exists())
			return;

		dir.setFilter(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot);

		//文件夾優先
		dir.setSorting(QDir::DirsFirst);

		//轉化成一個list
		QFileInfoList list = dir.entryInfoList();
		if (list.size() < 1)
		{
			return;
		}
		int i = 0;
		int count = list.size();
		do
		{
			QFileInfo fileInfo = list.at(i);
			if (fileInfo.isFile())
			{ // 是文件,刪除
				fileInfo.dir().remove(fileInfo.fileName());
			}
			else
			{ // 遞歸刪除
				deleteDirctory(fileInfo.absoluteFilePath());
			}

			i++;
		} while (i < count);
		dir.rmpath(dir.absolutePath());
	}

	Log::~Log()
	{
		m_stream.flush();
		LogFile::getInstance()->addMessage(m_msg, m_logType, m_outputFlag);
	}

	void Log::setFileSortByTime(int SaveTime)
	{	
		LogFile::getInstance()->setFileSortByTime(SaveTime);
	}

	void Log::setMaxFileLen(int count)
	{
		LogFile::getInstance()->setMaxFileLen(count);
	}


	void Log::RegisterCrashProcessProc(std::function<void(QStringList)> _proc)
	{
		StackFile::instance()->RegisterCrashProcessProc(_proc);
	}

}




使用方法

 

 



LOG_ERROR  << "---------" << 890890890;          //記錄錯誤
LOG_WARNING  << "---------" << 890890890;         //記錄警告
LOG_INFO  << "---------" << 890890890;           //記錄信息

SET_LOG_SEQUENCE_BY_DATE(7) //設置文件以時間排序,7保存天數
SET_LOG_MAX_COUNT(1024)		//設置文件最大行數

class ClassName
{
public:
 void crashTips(QStringList);
}

RegisterCrashProcesProc(&ClassName::crashTips,ObjPointer)  //註冊崩潰處理函數,ObjPointer(對

 

 

 

 

 


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章