2025-03-14 16:06:20 +08:00
|
|
|
#include "logger.h"
|
|
|
|
|
#include "settings.h"
|
|
|
|
|
#include <QFileInfo>
|
|
|
|
|
#include <QDir>
|
2025-03-24 18:13:06 +08:00
|
|
|
#include <QTextStream>
|
2025-03-14 16:06:20 +08:00
|
|
|
|
|
|
|
|
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 << 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"))
|
|
|
|
|
.arg(levelStrings[level])
|
|
|
|
|
.arg(context)
|
|
|
|
|
.arg(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);
|
|
|
|
|
}
|