Терминальный проект КиберПлат [open source]
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

296 lines
7.5KB

  1. /* @file Реализация задачи архивации журнальных файлов. */
  2. // Qt
  3. #include <Common/QtHeadersBegin.h>
  4. #include <QtCore/QFileInfo>
  5. #include <QtCore/QSet>
  6. #include <Common/QtHeadersEnd.h>
  7. // SDK
  8. #include <SDK/PaymentProcessor/Settings/TerminalSettings.h>
  9. #include <SDK/PaymentProcessor/Core/ISettingsService.h>
  10. // Модули
  11. #include <System/IApplication.h>
  12. #include <Common/Application.h>
  13. #include <Packer/Packer.h>
  14. // Проект
  15. #include "LogArchiver.h"
  16. namespace PPSDK = SDK::PaymentProcessor;
  17. namespace CLogArchiver
  18. {
  19. const QString DateFormat = "yyyy.MM.dd";
  20. const int BytesInMB = 1048576; // 2^20
  21. }
  22. //---------------------------------------------------------------------------
  23. bool isArchive(const QFileInfo & aFileInfo)
  24. {
  25. return aFileInfo.suffix().toLower() == "zip" ||
  26. aFileInfo.suffix().toLower() == "7z";
  27. }
  28. //---------------------------------------------------------------------------
  29. LogArchiver::LogArchiver(const QString & aName, const QString & aLogName, const QString & aParams)
  30. : ITask(aName, aLogName, aParams),
  31. ILogable(aLogName),
  32. mCanceled(false),
  33. mPacker("", nullptr)
  34. {
  35. IApplication * app = dynamic_cast<IApplication *>(BasicApplication::getInstance());
  36. PPSDK::ICore * core = app->getCore();
  37. PPSDK::TerminalSettings * terminalSettings = static_cast<PPSDK::TerminalSettings *>(core->getSettingsService()->
  38. getAdapter(PPSDK::CAdapterNames::TerminalAdapter));
  39. mMaxSize = terminalSettings->getLogsMaxSize();
  40. mLogDir = QDir(app->getWorkingDirectory() + "/logs");
  41. mKernelPath = app->getWorkingDirectory();
  42. mPacker.setLog(getLog());
  43. mPacker.setToolPath(mKernelPath);
  44. }
  45. //---------------------------------------------------------------------------
  46. LogArchiver::~LogArchiver()
  47. {
  48. }
  49. //---------------------------------------------------------------------------
  50. inline uint qHash(QDate key)
  51. {
  52. return key.toJulianDay();
  53. }
  54. //---------------------------------------------------------------------------
  55. void LogArchiver::execute()
  56. {
  57. if (mMaxSize < 1 || !mLogDir.exists())
  58. {
  59. toLog(LogLevel::Error, "Failed execute: (max_size < 1) OR (log_dir not exist)");
  60. emit finished(mName, false);
  61. return;
  62. }
  63. ILog::logRotateAll();
  64. foreach(auto date, getDatesForPack())
  65. {
  66. if (!mCanceled)
  67. {
  68. packLogs(date);
  69. }
  70. }
  71. if (!mCanceled)
  72. {
  73. checkArchiveSize();
  74. }
  75. emit finished(mName, true);
  76. }
  77. //---------------------------------------------------------------------------
  78. bool LogArchiver::cancel()
  79. {
  80. mCanceled = true;
  81. mPacker.terminate();
  82. return true;
  83. }
  84. //---------------------------------------------------------------------------
  85. bool LogArchiver::subscribeOnComplete(QObject * aReceiver, const char * aSlot)
  86. {
  87. return connect(this, SIGNAL(finished(const QString &, bool)), aReceiver, aSlot);
  88. }
  89. //---------------------------------------------------------------------------
  90. QString LogArchiver::logArchiveFileName(QDate aDate)
  91. {
  92. return mLogDir.absoluteFilePath(QString("%1_logs.7z").arg(aDate.toString(CLogArchiver::DateFormat)));
  93. }
  94. //---------------------------------------------------------------------------
  95. void LogArchiver::packLogs(QDate aDate)
  96. {
  97. toLog(LogLevel::Normal, QString("Packs logs '%1'").arg(aDate.toString(CLogArchiver::DateFormat)));
  98. bool updateArchive = QFile::exists(logArchiveFileName(aDate));
  99. // pack files to archive
  100. mPacker.setUpdateMode(updateArchive);
  101. mPacker.setFormat(Packer::SevenZip);
  102. mPacker.setLevel(7);
  103. mPacker.setTimeout(60 * 60 * 1000); // 1 час в милисекундах
  104. mPacker.setRecursive(true);
  105. QStringList toCompress;
  106. toCompress
  107. << QString("logs/%1*").arg(aDate.toString("yyyy.MM.dd"))
  108. << QString("receipts/%1*").arg(aDate.toString("yyyy.MM.dd"));
  109. QStringList archiveWildcards;
  110. archiveWildcards << "*.zip" << "*.7z";
  111. if (!mPacker.pack(logArchiveFileName(aDate), mKernelPath, toCompress, archiveWildcards).isEmpty())
  112. {
  113. if (!mPacker.exitCode())
  114. {
  115. toLog(LogLevel::Normal, QString("Result code: %1; Output: %2").arg(mPacker.exitCode()).arg(mPacker.messages()));
  116. }
  117. toLog(LogLevel::Normal, "Pack OK");
  118. if (!mCanceled)
  119. {
  120. removeLogs(aDate);
  121. }
  122. }
  123. else
  124. {
  125. toLog(LogLevel::Error, QString("Pack failed: exitCode=%1 message='%2'").arg(mPacker.exitCode()).arg(mPacker.messages()));
  126. // если мы не обновляем архив - удаляем неудачный архив
  127. if (!updateArchive)
  128. {
  129. removeFile(QFileInfo(logArchiveFileName(aDate)));
  130. }
  131. }
  132. }
  133. //---------------------------------------------------------------------------
  134. QList<QDate> LogArchiver::getDatesForPack() const
  135. {
  136. QSet<QDate> result;
  137. foreach (auto file, mLogDir.entryInfoList(QDir::Files | QDir::Dirs, QDir::Name))
  138. {
  139. if (mCanceled)
  140. {
  141. break;
  142. }
  143. QDate date = QDate::fromString(file.fileName().left(10), CLogArchiver::DateFormat);
  144. if (date.isValid() && date != QDate::currentDate())
  145. {
  146. if ((file.isFile() && !isArchive(file)) || file.isDir())
  147. {
  148. result << date;
  149. }
  150. }
  151. }
  152. foreach (auto dir, QDir(mKernelPath + "/receipts").entryInfoList(QDir::Dirs, QDir::Name))
  153. {
  154. if (mCanceled)
  155. {
  156. break;
  157. }
  158. QDate date = QDate::fromString(dir.fileName().left(10), CLogArchiver::DateFormat);
  159. if (date.isValid() && date != QDate::currentDate())
  160. {
  161. result << date;
  162. }
  163. }
  164. auto list = result.toList();
  165. qSort(list);
  166. return list;
  167. }
  168. //---------------------------------------------------------------------------
  169. void LogArchiver::removeLogs(QDate aDate)
  170. {
  171. auto clearLogDir = [this](const QDir & aDir, const QDate & aDate)
  172. {
  173. foreach(auto file, aDir.entryInfoList(QStringList(aDate.toString("yyyy.MM.dd*")), QDir::Files))
  174. {
  175. if (!isArchive(file))
  176. {
  177. removeFile(file);
  178. }
  179. }
  180. };
  181. clearLogDir(mLogDir, aDate);
  182. // чистим подпапки
  183. foreach(auto dir, mLogDir.entryInfoList(QDir::Dirs | QDir::NoDot | QDir::NoDotDot))
  184. {
  185. clearLogDir(dir.filePath(), aDate);
  186. }
  187. auto clearSubdir = [&](QFileInfo & aFileInfo)
  188. {
  189. QDir dir(aFileInfo.absoluteFilePath());
  190. foreach (auto file, dir.entryInfoList(QDir::Files))
  191. {
  192. removeFile(file);
  193. }
  194. };
  195. QList<QDir> dirsToRemove;
  196. dirsToRemove << mLogDir << QDir(mKernelPath + "/receipts");
  197. foreach (auto dir, dirsToRemove)
  198. {
  199. foreach (auto file, dir.entryInfoList(QStringList(aDate.toString("yyyy.MM.dd*")), QDir::Dirs))
  200. {
  201. clearSubdir(file);
  202. toLog(LogLevel::Debug, QString("Remove dir %1").arg(file.absoluteFilePath()));
  203. mLogDir.rmdir(file.absoluteFilePath());
  204. }
  205. }
  206. }
  207. //---------------------------------------------------------------------------
  208. void LogArchiver::checkArchiveSize()
  209. {
  210. toLog(LogLevel::Normal, QString("Check logs size limit. Max size is %1 Mb").arg(mMaxSize));
  211. QStringList archiveWildcards;
  212. archiveWildcards << "*.zip" << "*.7z";
  213. QList<QFileInfo> files = mLogDir.entryInfoList(archiveWildcards, QDir::Files, QDir::Name);
  214. auto fileSizeSumm = [](const QList<QFileInfo> & files) -> qint64
  215. {
  216. qint64 summ = 0;
  217. foreach (auto file, files)
  218. {
  219. summ += file.size();
  220. }
  221. return summ;
  222. };
  223. while (!files.isEmpty() && fileSizeSumm(files) > mMaxSize * CLogArchiver::BytesInMB && !mCanceled)
  224. {
  225. removeFile(files.takeFirst());
  226. }
  227. toLog(LogLevel::Normal, QString("Logs archive size is %1 Mb").arg(fileSizeSumm(files) / double(CLogArchiver::BytesInMB), 0, 'f', 2));
  228. }
  229. //---------------------------------------------------------------------------
  230. bool LogArchiver::removeFile(const QFileInfo & aFile)
  231. {
  232. bool result = QFile::remove(aFile.absoluteFilePath());
  233. toLog(result ? LogLevel::Normal : LogLevel::Error, QString("Remove [%1] %2").arg(aFile.absoluteFilePath()).arg(result ? "OK" : "Error"));
  234. return result;
  235. }
  236. //---------------------------------------------------------------------------