QT 日志 - qInstallMessageHandler將qDebug()打印內容輸出到文件

在編程開發中,日志功能至關重要,對于在開發期間或者是程序上線后,都有助于排查問題;

對于C/C++和QT方向,日志庫有log4cpp、plog、log4qt等,本篇文章將使用qt自帶的日志方式去實現。

定義日志函數:

void Log(QtMsgType type, const QMessageLogContext &context, const QString &msg);

函數名可隨意,但參數必須固定3個,如上面代碼;

QtMsgType是一個枚舉,記錄了多種打印類型;

enum QtMsgType { QtDebugMsg, QtWarningMsg, QtCriticalMsg, QtFatalMsg, QtInfoMsg, QtSystemMsg = QtCriticalMsg };

QMessageLogContext是日志上下文,可獲得qDebug()打印時所在的函數名和行號等;

class QMessageLogContext
{Q_DISABLE_COPY(QMessageLogContext)
public:Q_DECL_CONSTEXPR QMessageLogContext(): version(2), line(0), file(nullptr), function(nullptr), category(nullptr) {}Q_DECL_CONSTEXPR QMessageLogContext(const char *fileName, int lineNumber, const char *functionName, const char *categoryName): version(2), line(lineNumber), file(fileName), function(functionName), category(categoryName) {}void copy(const QMessageLogContext &logContext);int version;int line;const char *file;const char *function;const char *category;private:friend class QMessageLogger;friend class QDebug;
};

QString則是qDebug()打印輸出的內容。

如下定義一個日志函數:

void Log(QtMsgType type, const QMessageLogContext &context, const QString &msg) {//加鎖,防止多線程中qdebug太頻繁導致崩潰static QMutex mutex;QMutexLocker locker(&mutex);QString strContent;// 根據日志類型添加不同前綴 switch (type) {case QtDebugMsg:strContent = QString("[Debug] %1").arg(msg);break;case QtWarningMsg:strContent = QString("[Warning] %1").arg(msg);break;case QtCriticalMsg:strContent = QString("[Critical] %1").arg(msg);break;case QtFatalMsg:strContent = QString("[Fatal] %1").arg(msg);break;}// 構建完整日志信息 QString strMessage = QString("[%1] [%2:%3] %4").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz")).arg(context.function).arg(context.line).arg(strContent);// 寫入文件/* 在這里處理將 strMessage 內容寫入文件中 */
}

然后調用qInstallMessageHandler函數安裝日志鉤子:

qInstallMessageHandler(Log);

之后就可以將qDebug()、qWorning()等打印內容輸出到文件中。

如果是卸載的話,直接參數傳0即可:qInstallMessageHandler(0);

qDebug() << "調試信息";

qInfo() << "信息";

qWarning() << "警告信息";

qCritical() << "關鍵錯誤、嚴重錯誤";

qFatal:致命錯誤;

下面提供一個實現好的日志類,提供參考:

loghelper.h

#ifndef LOGHELPER_H
#define LOGHELPER_H#include <QObject>class QFile;
class QMutex;#ifdef quc
#if (QT_VERSION < QT_VERSION_CHECK(5,7,0))
#include <QtDesigner/QDesignerExportWidget>
#else
#include <QtUiPlugin/QDesignerExportWidget>
#endifclass QDESIGNER_WIDGET_EXPORT SaveLog : public QObject
#else
class LogHelper : public QObject
#endif
{Q_OBJECT
public:static LogHelper *Instance();explicit LogHelper(QObject *parent = 0);~LogHelper();private:static QScopedPointer<LogHelper> self;static QMutex mutexInstance;//文件對象QFile *file;//日志文件路徑QString path;//日志文件名稱QString name;// 當前日志文件對應的日期(yyyy-MM-dd)QString currentDate;//日志文件完整名稱QString fileName;public slots://啟動日志服務void start();//暫停日志服務void stop();//保存日志void save(const QString &content);//設置日志文件存放路徑void setPath(const QString &path);QString getPath() const;//設置日志文件名稱void setName(const QString &name);QString getName() const;
};#endif // LOGHELPER_H

loghelper.cpp

