#include "logger.h" #include "settings.h" #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(); } void Logger::loadConfig(/*const QString& configFilePath*/) { QString filePath = Settings::instance().value("Log", "logFile").toString(); setLogFile(filePath); QString strLevel = Settings::instance().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 = Settings::instance().value("Log", "maxSize").toLongLong(); m_maxBackupFiles = Settings::instance().value("Log", "backups").toInt(); QString strOutputToConsole = Settings::instance().value("Log", "consoleOutput").toString(); if(strOutputToConsole == "true") m_outputToConsole = true; else m_outputToConsole = false; QString strOutputToFile = Settings::instance().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); }