重构qDebug()<<,使log输出到文件

本文介绍如何重构qDebug()<<,将其输出的日志重定向到文件,以便于后期分析和调试。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

重构qDebug()<<,使log输出到文件

#include <QProcessEnvironment>
#include <QDateTime>
#include <QFile>
#include <QIODevice>

class HSDbg
{
public:

    HSDbg& operator<<(const QString& str)
    {
        QTextStream txtOutput(&qtLogfile);
        if(qtLogfile.open(QIODevice::Append | QIODevice::Text))
        {
            qDebug() << str;
            txtOutput << str << "\n";
            qtLogfile.close();
        }
        return *this;
    }
    HSDbg& opera
#include "DataUploader.h" #include "DatabaseManager.h" #include "Logger.h" #include <QCryptographicHash> #include <QSqlQuery> #include <QSqlError> DataUploader::DataUploader(QObject *parent) : QObject(parent) { //从以前的文件加载保存的文件状态 loadFileStates(); //将目录更改信号连接 connect(&watcher, &QFileSystemWatcher::directoryChanged, this, &DataUploader::onDirectoryChanged); //添加状态上报定时器 statusTimer = new QTimer(this); connect(statusTimer,&QTimer::timeout,this,&DataUploader::reportStatus); statusTimer->start(5000); } bool DataUploader::isRecentFile(const QString& filePath) { QFileInfo fileInfo(filePath); if(!fileInfo.exists()) { return false; } QDateTime fileTime = fileInfo.lastModified(); QDateTime twoDaysAgo = QDateTime::currentDateTime().addDays(-2); return fileTime >= twoDaysAgo; } void DataUploader::cleanupOldData() { QDateTime twoDaysAgo = QDateTime::currentDateTime().addDays(-2); QString twoDaysAgoStr = twoDaysAgo.toString("yyyy-MM-dd HH:mm:ss"); Logger::instance().log(Logger::INFO, QString("开始清理超过两天的旧数据,清理时间点: %1").arg(twoDaysAgoStr)); //DatabaseManager的实例方法 QSqlDatabase db = DatabaseManager::instance().getDatabase(); if (!db.isOpen()) { Logger::instance().log(Logger::ERROR, "数据库未连接,无法清理旧数据"); return; } QSqlQuery query(db); //清理component_errors表 query.prepare("DELETE FROM component_errors WHERE timestamp < ?"); query.addBindValue(twoDaysAgoStr); if (!query.exec()) { Logger::instance().log(Logger::ERROR, QString("清理 component_errors 表失败: %1").arg(query.lastError().text())); }else { Logger::instance().log(Logger::INFO, QString("已清理 %1 条 component_errors 记录").arg(query.numRowsAffected())); } //清理alarm_stats表 query.prepare("DELETE FROM alarm_stats WHERE timestamp < ?"); query.addBindValue(twoDaysAgoStr); if(!query.exec()) { Logger::instance().log(Logger::ERROR, QString("清理 alarm_stats 表失败: %1").arg(query.lastError().text())); }else { Logger::instance().log(Logger::INFO, QString("已清理 %1 条 alarm_stats 记录").arg(query.numRowsAffected())); } //清理完成后关闭连接 QString connectionName = db.connectionName(); db.close(); QSqlDatabase::removeDatabase(connectionName); } void DataUploader::setWatchPath(const QString& path) { //设置目录路径 if(!watchPath.isEmpty()) { watcher.removePath(watchPath); } watchPath = path; //添加路径到监视程序,处理现有文件 if(!watcher.addPath(watchPath)) { Logger::instance().log(Logger::ERROR, QString("无法监视目录: %1").arg(watchPath)); }else { Logger::instance().log(Logger::INFO, QString("成功监视目录: %1").arg(watchPath)); } processFiles(); } void DataUploader::reportStatus() { try { if(DatabaseManager::instance().isConnected()) { bool success = DatabaseManager::instance().updateProgramStatus(programName); qDebug() << "状态上报" << (success ? "成功" : "失败"); if(success) { Logger::instance().log(Logger::DEBUG, "状态上报成功"); }else { Logger::instance().log(Logger::ERROR, "状态上报失败"); } } }catch (...) { qCritical() << "状态上报发生异常"; Logger::instance().log(Logger::WARNING, "状态上报发生异常"); } } void DataUploader::onDirectoryChanged(const QString& path) { //处理目录更改 Q_UNUSED(path); Logger::instance().log(Logger::INFO, "检测到目录更改"); processFiles(); } void DataUploader::processFiles() { QDir dir(watchPath); QStringList files = dir.entryList(QDir::Files, QDir::Time); Logger::instance().log(Logger::INFO, QString("扫描目录 %1,找到 %2 个文件").arg(watchPath).arg(files.size())); // 先收集需要处理的文件 QStringList filesToProcess; { //QMutexLocker locker(&m_dataMutex); foreach(const QString &file, files) { QString filePath = dir.filePath(file); Logger::instance().log(Logger::DEBUG, QString("检查文件: %1").arg(filePath)); if(!isRecentFile(filePath)) { Logger::instance().log(Logger::DEBUG, QString("文件 %1 不是最近两天的,跳过").arg(filePath)); continue; } QString currentHash = calculateFileHash(filePath); if(!fileStates.contains(filePath) || fileStates[filePath].fileHash != currentHash) { filesToProcess.append(filePath); } } } // 处理文件(无锁状态) foreach(const QString &filePath, filesToProcess) { processFile(filePath); // 更新文件状态(加锁) QMutexLocker locker(&m_dataMutex); updateFileState(filePath, 0, calculateFileHash(filePath)); } // 保存状态(加锁) QMutexLocker locker(&m_dataMutex); saveFileStates(); } void DataUploader::processFile(const QString &filePath) { Logger::instance().log(Logger::INFO, QString("开始处理文件: %1").arg(filePath)); // 加锁保护所有共享数据结构 QMutexLocker locker(&m_dataMutex); //重置状态 boardInfoMap.clear(); pendingComponentErrors.clear(); pendingAlarmStats.clear(); componentErrors.clear(); alarmStats.clear(); QFile file(filePath); if(!file.open(QIODevice::ReadOnly | QIODevice::Text)) { Logger::instance().log(Logger::ERROR, QString("无法打开文件: %1, 错误: %2").arg(filePath).arg(file.errorString())); return; } QTextStream in(&file); while(!in.atEnd()) { QString line = in.readLine(); QStringList parsedData = parseLogLine(line); if(parsedData.isEmpty()) continue; QString dataType = parsedData[0]; QString timestamp = parsedData.last(); //时间戳总是在最后 if(dataType == "COMPONENT_ERROR") { PendingError error; error.component = parsedData[1]; error.alarmType = parsedData[2]; error.timestamp = timestamp; pendingComponentErrors.append(qMakePair(error, timestamp)); } else if(dataType == "BOARD_INFO") { QString boardName = parsedData[1]; QString inspectionId = parsedData[2]; boardInfoMap[timestamp] = boardName + "|" + inspectionId; //尝试关联已暂存的数据 processPendingData(timestamp); } else if(dataType == "ALARM_STATS") { PendingStat stat; stat.alarmType = parsedData[1]; stat.alarmName = parsedData[2]; stat.count = parsedData[3]; stat.timestamp = timestamp; pendingAlarmStats.append(qMakePair(stat, timestamp)); } } //文件处理完成后,关联所有剩余暂存数据 processPendingData(""); //插入数据到数据库 if(!componentErrors.isEmpty()) { DatabaseManager::instance().insertAOIData(componentErrors, "component_errors"); } if(!alarmStats.isEmpty()) { DatabaseManager::instance().insertAOIData(alarmStats, "alarm_stats"); } } void DataUploader::processPendingData(const QString& specificTimestamp) { //处理元件错误 QMutableListIterator<QPair<PendingError, QString>> compIt(pendingComponentErrors); while(compIt.hasNext()) { auto& item = compIt.next(); QString timestamp = item.second; const PendingError& error = item.first; if(!specificTimestamp.isEmpty() && timestamp != specificTimestamp) { continue; } QString boardInfo = findBoardInfo(timestamp); if(!boardInfo.isEmpty()) { QStringList parts = boardInfo.split("|"); QString record = QString("%1|%2|%3|%4|%5") .arg(parts[0]) //boardName .arg(parts[1]) //inspectionId .arg(error.component) .arg(error.alarmType) .arg(error.timestamp); componentErrors.append(record); compIt.remove(); //安全删除当前元素 } } //正向遍历错误统计 QMutableListIterator<QPair<PendingStat, QString>> statIt(pendingAlarmStats); while(statIt.hasNext()) { auto& item = statIt.next(); QString timestamp = item.second; const PendingStat& stat = item.first; if(!specificTimestamp.isEmpty() && timestamp != specificTimestamp) { continue; } QString boardInfo = findBoardInfo(timestamp); if(!boardInfo.isEmpty()) { QStringList parts = boardInfo.split("|"); QString record = QString("%1|%2|%3|%4|%5|%6") .arg(parts[0]) //boardName .arg(parts[1]) //inspectionId .arg(stat.alarmType) .arg(stat.alarmName) .arg(stat.count) .arg(stat.timestamp); alarmStats.append(record); statIt.remove(); //安全删除当前元素 } } } QString DataUploader::findBoardInfo(const QString& timestamp) { //QReadLocker locker(&m_dataLock); //卡在这里 //查找最接近的板信息(时间戳小于等于给定时间戳) auto it = boardInfoMap.upperBound(timestamp); if(it != boardInfoMap.begin()) { --it; return it.value(); } return QString(); } QStringList DataUploader::parseLogLine(const QString &line) { // 记录原始日志行 Logger::instance().log(Logger::DEBUG, QString("解析日志行: %1").arg(line)); QStringList result; //提取时间戳 QRegularExpression timeRegex("^(\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2})"); QRegularExpressionMatch timeMatch = timeRegex.match(line); QString timestamp = timeMatch.hasMatch() ? timeMatch.captured(1) : ""; //解析组件缺陷 if(line.contains("CreateRepairInspection Spot component Name =")) { //QRegularExpression regex("Name = \\s*(\\S+).*AlarmType =\\s*([^,]+)"); QRegularExpression regex("Name\\s*=\\s*(\\S+).*AlarmType\\s*=\\s*([^,]+)"); QRegularExpressionMatch match = regex.match(line); if(match.hasMatch()) { result << "COMPONENT_ERROR" << match.captured(1).trimmed() //component << match.captured(2).trimmed() //alarmType << timestamp; } } //解析板信息行 if(line.contains("ExportBitmapManager.Export")) { //QRegularExpression regex("id = ([^,]+), boardName = ([^,]+)"); QRegularExpression regex("id\\s*=\\s*([^,]+),\\s*boardName\\s*=\\s*([^,]+)"); QRegularExpressionMatch match = regex.match(line); if(match.hasMatch()) { result << "BOARD_INFO" << match.captured(2).trimmed() //boardName << match.captured(1).trimmed() //inspectionId << timestamp; } } //解析错误统计信息 if(line.contains("alarmType =") && line.contains("alarmName =") && line.contains("count =") && !line.contains("update")) { QRegularExpression regex("alarmType\\s*=\\s*([^,]+),\\s*alarmName\\s*=\\s*([^,]+),\\s*count\\s*=\\s*(\\d+)"); QRegularExpressionMatch match = regex.match(line); if(match.hasMatch()) { QString countStr = match.captured(3).trimmed(); int count = countStr.toInt(); if(count > 0) { result << "ALARM_STATS" << match.captured(1).trimmed() //alarmType << match.captured(2).trimmed() //alarmName << match.captured(3).trimmed() //count << timestamp; } } } if(!result.isEmpty()) { Logger::instance().log(Logger::DEBUG, QString("解析结果: %1").arg(result.join("|"))); } return result; } QString DataUploader::extractValue(const QString &line, const QString &key) { //提取键值对正则表达式 QRegularExpression regex(QString("%1\\s*=\\s*([^\\s,]+)").arg(key)); QRegularExpressionMatch match = regex.match(line); return match.hasMatch() ? match.captured(1).trimmed() : ""; } QString DataUploader::calculateFileHash(const QString &filePath) { //计算文件的MD5哈希值 QFile file(filePath); if(!file.open(QIODevice::ReadOnly)) { Logger::instance().log(Logger::ERROR, QString("无法打开文件计算哈希: %1").arg(filePath)); return QString(); } QCryptographicHash hash(QCryptographicHash::Md5); if(!hash.addData(&file)) { Logger::instance().log(Logger::ERROR, QString("计算文件哈希失败: %1").arg(filePath)); return QString(); } qDebug() << "哈希计算成功"; return hash.result().toHex(); } void DataUploader::updateFileState(const QString &filePath, qint64 pos, const QString &hash) { //更新跟踪文件的状态 FileState state; state.lastPos = pos; state.fileHash = hash; fileStates[filePath] = state; } void DataUploader::saveFileStates() { //文件状态保存到持久存储 state.beginGroup("FileStates"); for(auto it = fileStates.begin(); it != fileStates.end(); ++it) { state.setValue(it.key() + "/pos", it->lastPos); state.setValue(it.key() + "/hash", it->fileHash); } state.endGroup(); } void DataUploader::loadFileStates() { //加载文件状态 state.beginGroup("FileStates"); foreach(QString key, state.childKeys()) { if(key.endsWith("/pos")) { QString filePath = key.left(key.length() - 4); FileState fs; fs.lastPos = state.value(key).toLongLong(); fs.fileHash = state.value(filePath + "/hash").toString(); fileStates[filePath] = fs; } } state.endGroup(); } 为什么这个代码无法上传数据,只能上报状态呢,难道是哪里写的不对导致阻塞了吗
07-19
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值