#include "loghelper.h"
#include <QFile>
#include <QDir>
#include <QDateTime>
#include <QApplication>
#include <QTimer>
#include <QStringList>
#include <QTextStream>
#include <QMutex>
#include <QDebug>// 初始化靜態成員
QMutex LogHelper::mutexInstance;
QScopedPointer<LogHelper> LogHelper::self;//日志重定向
#if (QT_VERSION <= QT_VERSION_CHECK(5,0,0))
void Log(QtMsgType type, const char *msg)
#else
void Log(QtMsgType type, const QMessageLogContext &context, const QString &msg)
#endif
{//加鎖,防止多線程中qdebug太頻繁導致崩潰static QMutex mutex;QMutexLocker locker(&mutex);QString strContent;// 根據日志類型添加不同前綴switch (type) {case QtDebugMsg:strContent = QString("[Debug] %1").arg(msg);break;case QtWarningMsg:strContent = QString("[Warning] %1").arg(msg);break;case QtCriticalMsg:strContent = QString("[Critical] %1").arg(msg);break;case QtFatalMsg:strContent = QString("[Fatal] %1").arg(msg);break;case QtInfoMsg:strContent = QString("[Info] %1").arg(msg);break;}// 構建完整日志信息QString strMessage = QString("[%1] [%2:%3] %4").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz")).arg(context.function).arg(context.line).arg(strContent);// 寫入文件LogHelper::Instance()->save(strMessage);
}LogHelper *LogHelper::Instance()
{if (self.isNull()) {QMutexLocker locker(&mutexInstance);if (self.isNull()) {self.reset(new LogHelper);}}return self.data();
}LogHelper::LogHelper(QObject *parent) : QObject(parent)
{file = new QFile(this);//默認取應用程序根目錄setPath(qApp->applicationDirPath() + "/logs");//默認取應用程序可執行文件名稱QFileInfo appInfo(QApplication::applicationFilePath());setName(appInfo.baseName());fileName = "";// 獲取當前日期 (格式:yyyy-MM-dd)currentDate = QDate::currentDate().toString("yyyy-MM-dd");// 構建新文件名fileName = QString("%1/%2_log_%3.txt").arg(path, name, currentDate);QFileInfo info(fileName);if (!info.exists()) {currentDate = "";}
}LogHelper::~LogHelper()
{file->close();
}//安裝日志鉤子,輸出調試信息到文件,便于調試
void LogHelper::start()
{
#if (QT_VERSION <= QT_VERSION_CHECK(5,0,0))qInstallMsgHandler(Log);
#elseqInstallMessageHandler(Log);
#endif
}//卸載日志鉤子
void LogHelper::stop()
{
#if (QT_VERSION <= QT_VERSION_CHECK(5,0,0))qInstallMsgHandler(0);
#elseqInstallMessageHandler(0);
#endif
}void LogHelper::save(const QString &content)
{// 獲取當前日期 (格式:yyyy-MM-dd)QString today = QDate::currentDate().toString("yyyy-MM-dd");// 檢查日期是否變化if (currentDate != today) {currentDate = today;// 關閉已打開的文件if (file->isOpen()) {file->close();}// 構建新文件名QString newFileName = QString("%1/%2_log_%3.txt").arg(path, name, currentDate);// 確保目錄存在QDir dir;if (!dir.exists(path)) {dir.mkpath(path);}// 打開新日志文件file->setFileName(newFileName);if (!file->open(QIODevice::WriteOnly | QIODevice::Append | QFile::Text)) {// 直接輸出到stderr,避免遞歸調用日志系統fprintf(stderr, "無法打開日志文件: %s\n", fileName.toUtf8().constData());return;}fileName = newFileName;}// 確保文件已打開if (!file->isOpen()) {// 打開新日志文件file->setFileName(fileName);if (!file->open(QIODevice::WriteOnly | QIODevice::Append | QFile::Text)) {// 直接輸出到stderr,避免遞歸調用日志系統fprintf(stderr, "無法打開日志文件: %s\n", fileName.toUtf8().constData());return;}}// 寫入日志內容 (添加換行符)QTextStream logStream(file);logStream << content << "\n";logStream.flush();  // 刷新緩沖區確保及時寫入
}void LogHelper::setPath(const QString &path)
{QDir dirPath(path);if(!dirPath.exists()){dirPath.mkdir(path);}this->path = path;// 重置當前日期,強制下次寫入時重新打開文件currentDate = "";
}void LogHelper::setName(const QString &name)
{this->name = name;// 重置當前日期,強制下次寫入時重新打開文件currentDate = "";
}QString LogHelper::getPath() const
{return path;
}QString LogHelper::getName() const
{return name;
}

使用:

#include "loghelper.h"LogHelper::Instance()->start(); //啟動日志鉤子qDebug() << "qDebug 測試日志打印、、、";
qWarning() << "qWarning 測試日志打印、、、";
qCritical() << "qCritical 測試日志打印、、、";
qInfo() << "qInfo 測試日志打印、、、";

