QT中日志的使用案例 || 自动创建、管理、保存QT日志数据

news/2025/2/26 0:11:32

目录

1.quiwidget.cpp

2.widget.cpp

3.widget.h

4.在需要记录日志的地方直接将信息插入即可

1. 释放 `m_fileLog` 和 `m_textStream`

1.1 为什么要关闭和删除 `m_fileLog` 和 `m_textStream`?

1.2 如果不这样做会有什么坏处?

 3. 总结

4.参考文章


需求分析:

        想实现在qt代码中对软件运行过程中容易出现的问题进行记录,将电量异常分析日志与平时详细日志进行分开存储。同时为了避免占用内存过大影响使用,需要每日对不是今天的文件夹进行清理,由于电量分析日志占用内存不大,目前,只对详细日志进行每日删除的操作以下是具体的一些实现思路。

1.quiwidget.cpp

#define TIMEMS          qPrintable(QTime::currentTime().toString("HH:mm:ss zzz"))
#define TIME            qPrintable(QTime::currentTime().toString("HH:mm:ss"))
#define QDATE           qPrintable(QDate::currentDate().toString("yyyy-MM-dd"))
#define QTIME           qPrintable(QTime::currentTime().toString("HH-mm-ss"))
#define DATETIME        qPrintable(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss"))
#define STRDATETIME     qPrintable(QDateTime::currentDateTime().toString("yyyy-MM-dd-HH-mm-ss"))
#define STRDATETIMEMS   qPrintable(QDateTime::currentDateTime().toString("yyyy-MM-dd-HH-mm-ss-zzz"))
#define RECORDFILE      qPrintable(QDateTime::currentDateTime().toString("yyyyMMdd_HHmmss"))

2.widget.cpp

void Widget::writeLog(const QString &log, bool isChargeLog) {
    QString currentDate = QDate::currentDate().toString("yyyy-MM-dd");

    // 根据isChargeLog标志判断是充电异常日志还是详细日志
    QString logDir, logFileName,logDir2;

    // 选择路径和文件名
    if (isChargeLog) {
        logDir = QString("%1\\ChargeRecord\\%2").arg(m_strExeDir).arg(QDATE);
        logFileName = QString("%1\\ChargeLog_%2.txt").arg(logDir).arg(RECORDFILE);
    } else {
        logDir = QString("%1\\DetailedRecord\\%2").arg(m_strExeDir).arg(QDATE);
        logDir2 = QString("%1\\DetailedRecord").arg(m_strExeDir);
        logFileName = QString("%1\\DetailedRecord.txt").arg(logDir);
        // 删除非今天的文件夹和文件
        cleanupOldLogs(logDir2);
    }
    // 创建文件夹
    QDir temp;
    temp.mkpath(logDir);


    // 如果文件未被创建则创建文件
    if (m_fileLog == nullptr || m_fileLog->fileName() != logFileName) {
        if (m_fileLog != nullptr) {
            delete m_fileLog;  // 删除旧的文件对象
        }
        m_fileLog = new QFile(logFileName);
        bool ok = m_fileLog->open(QIODevice::ReadWrite | QIODevice::Append);
        if (ok) {
            // 赋值stream
            if (m_textStream != nullptr) {
                delete m_textStream;  // 删除旧的流对象
            }
            m_textStream = new QTextStream(m_fileLog);
        }
    }

    // 如果文件流准备好,写入日志
    if (m_textStream != nullptr) {
        QString logWithTimestamp = QString("[%1] %2").arg(DATETIME, log); // 添加时间戳
        *m_textStream << logWithTimestamp << endl;
        m_fileLog->flush();  // 立刻写入以防崩溃

    }
}


void Widget::loadDetailedLog(){
    writeLog(QString("---------start----------"),false);
    writeLog(QString("状态转换m_nCurState【借卡:第一次交互前】 %1").arg(m_nCurState),false);
    writeLog(QString("---------end----------"),false);
}

void Widget::onBtnqqClicked()
{
    ui->widgetLocker->saveSlotJson(m_strExeDir, PARAM_FILE_NAME);
}

