目录
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博客