log.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634
  1. /*--------------------------------------------------------------------------
  2. * 基于此我们就可以写出我们自定义的日志系统了,我写的这个日志系统具有以下特点:
  3. 1、支持设置日志输出路径
  4. 2、支持日志打印格式设置
  5. 3、支持不同日志级别的不同样式设置
  6. 4、支持3种日志备份策略,采用延迟策略,不是用定时器实现周期备份,而是在具体打印内容时才触发,这么做的目的是减少定时的开销
  7. 5、支持分日志类型备份日志
  8. 6、支持备份策略参数的json文件地址存储自定义
  9. 7、支持回调函数注册,便于一些想将日志输出到自己项目界面展示
  10. --------------------------------------------------------------------------------*/
  11. #include "log.h"
  12. #include <iostream>
  13. #include <QDebug>
  14. #include <QtMessageHandler>
  15. #include <QFile>
  16. #include <QDir>
  17. #include <QDateTime>
  18. #include <qjsondocument.h>
  19. #include <qjsonobject.h>
  20. using namespace std;
  21. const int ConsoleFont::DEFAULT = 0; // 重置颜色设置
  22. const int ConsoleFont::BOLD = 1; // 加粗
  23. const int ConsoleFont::UN_BOLD = 2; // 去粗
  24. const int ConsoleFont::UNDER_LINE = 4; // 下滑线
  25. const int ConsoleFont::GLINT = 5; // 闪烁
  26. const int ConsoleFont::INVERSE = 7;// 反色
  27. const int ConsoleFont::BOLD_2 = 21; // 加粗
  28. const int ConsoleFont::NORMAL = 22; // 正常
  29. const int ConsoleFont::UN_UNDER_LINE = 24; // 去掉下滑线
  30. const int ConsoleFont::STOP_GLINT = 25; // 停止闪烁
  31. const int ConsoleFont::INVERSE_2 = 27;// 反色
  32. const int ConsoleFont::FORE_GROUND_BLACK = 30;// 前景,黑色
  33. const int ConsoleFont::FORE_GROUND_RED = 31;// 前景,红色
  34. const int ConsoleFont::FORE_GROUND_GREEN = 32;// 前景,绿色
  35. const int ConsoleFont::FORE_GROUND_ORANGE = 33;// 前景,黄色
  36. const int ConsoleFont::FORE_GROUND_BLUE = 34; //前景,篮色
  37. const int ConsoleFont::FORE_GROUND_PURPLE = 35; //前景,紫色
  38. const int ConsoleFont::FORE_GROUND_CYAN = 36; //前景,青色
  39. const int ConsoleFont::FORE_GROUND_WHITE = 37; //前景,白色
  40. const int ConsoleFont::BACK_GROUND_BLACK = 40;// 背景,黑色
  41. const int ConsoleFont::BACK_GROUND_RED = 41;// 背景,红色
  42. const int ConsoleFont::BACK_GROUND_GREEN = 42;// 背景,绿色
  43. const int ConsoleFont::BACK_GROUND_ORANGE = 43;// 背景,黄色
  44. const int ConsoleFont::BACK_GROUND_BLUE = 44; //背景,篮色
  45. const int ConsoleFont::BACK_GROUND_PURPLE = 45; //背景,紫色
  46. const int ConsoleFont::BACK_GROUND_CYAN = 46; //背景,青色
  47. const int ConsoleFont::BACK_GROUND_WHITE = 47; //背景,白色
  48. // 设置控制台字体样式
  49. const QString ConsoleFont::setConsoleFont(std::initializer_list<int> codes, QString msg)
  50. {
  51. if(codes.size() == 0) {
  52. return msg;
  53. }
  54. QString codeStr = "";
  55. int i = 0;
  56. for (auto code: codes)
  57. {
  58. if(i == 0) {
  59. codeStr = QString::number(code);
  60. } else {
  61. codeStr = codeStr + ";" + QString::number(code);
  62. }
  63. i++;
  64. }
  65. return "\033[" + codeStr + "m" + msg + "\033[0m";
  66. }
  67. const QString Log::INFO_FORMAT_TIME = "%time"; // 格式化输出日期表示
  68. const QString Log::INFO_FORMAT_LEVEL = "%level"; // 格式化输出日志级别表示
  69. const QString Log::INFO_FORMAT_CODE_FILE = "%file"; // 格式化输出代码文件地址表示
  70. const QString Log::INFO_FORMAT_CODE_LINE = "%line"; // 格式化输出代码所在行表示
  71. const QString Log::INFO_FORMAT_CODE_FUNCTION = "%function"; // 格式化输出代码所在方法函数表示
  72. const QString Log::INFO_FORMAT_CODE_MSG = "%msg"; // 格式化输出代码打印内容表示
  73. QMap<QtMsgType,QPair<QString, LogOutType>> Log::msgTypeMap = Log::initMsgTypeMap(); // 日志类型与日志标识、输出类型映射
  74. QMap<QtMsgType,std::initializer_list<int>> Log::levelFontMap = Log::initLevelFonts(); // 日志类型与字体样式设置映射
  75. QMap<LogSwitchoverType, QString> Log::logSwitchoverTypeMap = Log::initLogSwitchoverTypeMap(); // 初始化备份类型与字符的映射
  76. QFile* Log::outLogFile = new QFile("logs/system.log");// 日志输出文件
  77. QMap<QtMsgType, QFile*> Log::levelPaths = QMap<QtMsgType, QFile*>();// 不同级别的日志输出路径
  78. bool Log::isDistinguishLevel = false; // 是否区分日志级别输出
  79. QString Log::infoFormat = QString("%1 [%2] --- [%3:%4] %5: %6")
  80. .arg(Log::INFO_FORMAT_TIME, Log::INFO_FORMAT_LEVEL, Log::INFO_FORMAT_CODE_FILE,
  81. Log::INFO_FORMAT_CODE_LINE, Log::INFO_FORMAT_CODE_FUNCTION, Log::INFO_FORMAT_CODE_MSG); // 日志信息格式化
  82. QString Log::timeFormat = "yyyy-MM-dd hh:mm:ss:zzz"; // 时间格式化
  83. QFile* Log::logParamJsonFile = new QFile("logdata/logparam.json"); // 日志参数保存json文件
  84. LogSwitchoverType Log::logSwitchoverType = LogSwitchoverType::ALL_DAY; // 指定备份类型
  85. long long Log::switchoverNum = 1; // 指定备份时间或大小
  86. QDateTime Log::switchoverTime = QDateTime(); // 日志备份切换时间
  87. bool Log::isLoadLogSwitchoverSetting = false; // 是否已经加载日志备份设置
  88. CallBackInfoSafeList Log::pCallbacks = CallBackInfoSafeList(); // 回调函数集合
  89. // 设置日志输出内容格式化
  90. void Log::setFormat(QString infoFormat, QString timeFormat)
  91. {
  92. if(infoFormat.trimmed().isEmpty()) {
  93. Log::printSystemLog("error", "设置日志格式化infoFormat为空,不进行设置");
  94. return;
  95. }
  96. if(!isContainInfoFormat(infoFormat)) {
  97. Log::printSystemLog("error", "设置日志格式化infoFormat不包含关键词:"+Log::INFO_FORMAT_CODE_MSG+",不进行设置");
  98. return;
  99. }
  100. if(timeFormat.trimmed().isEmpty()) {
  101. Log::printSystemLog("error", "设置日志格式化timeFormat为空,不进行设置");
  102. return;
  103. }
  104. Log::infoFormat = infoFormat;
  105. Log::timeFormat = timeFormat;
  106. Log::printSystemLog("info", "设置日志输出内容格式化:"+infoFormat+",日期格式:"+ timeFormat);
  107. }
  108. // 设置日志类型对应的字体样式
  109. void Log::setLevelFont(QtMsgType type, std::initializer_list<int> levelFont)
  110. {
  111. Log::levelFontMap.insert(type, levelFont);
  112. Log::printSystemLog("info", "设置日志级别"+msgTypeMap.value(type, QPair<QString, LogOutType>()).first+"样式");
  113. }
  114. // 设置输出路径
  115. void Log::setOutLogPath(QString outLogPath)
  116. {
  117. if(outLogPath.trimmed().isEmpty()) {
  118. Log::printSystemLog("error", "设置日志输出路径为空,不进行设置");
  119. return;
  120. }
  121. Log::outLogFile = new QFile(outLogPath);
  122. Log::printSystemLog("info", "设置日志输出路径:"+ outLogPath);
  123. // 如果要区分日志级别输出,则重新调用设置是否分日志级别输出
  124. if(Log::isDistinguishLevel) {
  125. Log::setDistinguishLevel(Log::isDistinguishLevel);
  126. }
  127. }
  128. // 设置日志备份参数json存储地址
  129. void Log::setOutLogParamJsonPath(QString logParamJsonPath)
  130. {
  131. if(logParamJsonPath.trimmed().isEmpty()) {
  132. Log::printSystemLog("error","设置日志备份参数json存储地址为空,不进行设置");
  133. return;
  134. }
  135. Log::logParamJsonFile = new QFile(logParamJsonPath);
  136. Log::printSystemLog("info","设置日志备份参数json存储地址:"+ logParamJsonPath);
  137. }
  138. // 设置日志切换类型和时间或大小
  139. void Log::setLogSwitchover(LogSwitchoverType logSwitchoverType, long long switchoverNum)
  140. {
  141. if(switchoverNum < 1) {
  142. Log::printSystemLog("error", "设置时间或大小切换限定:"+QString::number(switchoverNum)+",小于1,不进行设置");
  143. return;
  144. }
  145. Log::logSwitchoverType = logSwitchoverType;
  146. Log::switchoverNum = switchoverNum;
  147. Log::printSystemLog("info","设置日志切换类型:"+ Log::logSwitchoverTypeMap.value(logSwitchoverType,"")+",时间或大小切换限定:"+QString::number(switchoverNum));
  148. }
  149. // 是否分日志级别输出
  150. void Log::setDistinguishLevel(bool isDistinguishLevel)
  151. {
  152. // 关闭已经打开的流,和删除已经设置的文件
  153. for(QtMsgType type : Log::levelPaths.keys()) {
  154. QFile* file = Log::levelPaths.value(type);
  155. if(file->isOpen()) {
  156. file->close();
  157. }
  158. if(file->exists()) {
  159. file->remove();
  160. }
  161. }
  162. // 清空映射关系
  163. Log::levelPaths.clear();
  164. // 设置是否分日志级别输出
  165. Log::isDistinguishLevel = isDistinguishLevel;
  166. // 如果分日志级别输出,则创建对应的文件和打开文件
  167. if(isDistinguishLevel) {
  168. // 关闭默认输出路径
  169. if(outLogFile->isOpen()) {
  170. outLogFile->close();
  171. }
  172. // 根据设置的输出文件路径构造不同级别的文件
  173. QFileInfo fileInfo = QFileInfo(Log::outLogFile->fileName());
  174. QString fileName = fileInfo.fileName();
  175. QDir dir = fileInfo.absoluteDir();
  176. if(!dir.exists()) {
  177. dir.mkdir(dir.absolutePath());
  178. }
  179. QMap<QtMsgType, QString> prefixMap = QMap<QtMsgType, QString>();
  180. prefixMap.insert(QtDebugMsg, "debug_");
  181. //一般信息文件路径
  182. prefixMap.insert(QtInfoMsg, "info_");
  183. //一般的警告文件路径
  184. prefixMap.insert(QtWarningMsg, "warn_");
  185. //严重错误文件路径
  186. prefixMap.insert(QtCriticalMsg, "critical_");
  187. //致命错误文件路径
  188. prefixMap.insert(QtFatalMsg, "fail_");
  189. for(QtMsgType type : prefixMap.keys()) {
  190. //调试信息文件路径
  191. QString filePath = dir.absoluteFilePath(prefixMap.value(type) + fileName);
  192. QFile* file = new QFile(filePath);
  193. Log::levelPaths.insert(type, file);
  194. }
  195. Log::printSystemLog("info", "已设置分日志级别输出,构建分日志文件成功");
  196. }
  197. }
  198. // 日志注册函数
  199. void Log::messageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
  200. {
  201. // 加载日志和写入相关联配置
  202. Log::loadLogSwitchoverSetting();
  203. // 下面开始解析信息
  204. QString txtMessage = "";
  205. QString nowTime = QDateTime::currentDateTime().toString(Log::timeFormat.trimmed().isEmpty() ? "yyyy-MM-dd hh:mm:ss:zzz" : Log::timeFormat);
  206. if(Log::msgTypeMap.contains(type)) {
  207. QPair<QString, LogOutType> infoPair = Log::msgTypeMap.value(type, Log::msgTypeMap.value(QtInfoMsg));
  208. // 格式化信息
  209. txtMessage = !Log::isContainInfoFormat() ? QString("%1 [%2] --- [%3:%4] %5: %6")
  210. .arg(Log::INFO_FORMAT_TIME, Log::INFO_FORMAT_LEVEL, Log::INFO_FORMAT_CODE_FILE,
  211. Log::INFO_FORMAT_CODE_LINE, Log::INFO_FORMAT_CODE_FUNCTION, Log::INFO_FORMAT_CODE_MSG)
  212. : infoFormat;
  213. txtMessage = Log::replace(txtMessage, Log::INFO_FORMAT_TIME, nowTime);
  214. txtMessage = Log::replace(txtMessage, Log::INFO_FORMAT_LEVEL, infoPair.first);
  215. txtMessage = Log::replace(txtMessage, Log::INFO_FORMAT_CODE_FILE, context.file);
  216. txtMessage = Log::replace(txtMessage, Log::INFO_FORMAT_CODE_LINE, QString::number(context.line));
  217. txtMessage = Log::replace(txtMessage, Log::INFO_FORMAT_CODE_FUNCTION, context.function);
  218. txtMessage = Log::replace(txtMessage, Log::INFO_FORMAT_CODE_MSG, msg);
  219. // 根据不同日志级别设置字体样式
  220. QString txtFontMessage = ConsoleFont::setConsoleFont(Log::levelFontMap.value(type, {ConsoleFont::DEFAULT}), txtMessage);
  221. // 根据输出日志类型选择输出到标准流或者是标准错误流
  222. if(infoPair.second == LogOutType::STANDARD_OUTPUT) {
  223. std::cout << txtFontMessage.toLocal8Bit().constData() << std::endl;
  224. } else {
  225. std::cerr << txtFontMessage.toLocal8Bit().constData() << std::endl;
  226. }
  227. // 写入到文件中
  228. writeLog(txtMessage, type);
  229. // 回调函数
  230. for(CallBackInfo callBack : Log::pCallbacks.getList()) {
  231. LogInfoCallBackHandler hander = callBack.hander;
  232. hander(type, context, msg, txtMessage, callBack.context);
  233. }
  234. if(type == QtFatalMsg) {
  235. Log::printSystemLog("error", "致命错误,立即终止:"+ msg);
  236. // 致命错误立即终止
  237. abort();
  238. }
  239. } else {
  240. std::cout << msg.toLocal8Bit().constData() << std::endl;
  241. }
  242. }
  243. // 注册回调函数
  244. LogInfoCallBackHandler Log::registLogInfoCallBack(QString unique, LogInfoCallBackHandler hander, void *context)
  245. {
  246. // 如果已经存在已经注册过的回调函数,则不在进行注册
  247. for(CallBackInfo info : pCallbacks.getList()) {
  248. if(info.unique == unique) {
  249. Log::printSystemLog("warn", "已经注册过该日志回调函数:" + unique+",不在进行注册操作");
  250. return hander;
  251. }
  252. }
  253. CallBackInfo callBackInfo(unique, hander, context);
  254. Log::pCallbacks.add(callBackInfo);
  255. Log::printSystemLog("info", "注册日志回调函数:" + unique + "成功");
  256. return hander;
  257. }
  258. // 初始化不同日志级别的输出级别标识和输出流
  259. QMap<QtMsgType,QPair<QString, LogOutType>> Log::initMsgTypeMap()
  260. {
  261. QMap<QtMsgType,QPair<QString, LogOutType>> msgTypeMap;
  262. //调试信息提示
  263. msgTypeMap.insert(QtDebugMsg,QPair<QString, LogOutType>("Debug", LogOutType::STANDARD_OUTPUT));
  264. //一般信息提示
  265. msgTypeMap.insert(QtInfoMsg,QPair<QString, LogOutType>("Info", LogOutType::STANDARD_OUTPUT));
  266. //一般的警告提示
  267. msgTypeMap.insert(QtWarningMsg,QPair<QString, LogOutType>("Waring", LogOutType::STANDARD_OUTPUT));
  268. //严重错误提示
  269. msgTypeMap.insert(QtCriticalMsg,QPair<QString, LogOutType>("Critical", LogOutType::STANDARD_ERROR_OUTPUT));
  270. //致命错误提示
  271. msgTypeMap.insert(QtFatalMsg,QPair<QString, LogOutType>("Fatal", LogOutType::STANDARD_ERROR_OUTPUT));
  272. return msgTypeMap;
  273. }
  274. // 初始化不同日志级别的字体样式参数
  275. QMap<QtMsgType, std::initializer_list<int>> Log::initLevelFonts()
  276. {
  277. QMap<QtMsgType, std::initializer_list<int>> levelColorMap;
  278. //调试信息字体样式
  279. levelColorMap.insert(QtDebugMsg,{ConsoleFont::FORE_GROUND_GREEN});
  280. //一般信息字体样式
  281. levelColorMap.insert(QtInfoMsg,{ConsoleFont::DEFAULT});
  282. //一般的警告信息字体样式
  283. levelColorMap.insert(QtWarningMsg,{ConsoleFont::FORE_GROUND_ORANGE});
  284. //严重错误字体样式
  285. levelColorMap.insert(QtCriticalMsg,{ConsoleFont::FORE_GROUND_RED, ConsoleFont::UNDER_LINE});
  286. //致命错误字体样式
  287. levelColorMap.insert(QtFatalMsg,{ConsoleFont::FORE_GROUND_RED, ConsoleFont::BOLD});
  288. return levelColorMap;
  289. }
  290. // 初始化备份类型与字符的映射
  291. QMap<LogSwitchoverType, QString> Log::initLogSwitchoverTypeMap()
  292. {
  293. QMap<LogSwitchoverType, QString> logSwitchoverTypeMap = QMap<LogSwitchoverType, QString>();
  294. logSwitchoverTypeMap.insert(LogSwitchoverType::ALL_DAY, "all_day");
  295. logSwitchoverTypeMap.insert(LogSwitchoverType::TIME_PERIOD, "time_period");
  296. logSwitchoverTypeMap.insert(LogSwitchoverType::FILE_SIZE, "file_size");
  297. return logSwitchoverTypeMap;
  298. }
  299. // 写入文件
  300. void Log::writeLog(QString str, QtMsgType msgType)
  301. {
  302. // 判断是否进行备份切换
  303. if(Log::logSwitchoverType == LogSwitchoverType::ALL_DAY || Log::logSwitchoverType == LogSwitchoverType::TIME_PERIOD) {
  304. // 如果当前时间超过了切换时间,则进行切换
  305. if(QDateTime::currentDateTime().operator >(Log::switchoverTime)) {
  306. // 备份日志文件
  307. Log::copyLogOutFile(msgType);
  308. }
  309. } else {
  310. long long fileSize = 0;
  311. if(Log::isDistinguishLevel) {
  312. // 计算所有日志文件大小之和
  313. for(QtMsgType type: Log::levelPaths.keys()) {
  314. QFile* msgFile = Log::levelPaths.value(type);
  315. fileSize = fileSize + msgFile->size();
  316. }
  317. } else {
  318. fileSize = Log::outLogFile->size();
  319. }
  320. // 当文件大小达到配置限制,则备份文件,因为size返回的是byte,而switchoverNum的单位是kb,所以需要乘以1024
  321. if(fileSize > (Log::switchoverNum*1024)) {
  322. // 备份日志文件
  323. Log::copyLogOutFile(msgType);
  324. }
  325. }
  326. // 写入日志到相应文件
  327. if(Log::isDistinguishLevel) {
  328. QFile* file = Log::levelPaths.value(msgType);
  329. if(!file->isOpen()) {
  330. file->open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text);
  331. }
  332. file->write((str+"\n").toUtf8());
  333. file->flush();
  334. } else {
  335. if(!Log::outLogFile->isOpen()) {
  336. Log::outLogFile->open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text);
  337. }
  338. Log::outLogFile->write((str+"\n").toUtf8());
  339. Log::outLogFile->flush();
  340. }
  341. }
  342. // 备份日志文件
  343. void Log::copyLogOutFile(QtMsgType msgType)
  344. {
  345. QString nowTimeStr = QDateTime::currentDateTime().toString("yyyyMMddhhmmsszzz");
  346. QList<bool> copyStatus;
  347. QList<QtMsgType> msgTypes;
  348. if(Log::isDistinguishLevel) {
  349. // 创建备份文件夹
  350. QFile* file = Log::levelPaths.value(msgType);
  351. QFileInfo fileInfo = QFileInfo(file->fileName());
  352. QDir dir = fileInfo.absoluteDir();
  353. QString copyDirPath = dir.absoluteFilePath(nowTimeStr+"log");
  354. if(!dir.exists(copyDirPath)) {
  355. dir.mkdir(copyDirPath);
  356. }
  357. QDir copyDir(copyDirPath);
  358. // 将日志文件备份到备份文件夹下
  359. for(QtMsgType type: Log::levelPaths.keys()) {
  360. QFile* msgFile = Log::levelPaths.value(type);
  361. QFileInfo fileInfo = QFileInfo(msgFile->fileName());
  362. bool isSucceed = Log::copy(fileInfo.absoluteFilePath(), copyDir.absoluteFilePath(fileInfo.fileName()));
  363. copyStatus.append(isSucceed);
  364. if(isSucceed) {
  365. msgTypes.append(type);
  366. }
  367. Log::printSystemLog("info", "拷贝文件:"+fileInfo.absoluteFilePath() + "到"+ copyDir.absoluteFilePath(fileInfo.fileName())+(isSucceed ? "成功":"失败"));
  368. }
  369. } else {
  370. // 创建备份文件夹
  371. QFileInfo fileInfo = QFileInfo(Log::outLogFile->fileName());
  372. QDir dir = fileInfo.absoluteDir();
  373. QString copyDirPath = dir.absoluteFilePath(nowTimeStr+"log");
  374. if(!dir.exists(copyDirPath)) {
  375. dir.mkdir(copyDirPath);
  376. }
  377. QDir copyDir(copyDirPath);
  378. // 将日志文件备份到备份文件夹下
  379. bool isSucceed = Log::copy(fileInfo.absoluteFilePath(), copyDir.absoluteFilePath(fileInfo.fileName()));
  380. copyStatus.append(isSucceed);
  381. Log::printSystemLog("info", "拷贝文件:"+fileInfo.absoluteFilePath() + "到"+ copyDir.absoluteFilePath(fileInfo.fileName())+(isSucceed ? "成功":"失败"));
  382. }
  383. // 备份成功与否提示信息
  384. if(!copyStatus.contains(false)) {
  385. Log::printSystemLog("info", "备份日志文件成功");
  386. } else {
  387. Log::printSystemLog("error", "备份部分日志文件失败!");
  388. }
  389. // 如果备份全部成功,则清空日志文件和写入新的切换时间到日志参数配置文件
  390. if(Log::isDistinguishLevel) {
  391. for(QtMsgType type: msgTypes) {
  392. QFile* msgFile = Log::levelPaths.value(type);
  393. QFileInfo fileInfo = QFileInfo(msgFile->fileName());
  394. if(msgFile->resize(0)) {
  395. Log::printSystemLog("info", "清空日志文件内容成功:"+fileInfo.absoluteFilePath());
  396. } else {
  397. Log::printSystemLog("error", "清空日志文件内容失败:"+fileInfo.absoluteFilePath());
  398. }
  399. }
  400. } else {
  401. if(!copyStatus.contains(false)) {
  402. QFileInfo fileInfo = QFileInfo(Log::outLogFile->fileName());
  403. if(Log::outLogFile->resize(0)) {
  404. Log::printSystemLog("info", "清空日志文件内容成功:"+fileInfo.absoluteFilePath());
  405. } else {
  406. Log::printSystemLog("error", "清空日志文件内容失败:"+fileInfo.absoluteFilePath());
  407. }
  408. }
  409. }
  410. // 备份完成之后更新再次需要备份的时间,并写入备份参数配置文件中
  411. Log::writeLogParamJson();
  412. }
  413. // 加载备份策略的设置
  414. void Log::loadLogSwitchoverSetting()
  415. {
  416. if(Log::isLoadLogSwitchoverSetting) {
  417. return;
  418. }
  419. logSwitchoverSettingHander();
  420. Log::isLoadLogSwitchoverSetting = true;
  421. // 如果需要分日志输出,则先创建分日志文件
  422. if(Log::isDistinguishLevel) {
  423. for(QtMsgType type: Log::levelPaths.keys()) {
  424. QFile* file = Log::levelPaths.value(type);
  425. // 如果文件所在文件夹不存在则创建文件夹
  426. QFileInfo fileInfo = QFileInfo(file->fileName());
  427. QDir dir = fileInfo.absoluteDir();
  428. if(!dir.exists()) {
  429. dir.mkdir(dir.absolutePath());
  430. }
  431. file->open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text);
  432. }
  433. } else {
  434. // 如果文件所在文件夹不存在则创建文件夹
  435. QFileInfo fileInfo = QFileInfo(Log::outLogFile->fileName());
  436. QDir dir = fileInfo.absoluteDir();
  437. if(!dir.exists()) {
  438. dir.mkdir(dir.absolutePath());
  439. }
  440. Log::outLogFile->open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text);
  441. }
  442. }
  443. // 备份参数处理,并同步更新json文件中的数据到内存,或将设置参数写入参数文件
  444. void Log::logSwitchoverSettingHander()
  445. {
  446. if(!Log::logParamJsonFile->exists()) { // 文件不存在则创建日志备份参数json文件并将参数写入文件
  447. Log::writeLogParamJson();
  448. } else {
  449. if(Log::logParamJsonFile->open(QIODevice::ReadWrite | QIODevice::Text)) { // 打开文件成功则读取json文件中的配置到内存中
  450. QByteArray json = Log::logParamJsonFile->readAll().trimmed();
  451. Log::logParamJsonFile->close();
  452. QJsonParseError jsonError;
  453. QJsonDocument jsonDoc(QJsonDocument::fromJson(json, &jsonError));
  454. if(jsonError.error == QJsonParseError::NoError)
  455. {
  456. QJsonObject rootObj = jsonDoc.object();
  457. QString logSwitchoverTypeStr = rootObj.value("logSwitchoverType").toString("");
  458. QString switchoverNumberStr = rootObj.value("switchoverNum").toString("");
  459. long long switchoverNumber = switchoverNumberStr.trimmed().isEmpty() ? 1: switchoverNumberStr.toLongLong();
  460. QString switchoverTimeStr = rootObj.value("switchoverTime").toString("");
  461. LogSwitchoverType switchoverType;
  462. QDateTime switchoverTime;
  463. for(LogSwitchoverType type: Log::logSwitchoverTypeMap.keys()) {
  464. if(Log::logSwitchoverTypeMap.value(type) == logSwitchoverTypeStr) {
  465. switchoverType = type;
  466. if(type == LogSwitchoverType::ALL_DAY) {
  467. switchoverTime = QDateTime::fromString(switchoverTimeStr, "yyyy-MM-dd");
  468. } else if(type == LogSwitchoverType::TIME_PERIOD) {
  469. switchoverTime = QDateTime::fromString(switchoverTimeStr, "yyyy-MM-dd hh:mm:ss");
  470. }
  471. break;
  472. }
  473. }
  474. Log::printSystemLog("info", "读取转换备份配置Json文件成功:"+logParamJsonFile->fileName()+",内容是:"+QString(json));
  475. // 如果不和默认的一样,则拷贝一份,将默认的参数写入文件,这么做的目的是用户在之前设置过日志切换类型和时间或大小,后面又修改代码后又没有进行相应的设置,则需要用到默认设置
  476. if(switchoverType != Log::logSwitchoverType || switchoverNumber != Log::switchoverNum) {
  477. Log::printSystemLog("warn", "读取配置和设置的设置过日志切换类型和时间或大小不同,写入最新配置:"+logParamJsonFile->fileName());
  478. Log::writeLogParamJson();
  479. } else {
  480. Log::logSwitchoverType = switchoverType;
  481. Log::switchoverNum = switchoverNumber;
  482. Log::switchoverTime = switchoverTime;
  483. }
  484. } else { // 解析失败,则将内存中日志备份参数写入文件
  485. Log::printSystemLog("error", "转换备份配置Json文件失败:"+logParamJsonFile->fileName()+",已转为默认配置");
  486. Log::writeLogParamJson();
  487. }
  488. } else { // 打开失败,则将内存中日志备份参数写入文件
  489. Log::printSystemLog("error", "打开备份配置Json文件失败:"+logParamJsonFile->fileName()+",已转为默认配置");
  490. Log::writeLogParamJson();
  491. }
  492. }
  493. }
  494. // 写入备份参数到日志备份配置json文件中
  495. void Log::writeLogParamJson()
  496. {
  497. QDateTime nowTime = QDateTime::currentDateTime();
  498. QString switchoverTimeStr = "";
  499. if(Log::logSwitchoverType == LogSwitchoverType::ALL_DAY) {
  500. // 指定天数之后
  501. Log::switchoverTime = nowTime.addDays(Log::switchoverNum);
  502. switchoverTimeStr = switchoverTime.toString("yyyy-MM-dd");
  503. } else if(Log::logSwitchoverType == LogSwitchoverType::TIME_PERIOD) {
  504. // 指定秒之后
  505. Log::switchoverTime = nowTime.addSecs(Log::switchoverNum);
  506. switchoverTimeStr = nowTime.toString("yyyy-MM-dd hh:mm:ss");
  507. }
  508. QJsonObject jsonObject;//构建json对象json
  509. jsonObject.insert("logSwitchoverType", Log::logSwitchoverTypeMap.value(Log::logSwitchoverType, ""));
  510. jsonObject.insert("switchoverNum", QString::number(Log::switchoverNum));
  511. jsonObject.insert("switchoverTime", switchoverTimeStr);
  512. QJsonDocument document;
  513. document.setObject(jsonObject);
  514. QString jsonStr(document.toJson(QJsonDocument::Indented));
  515. // 如果文件所在文件夹不存在则创建文件夹
  516. QFileInfo fileInfo = QFileInfo(Log::logParamJsonFile->fileName());
  517. QDir dir = fileInfo.absoluteDir();
  518. if(!dir.exists()) {
  519. dir.mkdir(dir.absolutePath());
  520. }
  521. if(Log::logParamJsonFile->open(QIODevice::WriteOnly | QIODevice::Text)) {
  522. Log::logParamJsonFile->write(jsonStr.toUtf8());
  523. Log::logParamJsonFile->flush();
  524. Log::logParamJsonFile->close();
  525. printSystemLog("info", "写入备份日志参数到"+fileInfo.absoluteFilePath()+",成功;参数:"+jsonStr);
  526. } else {
  527. printSystemLog("error", "写入备份日志参数到"+fileInfo.absoluteFilePath()+",失败");
  528. }
  529. }
  530. // 拷贝文件
  531. bool Log::copy(QString srcFilepPath, QString targetFilePath)
  532. {
  533. QFile srcFile(srcFilepPath);
  534. QFile targetFile(targetFilePath);
  535. if(srcFile.exists()) {
  536. if(targetFile.exists()) {
  537. targetFile.remove();
  538. }
  539. return srcFile.copy(targetFilePath);
  540. }
  541. Log::printSystemLog("error", "需要备份文件不存在:" + srcFilepPath);
  542. return false;
  543. }
  544. // 替换字符串中的子字符串
  545. QString Log::replace(QString src, QString before, QString after)
  546. {
  547. if(src.isEmpty()) {
  548. return src;
  549. }
  550. if(!src.contains(before)) {
  551. return src;
  552. }
  553. QString repalceAfter = src;
  554. while (repalceAfter.contains(before)) {
  555. repalceAfter = repalceAfter.replace(src.indexOf(before), before.size(), after);
  556. }
  557. return repalceAfter;
  558. }
  559. // 是否包含必要的格式化信息
  560. bool Log::isContainInfoFormat(QString mInfoFormat)
  561. {
  562. QString infoFormat = Log::infoFormat;
  563. if(!mInfoFormat.trimmed().isEmpty()) {
  564. infoFormat = mInfoFormat;
  565. }
  566. if(infoFormat.trimmed().isEmpty()) {
  567. return false;
  568. }
  569. if(!infoFormat.contains(Log::INFO_FORMAT_CODE_MSG)) {
  570. return false;
  571. }
  572. return true;
  573. }
  574. // 日志系统信息样式及打印
  575. void Log::printSystemLog(QString level, QString msg)
  576. {
  577. if(level.trimmed().toLower() == "info") {
  578. QString info = ConsoleFont::setConsoleFont({ConsoleFont::FORE_GROUND_BLUE,ConsoleFont::BACK_GROUND_CYAN,ConsoleFont::BOLD}, msg);
  579. std::cout << info.toLocal8Bit().constData() << std::endl;
  580. } else if(level.trimmed().toLower() == "warn") {
  581. QString info = ConsoleFont::setConsoleFont({ConsoleFont::FORE_GROUND_ORANGE,ConsoleFont::BACK_GROUND_PURPLE,ConsoleFont::BOLD}, msg);
  582. std::cout << info.toLocal8Bit().constData() << std::endl;
  583. } else if(level.trimmed().toLower() == "error") {
  584. QString info = ConsoleFont::setConsoleFont({ConsoleFont::FORE_GROUND_RED,ConsoleFont::BACK_GROUND_GREEN,ConsoleFont::BOLD}, msg);
  585. std::cout << info.toLocal8Bit().constData() << std::endl;
  586. } else {
  587. QString info = ConsoleFont::setConsoleFont({ConsoleFont::FORE_GROUND_BLACK,ConsoleFont::BACK_GROUND_CYAN,ConsoleFont::BOLD}, msg);
  588. std::cout << info.toLocal8Bit().constData() << std::endl;
  589. }
  590. }