From 001c4459f02984bc631df352e04db8686050cbc9 Mon Sep 17 00:00:00 2001 From: duanshengchao <519970194@qq.com> Date: Wed, 3 Dec 2025 17:09:26 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E6=B7=BB=E5=8A=A0log=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 3 + include/logger.h | 71 +++++++++++++ log_config.ini | 6 ++ source/alarmEventDataService.cpp | 15 ++- source/alarmEventDataView.cpp | 2 + source/logger.cpp | 164 +++++++++++++++++++++++++++++++ 6 files changed, 257 insertions(+), 4 deletions(-) create mode 100644 include/logger.h create mode 100644 log_config.ini create mode 100644 source/logger.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index e8daad2..7596019 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,6 +56,7 @@ set(H_HEADER_FILES include/alarmEventUtils.h include/alarmEventDataService.h include/alarmEventConfigDialog.h + include/logger.h ) set(CPP_SOURCE_FILES @@ -93,6 +94,7 @@ set(CPP_SOURCE_FILES source/alarmEventUtils.cpp source/alarmEventDataService.cpp source/alarmEventConfigDialog.cpp + source/logger.cpp ) set(UI_FILES @@ -219,6 +221,7 @@ install(TARGETS PowerMaster set(CONFIG_FILES alarmEventService_config.ini realTimeDataService_config.ini + log_config.ini ) set(CONFIG_FILE_DIR "${CMAKE_BINARY_DIR}/config") # 确保配置文件目录存在 diff --git a/include/logger.h b/include/logger.h new file mode 100644 index 0000000..c312340 --- /dev/null +++ b/include/logger.h @@ -0,0 +1,71 @@ +#ifndef LOGGER_H +#define LOGGER_H + +#include +#include +#include + +// 日志宏定义 +#define LOG(level, module, message) Logger::instance().log(Logger::level, module, message) +#define LOG_DEBUG(module, message) LOG(DEBUG, module, message) +#define LOG_INFO(module, message) LOG(INFO, module, message) +#define LOG_WARN(module, message) LOG(WARNING, module, message) +#define LOG_ERROR(module, message) LOG(ERROR, module, message) +#define LOG_FATAL(module, message) LOG(FATAL, module, message) + +class QSettings; +class Logger : public QObject +{ + Q_OBJECT + +public: + enum LogLevel + { + FATAL = 0, + ERROR, + WARNING, + INFO, + DEBUG + }; + + //获取单例实例 + static Logger& instance(); + void log(LogLevel, const QString&, const QString&); + +private: + explicit Logger(); + ~Logger(); + //禁止拷贝 + Logger(const Logger&) = delete; //delete关键字表示该函数不可用,包括编译器自动生成的函数 + Logger& operator=(const Logger&) = delete; + + void initialize(); + void loadConfig(/*const QString&*/); //本系统是通过Settings类进行配置文件信息读取 + void setLogFile(const QString&); + void shutdown(); + + void writeToFile(const QString&); + void rollLogFiles(); //当文件大小超过设置上线时会触发'滚动' + QString formatLogMessage(LogLevel, const QString&, const QString&); + + struct LogEntry + { + QDateTime time; + LogLevel level; + QString module; + QString message; + Qt::HANDLE threadId; + }; + + //配置参数 + QSettings* m_settings; + LogLevel m_logLevel; + QString m_logFilePath; + QFile m_logFile; + qint64 m_maxFileSize; + int m_maxBackupFiles; + bool m_outputToConsole; + bool m_outputOtFile; +}; + +#endif //LOGGER_H diff --git a/log_config.ini b/log_config.ini new file mode 100644 index 0000000..5775012 --- /dev/null +++ b/log_config.ini @@ -0,0 +1,6 @@ +[Log] +level=DEBUG +maxSize=10485760 +backups=5 +consoleOutput=false +fileOutput=true \ No newline at end of file diff --git a/source/alarmEventDataService.cpp b/source/alarmEventDataService.cpp index 9955e5c..7ebf4b1 100644 --- a/source/alarmEventDataService.cpp +++ b/source/alarmEventDataService.cpp @@ -1,4 +1,5 @@ #include "alarmEventDataService.h" +#include "logger.h" #include #include #include @@ -296,28 +297,31 @@ void AlarmEventDataService::onRabbitMQConnected() void AlarmEventDataService::onRabbitMQDisconnected() { - qWarning() << "Disconnected to RabbitMQ"; + //qWarning() << "Disconnected to RabbitMQ"; + LOG_WARN("RabbitMQ", "Disconnected to RabbitMQ"); m_serviceStatus = ServiceStatus::Disconnected; scheduleReconnect(); } void AlarmEventDataService::onRabbitMQError(QAMQP::Error error) { - qWarning() << m_amqpClient->errorString(); + //qWarning() << m_amqpClient->errorString(); + LOG_WARN("RabbitMQ", m_amqpClient->errorString()); m_serviceStatus = ServiceStatus::Error; scheduleReconnect(); } void AlarmEventDataService::onRabbitMQSocketError(QAbstractSocket::SocketError error) { - qWarning() << "RabbitMQ connection socket error: " << error; + //qWarning() << "RabbitMQ connection socket error: " << error; + LOG_WARN("RabbitMQ", QString("RabbitMQ connection socket error: %1").arg(error)); m_serviceStatus = ServiceStatus::Error; scheduleReconnect(); } void AlarmEventDataService::onAmqpQueueOpend() { - //qInfo() << "AmqpQueue opend"; + LOG_INFO("RabbitMQ", "AmqpQueue opend"); m_amqpQueue->declare(QAmqpQueue::AutoDelete); m_amqpQueue->bind(m_amqpExchange, m_config.rabbitMQConfig.routingKey); //对于广播类型的交换机,消息会发送给所有与之绑定的队列,所以此时routingKey无所谓 connect(m_amqpQueue, SIGNAL(messageReceived()), this, SLOT(onMessageReceived())); @@ -331,6 +335,7 @@ void AlarmEventDataService::onMessageReceived() { QAmqpMessage message = m_amqpQueue->dequeue(); //qDebug() << message.payload(); + LOG_INFO("RabbitMQ", QString("messageReceived: %1").arg(message.payload())); MessageHandleResult result = processMessage(message); //手动确认消息 @@ -353,11 +358,13 @@ void AlarmEventDataService::onHistoricalRequestFinished(QNetworkReply* reply) { QByteArray data = reply->readAll(); processHistoricalResponse(data); + LOG_INFO("Http", QString(data)); } else { QString errorMsg = QString("Request HistoricalData error: %1").arg(/*reply->request().url().toDisplayString(),*/ reply->errorString()); emit historicalQueryError(errorMsg); + LOG_ERROR("Http", errorMsg); } reply->deleteLater(); diff --git a/source/alarmEventDataView.cpp b/source/alarmEventDataView.cpp index 7f25d22..9811aaf 100644 --- a/source/alarmEventDataView.cpp +++ b/source/alarmEventDataView.cpp @@ -226,6 +226,7 @@ void AlarmEventDataModel::setFilter(const AlarmEventDataFilter& filter) if(m_dataMode == RealTime) return; + //qInfo() << "AlarmEventDataModel::setFilter()"; m_currentFilter = filter; refresh(); } @@ -244,6 +245,7 @@ void AlarmEventDataModel::refresh() if(m_dataMode == RealTime) return; + //qInfo() << "AlarmEventDataModel::refresh()"; AlarmEventDataService::instance()->queryHistoricalEvents(m_currentFilter.beginTime(), m_currentFilter.endTime(), m_currentFilter.confirmStatus()); } diff --git a/source/logger.cpp b/source/logger.cpp new file mode 100644 index 0000000..7b42b69 --- /dev/null +++ b/source/logger.cpp @@ -0,0 +1,164 @@ +#include "logger.h" +#include +#include +#include +#include +#include + +Logger& Logger::instance() +{ + //采用静态局部变量的方式,静态局部变量的初始化是在第一次访问时,以后的调用不会多次初始化,并且生命周期和程序一致 + static Logger instance; + return instance; +} + +Logger::Logger() +{ + initialize(); +} + +Logger::~Logger() +{ + shutdown(); +} + +void Logger::initialize() +{ + //默认配置 + m_logFilePath = ""; + m_logLevel = INFO; + m_maxFileSize = 1024 *1024 * 10; //10MB + m_maxBackupFiles = 5; + m_outputToConsole = true; + m_outputOtFile = true; + //从配置文件中加载配置 + loadConfig(); + //创建log文件 + QString filePath = QCoreApplication::applicationDirPath() + "/log/app.log"; + setLogFile(filePath); +} + +void Logger::loadConfig(/*const QString& configFilePath*/) +{ + QString settingFile = QCoreApplication::applicationDirPath() + "/config/log_config.ini"; + QFile file(settingFile); + if(file.open(QIODevice::ReadWrite)) + { + m_settings = new QSettings(settingFile, QSettings::IniFormat); + + QString strLevel = m_settings->value("Log/level").toString().toUpper(); + if(strLevel == "DEBUG") + m_logLevel = DEBUG; + else if(strLevel == "INFO") + m_logLevel = INFO; + else if(strLevel == "WARNING") + m_logLevel = WARNING; + else if(strLevel == "ERROR") + m_logLevel = ERROR; + else if(strLevel == "FATAL") + m_logLevel = FATAL; + + m_maxFileSize = m_settings->value("Log/maxSize").toLongLong(); + m_maxBackupFiles = m_settings->value("Log/backups").toInt(); + QString strOutputToConsole = m_settings->value("Log/consoleOutput").toString(); + if(strOutputToConsole == "true") + m_outputToConsole = true; + else + m_outputToConsole = false; + QString strOutputToFile = m_settings->value("Log/fileOutput").toString(); + if(strOutputToFile == "true") + m_outputOtFile = true; + else + m_outputOtFile = false; + + } +} + +void Logger::setLogFile(const QString& filePath) +{ + //检查目录文件所在目录,如果不存在则创建目录 + QFileInfo fileInfo(filePath); + QDir logDir = fileInfo.dir(); + if(!logDir.exists()) + logDir.mkpath("."); + + //更新log文件前要先关闭当前已打开的文件 + if(m_logFile.isOpen()) + m_logFile.close(); + + m_logFilePath = filePath; + m_logFile.setFileName(filePath); +} + +void Logger::shutdown() +{ + if(m_logFile.isOpen()) + m_logFile.close(); +} + +void Logger::writeToFile(const QString& message) +{ + if(m_logFilePath.isEmpty()) + return; + + if(!m_logFile.isOpen()) + { + if (!m_logFile.open(QIODevice::Append | QIODevice::Text)) + { + qWarning() << "Failed to open log file:" << m_logFile.errorString(); + return; + } + //打开文件时先键入一个换行符 + QTextStream stream(&m_logFile); + stream.setEncoding(QStringConverter::Utf8);//强制UTF-8编码 + stream << Qt::endl; + stream.flush(); //刷新输出缓冲区,确保数据立即写入文件 + } + + QTextStream stream(&m_logFile); + stream << message << Qt::endl; + stream.flush(); //刷新输出缓冲区,确保数据立即写入文件 + + if(m_logFile.size() > m_maxFileSize) + rollLogFiles(); +} + +void Logger::rollLogFiles() +{ + if(m_logFile.isOpen()) + m_logFile.close(); + + //删除最旧的备份文件(备份文件以‘日志文件.数字’的格式命名,数字越大表示文件越旧) + QFile::remove(QString("%1.%2").arg(m_logFilePath).arg(m_maxBackupFiles)); + //剩余文件依次更改名称 + for(int i = m_maxBackupFiles - 1; i > 0; i--) + QFile::rename(QString("%1.%2").arg(m_logFilePath).arg(i), QString("%1.%2").arg(m_logFilePath).arg(i + 1)); + //将当前日志文件更改为'最新'的备份文件(编号为1) + QFile::rename(m_logFilePath, QString("%1.1").arg(m_logFilePath)); + //更新当前配置文件(重新打开) + m_logFile.setFileName(m_logFilePath); + if (!m_logFile.open(QIODevice::Append | QIODevice::Text)) + qWarning() << "Failed to open new log file after rolling:" << m_logFile.errorString(); +} + +QString Logger::formatLogMessage(LogLevel level, const QString& context, const QString& message) +{ + static const char* levelStrings[] = {"FATAL", "ERROR", "WARNING", "INFO", "DEBUG"}; + return QString("[%1] [%2] [%3] %4") + .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss") + ,levelStrings[level] + ,context + ,message); +} + +void Logger::log(LogLevel level, const QString& context, const QString& message) +{ + if(level > m_logLevel) + return; + + QString formatMessage = formatLogMessage(level, context, message); + if(m_outputToConsole) + QTextStream(stderr) << formatMessage << Qt::endl; + if(m_outputOtFile) + writeToFile(formatMessage); +}