注意,如果是在統信UOS系統ARM架構運行,因為統信系統的原因,默認情況下,只會打印qWarning、qCritical、qFatal三個級別的,qDebug和qInfo將不會處理;

如果希望qDebug和qInfo也能打印到文件,需要設置環境變量;

QT環境:

需要在main函數的最前方設置:

qputenv("QT_LOGGING_RULES", "*.debug=false;default.debug=true");
#include "mainwidget.h"
#include <QApplication>int main(int argc, char *argv[])
{// 強制啟用默認的debug日志打印輸出;否則在UOS系統里qDebug()無法將內容輸出到日志文件;Window環境不影響
#ifdef Q_OS_UNIXqputenv("QT_LOGGING_RULES", "*.debug=false;default.debug=true");
#endifQApplication a(argc, argv);MainWidget w;//w.show();w.showFullScreen();return a.exec();
}

windows環境不受影響,可正常使用!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/87652.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/87652.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/87652.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

記錄一下seata啟動403問題

1.現象&#xff1a;啟動報錯可能是403&#xff0c;或是是密碼錯誤一般是nacos加了認證&#xff0c;seata配置nacos賬號密碼的時候就啟動不了。可能是密碼錯誤&#xff0c;最有可能是seata版本太低導致的。1.4.2以及一下的版本應該都有這個問題2.問題密碼不能有特殊符號如&#…

【STM32實踐篇】:GPIO 詳解

文章目錄GPIO 基本結構GPIO 工作模式GPIO 基本結構 右邊的紅框是I/O引腳&#xff0c;這個I/O引腳就是我們可以看到的芯片實物的引腳&#xff0c;其他部分都是GPIO的內部結構。 保護二極管 上方二極管用于防過壓保護&#xff0c;當I/O引腳電壓高于 V_DD 二極管導通壓降?時&…

#include

關于 C 中的 include <>和 include “” 這兩種形式&#xff0c;區別其實是關于“搜索路徑”和“優先級”的。讓我詳細為你講解。 1. 簡單區別總結 #include <header>&#xff1a;告訴編譯器去“系統標準目錄”或“預定義的標準路徑”中查找頭文件&#xff08;比如…

永磁同步電機參數辨識算法--帶遺忘因子的遞推最小二乘法辨識

一、原理介紹之前已經介紹了遞推最小二乘法進行電氣參數辨識&#xff0c;在實時參數辨識中&#xff0c;協方差矩陣P和增益矩陣K是用于更新參數估計的重要工具&#xff0c;而系統參數變化時&#xff0c;P、K矩陣會逐漸減小&#xff0c;導致數據飽和。數據飽和與參數遲滯是實時參…

JVM 知識點

一、JVM 概述JVM&#xff08;Java Virtual Machine&#xff09;即 Java 虛擬機&#xff0c;它是 Java 編程語言的核心組件之一&#xff0c;負責執行 Java 程序。JVM 使得 Java 程序可以實現“一次編寫&#xff0c;到處運行”的特性&#xff0c;因為它提供了一個抽象的運行環境&…

windows裝機

1、制作啟動盤 2、制作啟動盤 啟動盤中含有WinPE系統和ISO 3、從U盤啟動&#xff0c;加載ISO 4、執行ISO中的setup安裝win10 5、之后從C盤啟動進入win10系統 6、安裝“華為電腦管家”,安裝驅動 華為電腦管家官方下載-筆記本驅動更新 | 華為官網 7、下載安裝必要軟件 https://…

提示技術系列(13)——ReAct

什么是提示技術&#xff1f; 提示技術是實現提示工程目標的具體技術手段&#xff0c;是提示工程中的“工具庫”。 什么又是提示工程&#xff1f; 提示工程是指通過設計、優化和迭代輸入到大語言模型&#xff08;LLM&#xff09;的提示&#xff08;Prompt&#xff09;&#xff…

【SVO】klt與極限搜索塊匹配findEpipolarMatchDirect

Matcher::findEpipolarMatchDirect 函數邏輯與原理分析 核心目標&#xff1a; 在極線上搜索參考幀特征點 ref_ftr 在當前幀 cur_frame 中的最佳匹配點&#xff0c;并通過三角化計算深度。 關鍵步驟解析&#xff1a; 1. 極線端點計算&#xff1a; const BearingVector A T_…

C 語言基礎入門:基本數據類型與運算符詳解

一、基本數據類型C 語言提供了豐富的基本數據類型&#xff0c;用于存儲不同類型的數據&#xff0c;主要包括整數類型、浮點類型和布爾類型。1. 整數類型整數類型用于存儲整數&#xff0c;根據是否帶符號以及占用存儲空間的不同&#xff0c;可進一步細分&#xff1a;類型名占用存…

