#include "Log.h" #include #include #include #include #include #include #include #include #include #include #include #ifdef WIN32 # include #else # include #endif #ifndef QT_ENDL # if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) # define QT_ENDL Qt::endl # else # define QT_ENDL endl # endif #endif static QString g_app = {}; static QString g_file_path = {}; static bool g_logError = false; static std::unique_ptr g_logFile = nullptr; static std::unique_ptr g_logStream = nullptr; static int g_logLevel = 4; std::map logLevelMap = { {QtFatalMsg, 0}, {QtCriticalMsg, 1}, {QtWarningMsg, 2}, {QtInfoMsg, 3}, {QtDebugMsg, 4} }; QString Log::prettyProductInfoWrapper() { auto productName = QSysInfo::prettyProductName(); #if QT_VERSION < QT_VERSION_CHECK(6, 5, 0) # if defined(Q_OS_MACOS) auto macosVersionFile = QString::fromUtf8("/System/Library/CoreServices/.SystemVersionPlatform.plist"); auto fi = QFileInfo(macosVersionFile); if (fi.exists() && fi.isReadable()) { auto plistFile = QFile(macosVersionFile); plistFile.open(QIODevice::ReadOnly); while (!plistFile.atEnd()) { auto line = plistFile.readLine(); if (line.contains("ProductUserVisibleVersion")) { auto nextLine = plistFile.readLine(); if (nextLine.contains("")) { QRegularExpression re(QString::fromUtf8("\\s*(.*)")); auto matches = re.match(QString::fromUtf8(nextLine)); if (matches.hasMatch()) { productName = QString::fromUtf8("macOS ") + matches.captured(1); break; } } } } } # endif #endif #if defined(Q_OS_WIN) QSettings regKey{ QString::fromUtf8(R"(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion)"), QSettings::NativeFormat}; if (regKey.contains(QString::fromUtf8("CurrentBuildNumber"))) { auto buildNumber = regKey.value(QString::fromUtf8("CurrentBuildNumber")).toInt(); if (buildNumber > 0) { if (buildNumber < 9200) { productName = QString::fromUtf8("Windows 7 build %1").arg(buildNumber); } else if (buildNumber < 10240) { productName = QString::fromUtf8("Windows 8 build %1").arg(buildNumber); } else if (buildNumber < 22000) { productName = QString::fromUtf8("Windows 10 build %1").arg(buildNumber); } else { productName = QString::fromUtf8("Windows 11 build %1").arg(buildNumber); } } } #endif return productName; } static inline void messageHandler(const QtMsgType type, const QMessageLogContext &context, const QString &message) { if (message == "Could not get the INetworkConnection instance for the adapter GUID.") { return; } if (logLevelMap[type] > g_logLevel) { return; } if (!message.isEmpty()) { QString levelName; switch (type) { case QtDebugMsg: levelName = QStringLiteral("Debug"); break; case QtInfoMsg: levelName = QStringLiteral("Info"); break; case QtWarningMsg: levelName = QStringLiteral("Warning"); break; case QtCriticalMsg: levelName = QStringLiteral("Critical"); break; case QtFatalMsg: levelName = QStringLiteral("Fatal"); break; } QString fileAndLineLogStr; if (context.file) { std::string strFileTmp = context.file; const char *ptr = strrchr(strFileTmp.c_str(), '/'); if (nullptr != ptr) { char fn[512] = {0}; sprintf(fn, "%s", ptr + 1); strFileTmp = fn; } const char *ptrTmp = strrchr(strFileTmp.c_str(), '\\'); if (nullptr != ptrTmp) { char fn[512] = {0}; sprintf(fn, "%s", ptrTmp + 1); strFileTmp = fn; } fileAndLineLogStr = QString::fromStdString("[%1:%2]").arg( QString::fromStdString(strFileTmp), QString::number(context.line)); } const QString finalMessage = QString::fromStdString("%1[%2]%3[%4]:%5") .arg(QDateTime::currentDateTime().toString("yyyy/MM/dd hh:mm:ss.zzz"), levelName, fileAndLineLogStr, QString::number(reinterpret_cast(QThread::currentThreadId())), message); if ((type == QtInfoMsg) || (type == QtDebugMsg)) { std::cout << qPrintable(finalMessage) << std::endl; } else { std::cerr << qPrintable(finalMessage) << std::endl; } if (g_logError) { return; } if (!g_logFile) { g_logFile = std::make_unique(g_file_path); if (!g_logFile->open(QFile::WriteOnly | QFile::Text | QFile::Append)) { std::cerr << "Can't open file to write: " << qPrintable(g_logFile->errorString()) << std::endl; g_logFile.reset(); g_logError = true; return; } } if (!g_logStream) { g_logStream = std::make_unique(); g_logStream->setDevice(g_logFile.get()); } (*g_logStream) << finalMessage << QT_ENDL; g_logStream->flush(); } } void Log::setup(char *argv[], const QString &app, int level) { Q_ASSERT(!app.isEmpty()); if (app.isEmpty()) { return; } g_logLevel = level; static bool once = false; if (once) { return; } QString applicationPath = QString::fromStdString(argv[0]); once = true; g_app = app; const QString logFileName = QString("%1_%2.log").arg(g_app, QDateTime::currentDateTime().toString("yyyyMMdd")); const QString logDirPath = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/log"; const QDir logDir(logDirPath); if (!logDir.exists()) { logDir.mkpath(logDirPath); } g_file_path = logDir.filePath(logFileName); qInstallMessageHandler(messageHandler); qInfo() << "==================================================="; qInfo() << "[AppName]" << g_app; qInfo() << "[AppVersion]" << PROJECT_VERSION; qInfo() << "[AppPath]" << applicationPath; qInfo() << "[QtVersion]" << QT_VERSION_STR; #ifdef WIN32 qInfo() << "[ProcessId]" << QString::number(_getpid()); #else qInfo() << "[ProcessId]" << QString::number(getpid()); #endif qInfo() << "[DeviceInfo]"; qInfo() << " [DeviceId]" << QSysInfo::machineUniqueId(); qInfo() << " [Manufacturer]" << prettyProductInfoWrapper(); qInfo() << " [CPU_ABI]" << QSysInfo::currentCpuArchitecture(); qInfo() << "[LOG_LEVEL]" << g_logLevel; qInfo() << "[LOG_PATH]" << g_file_path; qInfo() << "==================================================="; }