Qt有两种实现记录日志的方式,第一种是安装自定义的Qt消息处理程序,自动输出程序产生的调试消息、警告、关键和致命错误消息的函数;第二种是自定义一个类,可以在程序指定位置打印输出指定的内容。
自定义消息处理函数,然后安装该函数,注意此时QDebug的消息将会输出在日志文件,Qt程序调式时不再打印。
main.cpp
#include "widget.h"
#include
#include
#include
#include
#include
#include
void outputMessage(QtMsgType type,const QMessageLogContext &context,const QString &msg);
int main(int argc, char *argv[])
{
qInstallMessageHandler(outputMessage);//安装消息处理程序
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
void outputMessage(QtMsgType type,const QMessageLogContext &context,const QString &msg)
{
static QMutex mutex;
mutex.lock();
QByteArray localMsg = msg.toLocal8Bit();
QString text;
switch (type)
{
case QtDebugMsg:
text = QString("Debug:");
break;
case QtWarningMsg:
text = QString("Warning:");
break;
case QtCriticalMsg:
text = QString("Critical:");
break;
case QtFatalMsg:
text = QString("Fatal:");
break;
default:
text = QString("Debug:");
}
// 设置输出信息格式
QString context_info = QString("File:(%1) Line:(%2)").arg(QString(context.file)).arg(context.line); // F文件L行数
QString strDateTime = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
//QString strMessage = QString("%1 %2 \t%3 \t%4").arg(text).arg(context_info).arg(strDateTime).arg(msg);
QString strMessage = QString("%1 \t%2 \t%3").arg(text).arg(strDateTime).arg(msg);
// 输出信息至文件中(读写、追加形式)
QFile file(QString(QDateTime::currentDateTime().toString("yyyy-MM-dd").append("-log.txt")));
file.open(QIODevice::ReadWrite | QIODevice::Append);
QTextStream stream(&file);
stream << strMessage << "\r\n";
file.flush();
file.close();
// 解锁
mutex.unlock();
}
定义一个日志类,实现输出日志到指定文件的接口,在需要输出日志的地方调用接口即可。
clog.h
#ifndef CLOG_H
#define CLOG_H
#include
class CLog : public QObject
{
Q_OBJECT
public:
/*!
* @brief 日志等级
*/
enum CLOG_LEVEL
{
RINFO, /*!< 提示 */
RWARNING, /*!< 警告 */
RERROR /*!< 错误 */
};
Q_FLAG(CLOG_LEVEL)
struct LogConfig
{
bool isRecord2File; /*!< 是否记录到文件 */
int level; /*!< 记录日志等级,大于等于此level的日志将被记录 */
};
CLog();
void setLogLevel(const CLog::CLOG_LEVEL & level);
CLog::CLOG_LEVEL getLogLevel(){return logLevel;}
static bool init(CLog::LogConfig & logConfig);
static bool createDir(QString dirPath);
static void log(CLOG_LEVEL nLevel,const char * fileDesc,const char * functionDesc, int lineNum,const char* data, ...);
private:
static QString getLeveDesc(CLOG_LEVEL level);
private:
static bool isRecord2File;
static bool isFileReady;
static CLOG_LEVEL logLevel;
};
#ifdef Q_OS_WIN
#define FILE_SEPARATOR '\\'
#else
#define FILE_SEPARATOR '/'
#endif
#define FUNC_SEPARATOR '::'
#ifndef QT_NO_DEBUG
#define __FILENAME__ (strrchr(__FILE__, FILE_SEPARATOR) ? (strrchr(__FILE__, FILE_SEPARATOR) + 1):__FILE__)
#define __FUNNAME__ (strrchr(__FUNCTION__,FUNC_SEPARATOR)?(strrchr(__FUNCTION__, FUNC_SEPARATOR) + 1):__FUNCTION__)
#else
#define __FILENAME__ NULL
#define __FUNNAME__ NULL
#endif
#define CLOG_INFO(...) CLog::log(CLog::RINFO,__FILENAME__,__FUNNAME__,__LINE__, __VA_ARGS__)
#define CLOG_WARNING(...) CLog::log(CLog::RWARNING, __FILENAME__,__FUNNAME__, __LINE__,__VA_ARGS__)
#define CLOG_ERROR(...) CLog::log(CLog::RERROR,__FILENAME__,__FUNNAME__,__LINE__,__VA_ARGS__)
#endif // CLOG_H
clog.cpp
#include "clog.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
const char PATH_LogPath[] = "/../logs";
const char Suffix[] = ".log";
#define MAX_LOG_LENGH 1024
bool CLog::isFileReady = false;
bool CLog::isRecord2File = true;
CLog::CLOG_LEVEL CLog::logLevel = CLog::RINFO; //默认是info级
QFile localFile;
QMutex mutex;
CLog::CLog()
{
}
void CLog::setLogLevel(const CLog::CLOG_LEVEL &level)
{
logLevel = level;
}
bool CLog::init(LogConfig &logConfig)
{
isRecord2File = logConfig.isRecord2File;
logLevel = (CLog::CLOG_LEVEL)logConfig.level;
QString logDir = qApp->applicationDirPath() + QString(PATH_LogPath);
if(createDir(logDir))
{
QString fileName = logDir + QDir::separator() + QDate::currentDate().toString("yyyy-MM-dd") + QString(Suffix);
localFile.setFileName(fileName);
if(localFile.open(QFile::WriteOnly|QFile::Append|QFile::Text))
{
isFileReady = true;
}
}
return isFileReady;
}
bool CLog::createDir(QString dirPath)
{
QFileInfo fileInfo(dirPath);
if(!fileInfo.exists())
{
QDir tmpDir;
return tmpDir.mkpath(dirPath);
}
return true;
}
void CLog::log(CLOG_LEVEL nLevel, const char *fileDesc, const char *functionDesc, int lineNum, const char* data, ...)
{
QMutexLocker locker(&mutex);
if(isFileReady && nLevel >= logLevel)
{
QString recordInfo = QString("[%1]").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss:zzz"));
recordInfo.append(getLeveDesc(nLevel));
#ifndef QT_NO_DEBUG
recordInfo.append(QString("[%1:%2:%3]").arg(fileDesc).arg(functionDesc).arg(lineNum));
#endif
va_list vlist;
va_start(vlist,data);
QByteArray byteArray;
#if defined(Q_OS_WIN)
int recordLen = _vscprintf(data,vlist);
byteArray.resize(recordLen);
#else
byteArray.resize(1024);
#endif
vsprintf(byteArray.data(),data,vlist);
recordInfo.append(byteArray);
va_end(vlist);
recordInfo.append("\n");
if(isRecord2File){
localFile.write(recordInfo.toLocal8Bit().data(),recordInfo.toLocal8Bit().length());
localFile.flush();
}else{
// qDebug()<
}
}
}
QString CLog::getLeveDesc(CLog::CLOG_LEVEL level)
{
#if (QT_VERSION > 0x050500)
static QMetaEnum metaEnum = QMetaEnum::fromType<CLog::CLOG_LEVEL>();
return QString("[%1]").arg(metaEnum.key(level));
#else
switch(level)
{
case CLOG_LEVEL::INFO:
return "[INFO]";
case CLOG_LEVEL::INFO:
return "[WARNING]";
case CLOG_LEVEL::INFO:
return "[ERROR]";
}
#endif
}
简单调用示例:
CLog::LogConfig logConfig;
logConfig.isRecord2File = true;
logConfig.level = 0;
CLog::init(logConfig);
CLOG_INFO("df2008");
输出日志:
[2020-12-03 13:15:14:549][RINFO][widget.cpp:Widget:17]df2008