應用在核電行業的虛擬現實解決方案

核能領域正處于創新與責任的交匯點。盡管核反應堆提供了高效且可持續的能源&#xff0c;但由于放射性物質的危險性&#xff0c;其也帶來了獨特挑戰。虛擬現實&#xff08;VR&#xff09;技術正通過為遠程操作、應急響應和放射性物質處理提供先進解決方案&#xff0c;徹底革新這…

CTF Web的數組巧用

PHP數組繞過intval和preg_match的CTF技巧 原題目 <?php include("flag.php"); // 引入flag文件&#xff0c;flag變量在這里定義 show_source("index.php"); // 顯示index.php文件的源碼&#xff08;方便選手查看&#xff09;// 判斷是否通過GET方式傳入…

vue2+elementui使用compressorjs壓縮上傳的圖片

首先是npm install compressorjs 然后新建一個compressorjs.js的文件 import Compressor from "compressorjs";// 默認壓縮配置 const DEFAULT_COMPRESS_OPTIONS {quality: 0.6, // 默認壓縮質量 (0-1)maxWidth: 1920, // 最大寬度maxHeight: 1080, // 最大高度con…

GPIO詳解:不僅僅是輸入輸出那么簡單

GPIO詳解&#xff1a;不僅僅是輸入輸出那么簡單 “別小看一個小小的引腳&#xff0c;它可是 MCU 世界的社交之門。” &#x1f44b; 先打個招呼&#xff1a;什么是 GPIO&#xff1f; GPIO&#xff0c;全稱是 General Purpose Input/Output —— 通用輸入輸出口。 簡單說&…

深度學習5(深層神經網絡 + 參數和超參數)

深層神經網絡簡介 深層神經網絡是機器學習中一種重要的模型&#xff0c;它通過增加網絡的“深度”&#xff08;即隱藏層的數量&#xff09;來提升模型對復雜數據的表示和學習能力。同淺層類似&#xff0c;也分為三個部分&#xff1a; 輸入層&#xff1a;接收原始數據&#xff…

時間復雜度與空間復雜度分析

一、什么是復雜度&#xff1f; 1.1 為什么需要復雜度分析&#xff1f; 假設你寫了兩個程序來解決同一個問題&#xff0c;如何判斷哪個程序更好&#xff1f;我們不能只看運行時間&#xff0c;因為&#xff1a; 不同電腦性能不同同一電腦在不同時刻狀態也不同數據規模不同&#x…

上下文工程:從提示詞到自動化流程的AI應用新范式

上下文工程&#xff1a;從提示詞到自動化流程的 AI 應用新范式 一、背景與概述&#xff1a;從提示詞工程到上下文工程的演進 隨著大語言模型 (LLM) 技術的飛速發展&#xff0c;AI 應用開發正經歷從 “提示詞工程”(Prompt Engineering) 到 “上下文工程”(Context Engineerin…

HTML網頁應用打包Android App 完整實踐指南

技術準備與工具下載 必需工具清單 在開始之前&#xff0c;需要準備以下開發工具&#xff1a; Android Studio官網&#xff1a;https://developer.android.com/studio HBuilderX官網&#xff1a;https://www.dcloud.io/hbuilderx.html 離線SDK下載&#xff1a;https://nati…

簡單 Python 爬蟲程序設計

爬蟲是獲取網頁數據的常用工具&#xff0c;我們一起來設計一個基于 requests 和 BeautifulSoup 的簡單爬蟲&#xff0c;它可以獲取網頁內容并提取文本信息。 所需庫安裝 首先需要安裝兩個必要的庫&#xff1a; pip install requests beautifulsoup4 完整代碼 import reques…

AUTOSAR圖解==>AUTOSAR_AP_EXP_ARAComAPI

AUTOSAR ara::com API詳解 自適應平臺通信API技術詳解 目錄 1. 概述2. ara::com API架構 2.1 Proxy/Skeleton架構2.2 通信方式2.3 服務連接方式 3. 詳細API說明 3.1 Proxy類3.2 Skeleton類3.3 實例標識符3.4 通信組 4. ara::com API狀態管理 4.1 服務生命周期4.2 事件與方法狀…

Spring Boot + 本地部署大模型實現:優化與性能提升

在將大語言模型集成到 Spring Boot 應用中時&#xff0c;性能優化是一個關鍵環節。本地部署的大模型雖然提供了強大的功能&#xff0c;但也可能帶來一些性能挑戰&#xff0c;如響應時間較長、資源占用較高等問題。本文將介紹如何在 Spring Boot 應用中優化本地部署大模型的性能…