// 清除非今天的日志文件夹和文件
void Widget::cleanupOldLogs(const QString &logDir) {
    QDir dir(logDir);
    QStringList allDirs = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);

    // 检查并删除所有不是今天日期的子文件夹
    for (const QString &folder : allDirs) {
        if (folder != QDate::currentDate().toString("yyyy-MM-dd")) {
            //qDebug() << "Deleting old log folder:" << folder;
            QDir oldDir(logDir + "\\" + folder);
            oldDir.removeRecursively();  // 删除该文件夹及其下的所有内容
        }
    }

    // 检查文件夹是否为空(删除空的文件夹)
    if (dir.entryList(QDir::Files).isEmpty() && dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot).isEmpty()) {
        dir.rmdir(logDir);  // 删除空文件夹
    }
}

3.widget.h

//wigdet.h

public:
    void writeLog(const QString &log, bool isChargeLog);

4.在需要记录日志的地方直接将信息插入即可

    //记录日志
    QString num = QString::number(m_stCardInInfo.stSlotParam.nNum);
    QString onsite = QString::number(m_stCardInInfo.stSlotParam.stLt100Info.ucOnsite);
    QString cardNotSite = QString::number(m_stCardInInfo.stSlotParam.nCardNotSite);
    QString enable = QString::number(m_stCardInInfo.stSlotParam.nEnable);
    QString state = QString::number(m_stCardInInfo.stSlotParam.nState);
    QString cardStatus_flag = QString::number(m_stCardOutInfo.stSlotParam.cardStatus_flag);
    QString info = QString("归还的柜门状态 num: %1, Onsite: %2, CardNotSite: %3, Enable: %4, State: %5, bindState:%6")
                   .arg(num)
                   .arg(onsite)
                   .arg(cardNotSite)
                   .arg(enable)
                   .arg(state)
                   .arg(cardStatus_flag);

    writeLog(QString("%1").arg(info),false);

1. 释放 `m_fileLog` 和 `m_textStream`

1.1 为什么要关闭和删除 `m_fileLog` 和 `m_textStream`?

在 Qt 中,`QFile` 和 `QTextStream` 都是资源管理型的类,尤其是 `QFile` 负责文件的打开、关闭和读写操作,`QTextStream` 用于在文件中执行流式写入和读取。

- **关闭文件 (`m_fileLog->close()`)**:
  `QFile` 对象打开文件后,它会保持文件句柄(资源),直到文件被显式关闭。如果在程序结束时没有关闭文件,操作系统可能会认为文件仍然在使用中,导致以下问题:
  - **文件无法正确保存**:某些数据可能没有写入磁盘。
  - **文件句柄泄漏**:每个打开的文件都占用系统资源,如果程序没有释放文件句柄,操作系统可能会因为文件句柄数目耗尽而出现错误,尤其是在打开大量文件时。
  
  关闭文件是文件操作的最佳实践,它确保所有缓冲数据被刷新到磁盘,并且释放操作系统资源。

- **删除 `m_textStream`**:
  `QTextStream` 是一个指向 `QFile` 的流对象,负责将数据写入文件。`QTextStream` 本身是一个包装对象,不负责文件的实际打开和关闭,但是它依赖于 `QFile` 的存在。如果不手动删除 `QTextStream`,它将和 `QFile` 一起被销毁,但没有显式释放内存的话,会存在内存泄漏的风险。

1.2 如果不这样做会有什么坏处?

如果我们不关闭文件和删除 `QTextStream`,会有以下几个问题:

- **内存泄漏**:如果没有删除 `m_textStream`,它指向的内存就不会被释放,导致内存泄漏。
- **文件操作不完整**:如果不关闭文件,`QFile` 可能没有正确地将所有数据写入磁盘。即使没有写入错误,某些数据可能会丢失,因为文件未被正确刷新。
- **资源泄漏**:`QFile` 需要操作系统资源(如文件句柄),如果不手动关闭文件,这些资源就不会被释放。长时间运行的程序可能会导致文件句柄耗尽,导致系统不允许再打开新文件。

 3. 总结

在析构函数中进行资源释放(关闭文件、删除流对象、删除 `ui`)是良好的编程实践,具体原因如下:

- **文件关闭**:防止文件未完全保存、文件句柄泄漏等问题。
- **删除对象**:防止内存泄漏,确保内存得到及时释放。
- **UI 删除**:确保 Qt 控件的内存被及时清理,避免内存泄漏。

### 如果不这样做的坏处:

1. **文件未关闭**:数据可能未被写入文件,文件句柄资源可能泄漏,导致文件操作出错。
2. **内存泄漏**:没有手动删除 `m_textStream` 和其他动态分配的内存,程序可能会在退出时无法释放内存。
3. **系统资源泄漏**:如果没有释放文件句柄和其他系统资源,可能会耗尽操作系统资源,导致后续无法打开新文件或其他系统操作失败。

4.参考文章

Qt之第三方库‌日志log使用(四)_qt log-CSDN博客


http://www.niftyadmin.cn/n/5866985.html

相关文章

整数二分算法

例题&#xff1a; 给定一个按照升序排列的长度为 n 的整数数组&#xff0c;以及 q个查询。 对于每个查询&#xff0c;返回一个元素 k 的起始位置和终止位置&#xff08;位置从 0开始计数&#xff09;。 如果数组中不存在该元素&#xff0c;则返回 -1 -1。 输入格式 第一行…

5G网络切片辨析(eMBB,mMTC,uRLLC)

URLLC有三大应用场景&#xff0c;分别是eMBB&#xff08;增强型移动宽带&#xff09;、uRLLC&#xff08;高可靠低延时通信&#xff09;和mMTC&#xff08;海量机器通信&#xff09;。 增强型移动宽带&#xff08;eMBB&#xff09;&#xff1a;需要关注峰值速率&#xff0c;容…

【GESP】C++二级真题 luogu-b3955, [GESP202403 二级] 小杨的日字矩阵

GESP二级真题&#xff0c;多层循环、分支语句练习&#xff0c;难度★✮☆☆☆。 题目题解详见&#xff1a;https://www.coderli.com/gesp-2-luogu-b3955/ 【GESP】C二级真题 luogu-b3955, [GESP202403 二级] 小杨的日字矩阵 | OneCoderGESP二级真题&#xff0c;多层循环、分支…

unity学习53:UI的子容器:面板panel

目录 1 UI的最底层容器&#xff1a;canvas 1.1 UI的最底层容器&#xff1a;canvas 1.2 UI的合理结构 2 UI的子容器&#xff1a;面板panel 2.1 创建panel 2.2 面板的本质&#xff1a; image &#xff0c;就是一个透明的图片&#xff0c;1个空容器 3 面板的属性 4 面板的…

大数据平台上的机器学习模型部署:从理论到实

大数据平台上的机器学习模型部署&#xff1a;从理论到实践 大家好&#xff0c;我是Echo_Wish&#xff0c;一名专注于大数据领域的自媒体创作者。今天&#xff0c;我们将深入探讨大数据平台上的机器学习模型部署。随着数据量的爆炸式增长&#xff0c;如何在大数据平台上高效地部…

【LeetCode刷题之路】leetcode155.最小栈

LeetCode刷题记录 &#x1f310; 我的博客主页&#xff1a;iiiiiankor&#x1f3af; 如果你觉得我的内容对你有帮助&#xff0c;不妨点个赞&#x1f44d;、留个评论✍&#xff0c;或者收藏⭐&#xff0c;让我们一起进步&#xff01;&#x1f4dd; 专栏系列&#xff1a;LeetCode…

ubuntu新系统使用指南

1. 更新源 2. 配置rime 输入法 sudo apt install ibus-rimeibus-setup #打开配置界面添加雾凇拼音 cd ~/Documents/Tool/input_source/plumgit clone --depth 1 https://github.com/rime/plum plum #没有梯子就劝退cd plum/bash rime-install iDvel/rime-ice:others/recipe…

Pretraining Language Models with Text-Attributed Heterogeneous Graphs

Pretraining Language Models with Text-Attributed Heterogeneous Graphs EMNLP 推荐指数&#xff1a;#paper/⭐⭐#​ 贡献&#xff1a; 我们研究了在更复杂的数据结构上预训练LM的问题&#xff0c;即&#xff0c;TAHG。与大多数只能从每个节点的文本描述中学习的PLM不同&…