在编程开发中,日志功能至关重要,对于在开发期间或者是程序上线后,都有助于排查问题;

对于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 应用中优化本地部署大模型的性能…