#include "qlogmanager.h" #include #include QBasicAtomicPointer QLogManager::s_instance = Q_BASIC_ATOMIC_INITIALIZER(0); QMutex QLogManager::s_mutex; QLogManager::QLogManager(QObject* parent) : QObject(parent), m_minLevel(SQ_INFO), m_workerThread(nullptr), m_running(true) { m_logDirectory = QDir(QCoreApplication::applicationDirPath() + "/logs/"); if (!m_logDirectory.exists()) { if (!m_logDirectory.mkpath(".")) { emit newLog(SQ_ERROR, QString("Failed to create log directory: %1").arg(m_logDirectory.path())); } } cleanOldLogs(); m_workerThread = new QThread(); QObject::connect(m_workerThread, &QThread::started, [this]() { this->processLogs(); }); m_workerThread->start(); } QLogManager::~QLogManager() { m_running = false; m_queueSemaphore.release(); if (m_workerThread) { m_workerThread->quit(); if (!m_workerThread->wait(3000)) { m_workerThread->terminate(); m_workerThread->wait(); } delete m_workerThread; } LogEntry entry; while (m_logQueue.dequeue(entry)) { writeToFile(entry.level, entry.message); } for (auto& file : m_openFiles) { if (!file.isNull()) { file->close(); } } } QLogManager* QLogManager::Instance() { if (!s_instance.load()) { QMutexLocker locker(&s_mutex); if (!s_instance.load()) { QLogManager* instance = new QLogManager(); s_instance.store(instance); qAddPostRoutine(QLogManager::destroyInstance); } } return s_instance.load(); } void QLogManager::destroyInstance() { QMutexLocker locker(&s_mutex); QLogManager* instance = s_instance.load(); if (instance) { delete instance; s_instance.store(nullptr); } } void QLogManager::setLogLevel(LogLevel level) { QMutexLocker locker(&s_mutex); m_minLevel = level; } void QLogManager::log(LogLevel level, const QString& message, const QString& module) { if (level < m_minLevel) { return; } static thread_local QString lastTimeStr; static thread_local qint64 lastTimeMs = 0; qint64 now = QDateTime::currentMSecsSinceEpoch(); if (now - lastTimeMs > TIMESTAMP_CACHE_DURATION_MS) { lastTimeStr = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz"); lastTimeMs = now; } QString levelStr; switch (level) { case SQ_DEBUG: levelStr = "DEBUG"; break; case SQ_INFO: levelStr = "INFO"; break; case SQ_WARNING: levelStr = "WARNING"; break; case SQ_ERROR: levelStr = "ERROR"; break; case SQ_FATAL: levelStr = "FATAL"; break; } QString logLine = QString("[%1][%2][%3] %4") .arg(lastTimeStr, levelStr, module, message); emit newLog(level, logLine); LogEntry entry; entry.level = level; entry.message = logLine; entry.timestamp = now; m_logQueue.enqueue(entry); m_queueSemaphore.release(); } void QLogManager::processLogs() { while (m_running) { m_queueSemaphore.acquire(); LogEntry entry; while (m_logQueue.dequeue(entry)) { writeToFile(entry.level, entry.message); if (!m_running) { break; } } } } QString QLogManager::getLogFileName(LogLevel level) { QString levelStr; switch (level) { case SQ_DEBUG: levelStr = "debug"; break; case SQ_INFO: levelStr = "info"; break; case SQ_WARNING: levelStr = "warning"; break; case SQ_ERROR: levelStr = "error"; break; case SQ_FATAL: levelStr = "fatal"; break; default: levelStr = "system"; break; } return QString("%1_%2.log") .arg(levelStr) .arg(QDateTime::currentDateTime().toString("yyyyMMdd")); } void QLogManager::writeToFile(LogLevel level, const QString& logLine) { try { if (!m_openFiles.contains(level)) { QString fileName = m_logDirectory.filePath(getLogFileName(level)); QSharedPointer file(new QFile(fileName)); if (!file->open(QIODevice::WriteOnly | QIODevice::Append)) { emit newLog(SQ_ERROR, QString("[LogSystem] Failed to open log file: %1").arg(fileName)); return; } m_openFiles[level] = file; } if (!m_openFiles[level].isNull()) { QTextStream stream(m_openFiles[level].data()); stream << logLine << "\n"; stream.flush(); if (m_openFiles[level]->size() > MAX_FILE_SIZE) { rotateLogFile(level); } } } catch (...) { emit newLog(SQ_ERROR, "[LogSystem] Exception occurred while writing log"); } } void QLogManager::rotateLogFile(LogLevel level) { if (!m_openFiles.contains(level)) { return; } try { m_openFiles[level]->close(); QString oldName = m_logDirectory.filePath(getLogFileName(level)); QString newName = oldName + "." + QDateTime::currentDateTime().toString("hhmmsszzz"); if (!QFile::rename(oldName, newName)) { emit newLog(SQ_ERROR, QString("[LogSystem] Failed to rotate log file from %1 to %2") .arg(oldName, newName)); } m_openFiles.remove(level); } catch (...) { emit newLog(SQ_ERROR, "[LogSystem] Exception occurred while rotating log file"); } } void QLogManager::cleanOldLogs() { try { QStringList logFiles = m_logDirectory.entryList(QStringList() << "*.log" << "*.log.*", QDir::Files); QDateTime now = QDateTime::currentDateTime(); for (const QString& fileName : logFiles) { QFileInfo fileInfo(m_logDirectory.filePath(fileName)); if (fileInfo.lastModified().daysTo(now) > MAX_LOG_DAYS) { if (!QFile::remove(fileInfo.absoluteFilePath())) { emit newLog(SQ_WARNING, QString("Failed to remove old log file: %1") .arg(fileInfo.absoluteFilePath())); } } } } catch (...) { emit newLog(SQ_ERROR, "[LogSystem] Exception occurred while cleaning old logs"); } } void QLogManager::flush() { QMutexLocker locker(&s_mutex); for (auto& file : m_openFiles) { if (!file.isNull()) { file->flush(); } } }