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(对