qt日志是平时开发中常用的功能,qt也提供了相应的功能,但是功能并不完善,以下提供一个完善的日志功能,具有以下特点
1.非阻塞,并且开销几乎为零
2.具有日志等级分类功能,而且还能记录qt框架报告的错误
3.日志会根据日期变化,每天自动生成一个
4.使用极其简单,只要加载两个文件即可
5.demo链接https://github.com/MatchX/qtlogdemo 最好看看demo否则可能掉坑里
6.日志样式如下
#ifndef LOGMSG_H
#define LOGMSG_H
#include <iostream>
#include <mutex>
#include <set>
#include <QString>
#include <QDateTime>
#include <QCoreApplication>
#include <QDir>
#include <QTextStream>
#include <QDebug>
#define SAFEDELETE(Pt_t) {\
if ((Pt_t) != nullptr)\
{\
delete (Pt_t);(Pt_t) = nullptr;\
}\
}
#define SAFEDELETEARRAY(Pt_t) {\
if ((Pt_t) != nullptr)\
{\
delete[](Pt_t);(Pt_t) = nullptr;\
}\
}
//定义日志宏 difine log macro
#define logDebug QMessageLogger(__FILE__, __LINE__, Q_FUNC_INFO).debug
#define logInfo QMessageLogger(__FILE__, __LINE__, Q_FUNC_INFO).info
#define logWarning QMessageLogger(__FILE__, __LINE__, Q_FUNC_INFO).warning
#define logCritical QMessageLogger(__FILE__, __LINE__, Q_FUNC_INFO).critical
#define logFatal QMessageLogger(__FILE__, __LINE__, Q_FUNC_INFO).fatal
class LogMsg final
{
public:
LogMsg(const LogMsg&) = delete;
LogMsg& operator=(const LogMsg&) = delete;
LogMsg(const LogMsg&&) = delete;
LogMsg& operator=(LogMsg&&) = delete;
enum class LogMsgType : char {
ALL = 1,//全部
DebugMsg,//debug及以上
InfoMsg,//info及以上
WarningMsg,
CriticalMsg,
FatalMsg,
};
public:
static inline void outputMessage(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
if (LogMsgType::InfoMsg == ms_LogMstType && QtDebugMsg == type)
{
return;
}
else if (LogMsgType::WarningMsg == ms_LogMstType && (QtDebugMsg == type || QtInfoMsg == type))
{
return;
}
else if (LogMsgType::CriticalMsg== ms_LogMstType && (QtDebugMsg == type || QtInfoMsg == type || QtWarningMsg == type))
{
return;
}
else if (LogMsgType::FatalMsg == ms_LogMstType && (QtDebugMsg == type || QtInfoMsg == type || QtWarningMsg == type || QtCriticalMsg == type))
{
return;
}
if (!ms_IsWorking)
{
return;
}
QString pretext;
switch (type)
{
case QtDebugMsg:
{
pretext = QString("Debug:");
break;
}
case QtInfoMsg:
{
pretext = QString("Info:");
break;
}
case QtWarningMsg:
{
pretext = QString("Warning:");
break;
}
case QtCriticalMsg:
{
pretext = QString("Critical:");
break;
}
case QtFatalMsg:
{
pretext = QString("Fatal:");
break;
}
default:
{
pretext = QString("Unknown:");
};
}
const QString context_info = QString("File:(%1) Line:(%2)").arg(QString(context.file)).arg(context.line);
const QString current_date_time = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss ddd");
const QString current_date = QString("(%1)").arg(current_date_time);
const QString message = QString("%1 %2 %3 %4 %5").arg(pretext).arg(context_info).arg(msg).arg(current_date).arg(ms_TrailMSG) + "\n";
//write log message
{
std::lock_guard<std::mutex> lock2(ms_manager->m_LogMsgBufDeqLock);
ms_manager->m_LogMsgBufVec.push_back(message);
}
ms_manager->m_BufferCondVar.notify_all();
}
static void SetExtMsg(/*const std::string& headmsg,*/ const std::string& trailmsg)
{
//ms_manager->m_HeadMSG = QString::fromStdString(headmsg);
ms_TrailMSG = QString::fromStdString(trailmsg);
}
static int Create(const LogMsgType logmsgtype)
{
int retval{ -1 };
std::call_once(ms_CreateOnce, [&]()
{
const QString workdir = QCoreApplication::applicationDirPath();
ms_LogDirPath = workdir + QDir::separator() + "Log";
ms_LogDirPath = QDir::toNativeSeparators(ms_LogDirPath);
const QDir dir(ms_LogDirPath);
if (!dir.exists())
{
if (dir.mkpath(ms_LogDirPath))
{
retval = 0;
}
else
{
retval = -1;
}
}
else
{
retval = 0;
}
std::lock_guard<std::mutex> lock(ms_WorkStarusLock);
ms_manager = new LogMsg(logmsgtype);
});
return retval;
}
static void Destroy()
{
SAFEDELETE(ms_manager);
}
private:
explicit LogMsg(const LogMsgType logmsgtype = LogMsgType::ALL);
~LogMsg();
void OpenFile();
void CloseFile();
void WriteBufferThread();
private:
static LogMsg* ms_manager;
static bool ms_IsWorking;
static std::mutex ms_WorkStarusLock;
static std::once_flag ms_CreateOnce;
static QString ms_LogDirPath;
static LogMsgType ms_LogMstType;
//mutable QString m_HeadMSG;
static QString ms_TrailMSG;
QFile* m_OutFile{ nullptr };
mutable std::vector<QString> m_LogMsgBufVec;
mutable std::mutex m_LogMsgBufDeqLock;
bool m_FileIsOpen{ false };
std::thread m_WriteBufferThread;
//std::once_flag m_onceflg;
std::mutex m_BufferMutex;
std::condition_variable m_BufferCondVar;
};
#endif // LOGMSG_H
#include "logmsg.h"
LogMsg* LogMsg::ms_manager{ nullptr };
bool LogMsg::ms_IsWorking{ false };
std::mutex LogMsg::ms_WorkStarusLock;
std::once_flag LogMsg::ms_CreateOnce;
QString LogMsg::ms_LogDirPath;
LogMsg::LogMsgType LogMsg::ms_LogMstType;
QString LogMsg::ms_TrailMSG;
LogMsg::LogMsg(const LogMsgType logmsgtype)
{
ms_LogMstType = logmsgtype;
ms_IsWorking = true;
m_WriteBufferThread = std::thread(&LogMsg::WriteBufferThread, this);
}
LogMsg::~LogMsg()
{
try
{
ms_IsWorking = false;
std::lock(ms_WorkStarusLock, ms_manager->m_LogMsgBufDeqLock);
std::lock_guard<std::mutex> lock1(ms_WorkStarusLock, std::adopt_lock);
std::lock_guard<std::mutex> lock2(ms_manager->m_LogMsgBufDeqLock, std::adopt_lock);
//close write thread
m_BufferCondVar.notify_all();
if (m_WriteBufferThread.joinable())
{
m_WriteBufferThread.join();
}
//write buffer
const auto vecsize = ms_manager->m_LogMsgBufVec.size();
if (vecsize > 0)
{
OpenFile();
if (nullptr != m_OutFile)
{
QTextStream text_stream(ms_manager->m_OutFile);
text_stream.setCodec("UTF-8");
for (size_t i = 0; i < vecsize; ++i)
{
text_stream << ms_manager->m_LogMsgBufVec[i];
}
text_stream.flush();
ms_manager->m_LogMsgBufVec.clear();
}
CloseFile();
}
}
catch (...)
{
std::cout << "~LogMsg throw exception msg=" /*<< expt.what()*/ << std::endl;
}
}
void LogMsg::OpenFile()
{
if (m_OutFile)
{
return;
}
const QString LogFileName = QDateTime::currentDateTime().toString("yyyy-MM-dd") + ".log";
const QDir dir(ms_LogDirPath);
if (!dir.exists())
{
int tryamount{ 0 };
bool rval{ false };
do
{
if (tryamount++ >= 3)
{
std::cout << "mkdir failed";
break;
}
rval = dir.mkpath(ms_LogDirPath);
} while (!rval);
}
QString logfilepath = ms_LogDirPath + QDir::separator() + LogFileName;
logfilepath = QDir::toNativeSeparators(logfilepath);
m_OutFile = new QFile(logfilepath);
if (m_OutFile)
{
m_FileIsOpen = m_OutFile->open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Unbuffered);
}
}
void LogMsg::CloseFile()
{
if (nullptr != m_OutFile)
{
m_OutFile->flush();
m_OutFile->close();
SAFEDELETE(m_OutFile);
}
}
void LogMsg::WriteBufferThread()
{
QDateTime lastcheckdatetime;
while (ms_IsWorking)
{
std::unique_lock<std::mutex> ulock(m_BufferMutex);
m_BufferCondVar.wait(ulock, [this] {
return (!m_LogMsgBufVec.empty() || !ms_IsWorking);
});
if (!ms_IsWorking)
{
break;
}
if (!ms_WorkStarusLock.try_lock())
{
continue;
}
if (ms_IsWorking && !m_LogMsgBufVec.empty())
{
std::vector<QString> bufque;
{
std::lock_guard<std::mutex> veclock(m_LogMsgBufDeqLock);
bufque.swap(m_LogMsgBufVec);
}
const auto nowtime = QDateTime::currentDateTime();
if (!lastcheckdatetime.isValid() || 0 != nowtime.daysTo(lastcheckdatetime))
{
CloseFile();
OpenFile();
}
lastcheckdatetime = nowtime;
if (nullptr != m_OutFile && m_FileIsOpen)
{
const auto vecsize = bufque.size();
QTextStream text_stream(m_OutFile);
text_stream.setCodec("UTF-8");
for (size_t i = 0; i < vecsize; ++i)
{
text_stream << bufque[i];
}
text_stream.flush();
}
else
{
std::cout << "open logfile failed";
}
}
else if (!ms_IsWorking)
{
ms_WorkStarusLock.unlock();
break;
}
ms_WorkStarusLock.unlock();
}
CloseFile();
}