文章目录
qDebug重定向到 file 和 console
Qt 通过
qInstallMessageHandler
函数可以自定义qDebug等输出重定向,综合网上各路大神的讲解,整理实现如下:
- 以日期时间命名log文件,例如:20220207_141216.log
- log输出文件夹只保留10份历史log
-
Release 版本默认不包含函数名和行数等信息,需要在 .pro 文件中加入
DEFINES += QT_MESSAGELOGCONTEXT
其实现也很简单,直接上代码
LogHandler.h
#ifndef LOGHANDLER_H
#define LOGHANDLER_H
#include <iostream>
#include <QDebug>
#include <QDateTime>
#include <QMutexLocker>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QTimer>
#include <QTextStream>
#include <QTextCodec>
const int g_logLimitSize = 5;
struct LogHandlerPrivate {
LogHandlerPrivate();
~LogHandlerPrivate();
static void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg);
QDir logDir; // 日志文件夹
QTimer flushLogFileTimer; // 刷新输出到日志文件的定时器
static QFile *logFile; // 日志文件
static QTextStream *logOut; // 输出日志的 QTextStream,使用静态对象就是为了减少函数调用的开销
static QMutex logMutex; // 同步使用的 mutex
};
class LogHandler {
public:
void installMessageHandler(); // 给Qt安装消息处理函数
void uninstallMessageHandler(); // 取消安装消息处理函数并释放资源
static LogHandler& Get() {
static LogHandler m_logHandler;
return m_logHandler;
}
private:
LogHandler();
LogHandlerPrivate *d;
};
#endif // LOGHANDLER_H
LogHandler.cpp
#include "LogHandler.h"
// 初始化 static 变量
QMutex LogHandlerPrivate::logMutex;
QFile* LogHandlerPrivate::logFile = nullptr;
QTextStream* LogHandlerPrivate::logOut = nullptr;
LogHandlerPrivate::LogHandlerPrivate() {
logDir.setPath("log");
if (!logDir.exists())
logDir.mkpath(".");
//保留10份历史log
logDir.setFilter(QDir::AllEntries | QDir::NoDotAndDotDot); // 过滤掉 ". or .." 文件
QFileInfoList fileList = logDir.entryInfoList();
int currentCount = logDir.count();
foreach (QFileInfo f, fileList ) {
std::cout << f.baseName().toStdString() <<std::endl;
if (currentCount <= 10)
break;
logDir.remove(f.absoluteFilePath());
currentCount--;
}
// 新建本次启动log文件
QString logPath = logDir.absoluteFilePath(QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss.log"));
if (logFile == nullptr) {
logFile = new QFile(logPath);
logOut = (logFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append)) ? new QTextStream(logFile) : nullptr;
if (logOut != nullptr)
logOut->setCodec("UTF-8");
}
// 定时刷新日志输出缓存到文件,单位ms
flushLogFileTimer.setInterval(10);
flushLogFileTimer.start();
QObject::connect(&flushLogFileTimer, &QTimer::timeout, [] {
QMutexLocker locker(&LogHandlerPrivate::logMutex);
if (nullptr != logOut)
logOut->flush();
});
}
LogHandlerPrivate::~LogHandlerPrivate() {
if (nullptr != logFile) {
logFile->flush();
logFile->close();
delete logOut;
delete logFile;
logOut = nullptr;
logFile = nullptr;
}
}
void LogHandlerPrivate::messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) {
QMutexLocker locker(&LogHandlerPrivate::logMutex);
QString level;
switch (type) {
case QtDebugMsg:
level = "debug";
break;
case QtInfoMsg:
level = "info ";
break;
case QtWarningMsg:
level = "warn ";
break;
case QtCriticalMsg:
level = "err ";
break;
case QtFatalMsg:
level = "fatal";
break;
default:
break;
}
// 输出到标准输出: Windows 下 std::cout 使用 GB2312,而 msg 使用 UTF-8,但是程序的 Local 也还是使用 UTF-8
#if defined(Q_OS_WIN)
QByteArray localMsg = QTextCodec::codecForName("GB2312")->fromUnicode(msg); //msg.toLocal8Bit();
#else
QByteArray localMsg = msg.toLocal8Bit();
#endif
std::cout << std::string(localMsg) << std::endl;
if (nullptr == LogHandlerPrivate::logOut)
return;
// 输出到日志文件, 格式: 时间 [Level] 函数 行数: 消息
(*LogHandlerPrivate::logOut) << QString("%1 [%2] %3 %4: %5\n")
.arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"))
.arg(level, -5, ' ').arg(context.line).arg(context.function).arg(msg);
}
LogHandler::LogHandler() : d(nullptr) {}
// 给Qt安装消息处理函数
void LogHandler::installMessageHandler() {
QMutexLocker locker(&LogHandlerPrivate::logMutex); // 类似C++11的lock_guard,析构时自动解锁
if (nullptr == d) {
d = new LogHandlerPrivate();
qInstallMessageHandler(LogHandlerPrivate::messageHandler); // 给 Qt 安装自定义消息处理函数
}
}
// 取消安装消息处理函数并释放资源
void LogHandler::uninstallMessageHandler() {
QMutexLocker locker(&LogHandlerPrivate::logMutex);
qInstallMessageHandler(nullptr);
delete d;
d = nullptr;
}
main.cpp
#include "LogHandler.h"
#include <QApplication>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
// [[1]] 安装消息处理函数
LogHandler::Get().installMessageHandler();
// [[2]] 输出测试,查看是否写入到文件
qDebug() << "Hello";
qInfo() << "当前时间是: " << QTime::currentTime().toString("hh:mm:ss");
qWarning() << QString("God bless you!");
qCritical("This is a critical message at thisisqt.com");
//qFatal("This is a fatal message at thisisqt.com"); //该语句会使程序直接崩溃,且无log输出
// [[3]] 取消安装自定义消息处理,然后启用
LogHandler::Get().uninstallMessageHandler();
qDebug() << "........\n"; // 不写入日志
app.exit();
return 0;
}
效果如下
附录
QMutexLocker
其旨在用于简化lock、unlock操作。
常规lock、unlock,应用场景示例(qt 官方示例)
int complexFunction(int flag)
{
mutex.lock();
int retVal = 0;
switch (flag) {
case 0:
case 1:
retVal = moreComplexFunction(flag);
break;
case 2:
{
int status = anotherFunction();
if (status < 0) {
mutex.unlock();
return -2;
}
retVal = status + flag;
}
break;
default:
if (flag > 10) {
mutex.unlock();
return -1;
}
break;
}
mutex.unlock();
return retVal;
}
简化后代码如下,QMutexLocker 是在函数的入口与出口处lock、unlock
int complexFunction(int flag)
{
QMutexLocker locker(&mutex);
int retVal = 0;
switch (flag) {
case 0:
case 1:
return moreComplexFunction(flag);
case 2:
{
int status = anotherFunction();
if (status < 0)
return -2;
retVal = status + flag;
}
break;
default:
if (flag > 10)
return -1;
break;
}
return retVal;
}
struct in C and C++
- C++ 中struct 可以有成员函数(构造函数、析构函数、常规函数和虚函数)、静态成员(静态成员函数和静态成员变量),C 中不可以
- struct 默认访问属性是 public,class 默认访问属性是 private
- struct 继承 struct 或 class 默认是 public 继承,class 继承 struct 或 class 默认是 private 继承
–end
版权声明:本文为tianzong2019原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。