Log4CPP.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. #include "Log4CPP.h"
  2. #include <sys/stat.h>
  3. #include <dirent.h>
  4. #include <sys/types.h>
  5. #include <tinyxml2.h>
  6. #include <ctime>
  7. #include <stdexcept>
  8. #include <iostream>
  9. #include <cstdio>
  10. #include <vector>
  11. #include <algorithm>
  12. // 全局模块映射和互斥锁
  13. static std::mutex g_moduleMutex;
  14. static std::map<std::string, LogInstance*> g_moduleLoggers;
  15. bool LogInstance::parseConfigFile(const std::string& configFile, LogConfig& config) {
  16. tinyxml2::XMLDocument doc;
  17. if (doc.LoadFile(configFile.c_str()) != tinyxml2::XML_SUCCESS) {
  18. std::cerr << "[" << moduleName << "] Load config failed: " << configFile << std::endl;
  19. return false;
  20. }
  21. tinyxml2::XMLElement* root = doc.RootElement();
  22. if (!root) return false;
  23. // 先解析全局配置
  24. for (tinyxml2::XMLElement* elem = root->FirstChildElement(); elem; elem = elem->NextSiblingElement()) {
  25. const char* name = elem->Name();
  26. const char* value = elem->Attribute("value");
  27. if (!name || !value) continue;
  28. // 跳过module元素,稍后处理
  29. if (strcmp(name, "module") == 0) continue;
  30. // 全局配置
  31. if (strcmp(name, "root_level") == 0) config.root_level = value;
  32. else if (strcmp(name, "log_dir") == 0) config.log_dir = value;
  33. else if (strcmp(name, "history_dir") == 0) config.history_dir = value;
  34. else if (strcmp(name, "MAX_FILE_SIZE") == 0) {
  35. std::string sizeStr = value;
  36. if (!sizeStr.empty() && (sizeStr.back() == 'M' || sizeStr.back() == 'm')) {
  37. sizeStr.pop_back();
  38. }
  39. try {
  40. config.max_file_size = std::stoul(sizeStr);
  41. }
  42. catch (const std::exception& e) {
  43. std::cerr << "[" << moduleName << "] Parse MAX_FILE_SIZE failed: " << e.what() << std::endl;
  44. config.max_file_size = 10; // 默认值
  45. }
  46. }
  47. else if (strcmp(name, "MAX_HISTORY") == 0) {
  48. try {
  49. config.max_backups = std::stoi(value);
  50. }
  51. catch (const std::exception& e) {
  52. std::cerr << "[" << moduleName << "] Parse MAX_HISTORY failed: " << e.what() << std::endl;
  53. config.max_backups = 5; // 默认值
  54. }
  55. }
  56. else if (strcmp(name, "file_pattern") == 0) config.file_pattern = value;
  57. else if (strcmp(name, "console_pattern") == 0) config.console_pattern = value;
  58. else if (strcmp(name, "log_file_name") == 0) config.log_file_name = value;
  59. }
  60. // 再查找模块特定配置
  61. for (tinyxml2::XMLElement* elem = root->FirstChildElement(); elem; elem = elem->NextSiblingElement()) {
  62. const char* name = elem->Name();
  63. if (!name || strcmp(name, "module") != 0) continue;
  64. const char* moduleNameAttr = elem->Attribute("name");
  65. if (!moduleNameAttr || moduleName != moduleNameAttr) continue;
  66. // 找到匹配的模块配置
  67. for (tinyxml2::XMLElement* subElem = elem->FirstChildElement(); subElem; subElem = subElem->NextSiblingElement()) {
  68. const char* subName = subElem->Name();
  69. const char* subValue = subElem->Attribute("value");
  70. if (!subName || !subValue) continue;
  71. if (strcmp(subName, "root_level") == 0) config.root_level = subValue;
  72. else if (strcmp(subName, "log_dir") == 0) config.log_dir = subValue;
  73. else if (strcmp(subName, "history_dir") == 0) config.history_dir = subValue;
  74. else if (strcmp(subName, "MAX_FILE_SIZE") == 0) {
  75. std::string sizeStr = subValue;
  76. if (!sizeStr.empty() && (sizeStr.back() == 'M' || sizeStr.back() == 'm')) {
  77. sizeStr.pop_back();
  78. }
  79. try {
  80. config.max_file_size = std::stoul(sizeStr);
  81. }
  82. catch (const std::exception& e) {
  83. std::cerr << "[" << moduleName << "] Parse module MAX_FILE_SIZE failed: " << e.what() << std::endl;
  84. }
  85. }
  86. else if (strcmp(subName, "MAX_HISTORY") == 0) {
  87. try {
  88. config.max_backups = std::stoi(subValue);
  89. }
  90. catch (const std::exception& e) {
  91. std::cerr << "[" << moduleName << "] Parse module MAX_HISTORY failed: " << e.what() << std::endl;
  92. }
  93. }
  94. else if (strcmp(subName, "file_pattern") == 0) config.file_pattern = subValue;
  95. else if (strcmp(subName, "console_pattern") == 0) config.console_pattern = subValue;
  96. else if (strcmp(subName, "log_file_name") == 0) config.log_file_name = subValue;
  97. }
  98. break; // 找到匹配模块后跳出循环
  99. }
  100. return true;
  101. }
  102. bool LogInstance::createDirectory(const std::string& dir) {
  103. if (dir.empty()) return false;
  104. std::string path;
  105. for (size_t i = 0; i < dir.length(); ++i) {
  106. path += dir[i];
  107. if (dir[i] == '/' || dir[i] == '\\' || i == dir.length() - 1) {
  108. // 跳过根目录和空目录名
  109. if (path.length() <= 1) continue;
  110. #ifdef _WIN32
  111. if (_mkdir(path.c_str()) != 0 && errno != EEXIST) {
  112. std::cerr << "[" << moduleName << "] Create directory failed: " << path << std::endl;
  113. return false;
  114. }
  115. #else
  116. if (mkdir(path.c_str(), 0755) != 0 && errno != EEXIST) {
  117. std::cerr << "[" << moduleName << "] Create directory failed: " << path << std::endl;
  118. return false;
  119. }
  120. #endif
  121. }
  122. }
  123. return true;
  124. }
  125. void LogInstance::getCurrentTimeWithMs(std::string& timeStr) {
  126. auto now = std::chrono::system_clock::now();
  127. auto now_time_t = std::chrono::system_clock::to_time_t(now);
  128. auto now_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
  129. now.time_since_epoch()) % 1000;
  130. // 使用线程安全的 localtime_r 替代 localtime
  131. struct tm now_tm_storage;
  132. struct tm* now_tm = localtime_r(&now_time_t, &now_tm_storage);
  133. char buffer[80];
  134. if (now_tm != nullptr) {
  135. strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", now_tm);
  136. } else {
  137. // 如果 localtime_r 失败,使用默认值
  138. strncpy(buffer, "1970-01-01 00:00:00", sizeof(buffer));
  139. buffer[sizeof(buffer) - 1] = '\0';
  140. }
  141. std::ostringstream oss;
  142. oss << buffer << "." << std::setfill('0') << std::setw(3) << now_ms.count();
  143. timeStr = oss.str();
  144. }
  145. log4cpp::Priority::PriorityLevel LogInstance::parseLogLevel(const std::string& levelStr) {
  146. if (levelStr == "DEBUG") return log4cpp::Priority::DEBUG;
  147. if (levelStr == "INFO") return log4cpp::Priority::INFO;
  148. if (levelStr == "WARN") return log4cpp::Priority::WARN;
  149. if (levelStr == "ERROR") return log4cpp::Priority::ERROR;
  150. if (levelStr == "FATAL") return log4cpp::Priority::FATAL;
  151. return log4cpp::Priority::DEBUG; // 默认DEBUG
  152. }
  153. void LogInstance::moveOldLogsToHistory() {
  154. // 1. 获取当前日期(保留连字符,与日志文件名格式一致)
  155. std::string currentTimeWithMs;
  156. getCurrentTimeWithMs(currentTimeWithMs); // 格式:"2025-09-16 10:15:30.123"
  157. std::string currentDate;
  158. // 提取日期部分(前10个字符,直接保留连字符:"2025-09-16")
  159. if (currentTimeWithMs.length() >= 10) {
  160. currentDate = currentTimeWithMs.substr(0, 10); // 结果:"2025-09-16"
  161. }
  162. else {
  163. std::cerr << "[" << moduleName << "] 提取当前日期失败" << std::endl;
  164. return;
  165. }
  166. // 2. 构建目录路径
  167. std::string baseLogDir = config_.log_dir + "/" + logHost;
  168. std::string historyDir = config_.history_dir + "/" + logHost;
  169. if (!createDirectory(historyDir)) {
  170. std::cerr << "[" << moduleName << "] 创建历史目录失败: " << historyDir << std::endl;
  171. return;
  172. }
  173. // 3. 遍历日志目录
  174. DIR* dir = opendir(baseLogDir.c_str());
  175. if (!dir) {
  176. std::cerr << "[" << moduleName << "] 打开日志目录失败: " << baseLogDir << std::endl;
  177. return;
  178. }
  179. std::string logPrefix = actualLogFileName + "."; // 如 "DevPSGHD."
  180. struct dirent* entry;
  181. while ((entry = readdir(dir)) != nullptr) {
  182. std::string filename = entry->d_name;
  183. if (filename == "." || filename == "..") continue;
  184. if (filename.find(logPrefix) != 0) continue; // 只处理当前模块的日志
  185. // 4. 提取文件名中的日期(格式:"YYYY-MM-DD")
  186. // 文件名结构:logPrefix + [中间部分.] + "YYYY-MM-DD" + ".log"
  187. // 修正逻辑:从末尾查找,确保提取最后10位的日期部分
  188. const std::string logSuffix = ".log";
  189. size_t suffixPos = filename.find(logSuffix);
  190. if (suffixPos == std::string::npos) continue; // 不是.log文件,跳过
  191. // 日期应该在.log前面,且长度为10(YYYY-MM-DD)
  192. size_t dateStart = suffixPos - 10;
  193. if (suffixPos < 10) continue;
  194. std::string fileDate = filename.substr(dateStart, 10);
  195. // 验证日期格式(简单检查:包含两个连字符,且位置正确)
  196. if (fileDate.size() != 10 || fileDate[4] != '-' || fileDate[7] != '-') {
  197. continue; // 日期格式不正确,跳过
  198. }
  199. // 5. 调试输出:打印文件名日期和当前日期(方便确认是否匹配)
  200. // std::cout << "[" << moduleName << "] 检查日志: " << filename
  201. // << " | 文件名日期: " << fileDate
  202. // << " | 当前日期: " << currentDate << std::endl;
  203. // 6. 只移动非当天的日志(日期不匹配时)
  204. if (fileDate != currentDate) {
  205. std::string srcPath = baseLogDir + "/" + filename;
  206. std::string destPath = historyDir + "/" + filename;
  207. if (std::rename(srcPath.c_str(), destPath.c_str()) != 0) {
  208. std::cerr << "[" << moduleName << "] 移动日志失败: " << srcPath << " -> " << destPath << std::endl;
  209. }
  210. else {
  211. // std::cout << "[" << moduleName << "] 移动旧日志成功: " << srcPath << " -> " << destPath << std::endl;
  212. }
  213. }
  214. }
  215. closedir(dir);
  216. }
  217. bool LogInstance::init(const std::string& logHostName, const std::string& module,
  218. const std::string& configFile, bool toScreen) {
  219. if (isInitialized_) {
  220. // std::cout << "[" << module << "] Log already initialized" << std::endl;
  221. return true;
  222. }
  223. // 初始化基础信息
  224. logHost = logHostName;
  225. moduleName = module;
  226. outToScreen = toScreen;
  227. // 加载配置(优先模块配置,其次全局配置,最后默认)
  228. LogConfig tempConfig;
  229. if (!configFile.empty()) {
  230. if (!parseConfigFile(configFile, tempConfig)) {
  231. std::cerr << "[" << module << "] Parse config file failed, using default config" << std::endl;
  232. }
  233. }
  234. config_ = tempConfig;
  235. // 确定实际使用的日志文件名
  236. if (config_.log_file_name.empty()) {
  237. actualLogFileName = moduleName; // 使用模块名作为日志文件名
  238. }
  239. else {
  240. actualLogFileName = config_.log_file_name; // 使用配置的日志文件名
  241. // 支持在日志文件名中使用 {host} 占位符
  242. size_t pos = actualLogFileName.find("{host}");
  243. if (pos != std::string::npos) {
  244. actualLogFileName.replace(pos, 6, logHost);
  245. }
  246. }
  247. // 创建日志目录
  248. std::string baseLogDir = config_.log_dir + "/" + logHost;
  249. if (!createDirectory(baseLogDir)) {
  250. std::cerr << "[" << module << "] Create log dir failed: " << baseLogDir << std::endl;
  251. return false;
  252. }
  253. // 生成日志文件路径
  254. std::string timeStr;
  255. getCurrentTimeWithMs(timeStr);
  256. // 提取年月日部分(前10个字符,格式为"YYYY-MM-DD")
  257. std::string dateStr = timeStr.substr(0, 10);
  258. // 构造日志路径,仅使用年月日作为时间标识
  259. std::string logPath = baseLogDir + "/" + actualLogFileName + "." + dateStr + ".log";
  260. // 初始化log4cpp组件
  261. try {
  262. // 布局
  263. logLayout = new log4cpp::PatternLayout();
  264. logLayout->setConversionPattern(config_.file_pattern);
  265. screenLayout = new log4cpp::PatternLayout();
  266. screenLayout->setConversionPattern(config_.console_pattern);
  267. // 文件Appender
  268. rollLogFile = new log4cpp::RollingFileAppender(
  269. "roll_" + moduleName, logPath,
  270. config_.max_file_size * 1024 * 1024, // 转换为字节
  271. config_.max_backups, true
  272. );
  273. rollLogFile->setLayout(logLayout);
  274. // 控制台Appender
  275. if (outToScreen) {
  276. logScreen = new log4cpp::OstreamAppender("console_" + moduleName, &std::cout);
  277. logScreen->setLayout(screenLayout);
  278. }
  279. // 日志类别(用模块名区分)
  280. log4cpp::Category& root = log4cpp::Category::getRoot();
  281. logCat = &root.getInstance("file_" + moduleName);
  282. logCat->addAppender(rollLogFile);
  283. logCat->setPriority(parseLogLevel(config_.root_level));
  284. if (outToScreen) {
  285. coutCat = &root.getInstance("console_" + moduleName);
  286. coutCat->addAppender(logScreen);
  287. coutCat->setPriority(parseLogLevel(config_.root_level));
  288. }
  289. // 移动旧日志
  290. moveOldLogsToHistory();
  291. // 记录初始化日志
  292. std::string initTime;
  293. getCurrentTimeWithMs(initTime);
  294. logCat->info("Log initialized (file: %s)", logPath.c_str());
  295. if (outToScreen) {
  296. coutCat->info("Log initialized (module: %s)", moduleName.c_str());
  297. }
  298. isInitialized_ = true;
  299. return true;
  300. }
  301. catch (const std::exception& e) {
  302. std::cerr << "[" << module << "] Init failed: " << e.what() << std::endl;
  303. destroyLog(); // 清理部分初始化的资源
  304. return false;
  305. }
  306. }
  307. void LogInstance::destroyLog() {
  308. if (!isInitialized_) return;
  309. std::lock_guard<std::mutex> lock(logMtx_);
  310. // 记录销毁日志
  311. if (logCat) logCat->info("Log destroying");
  312. // 清理log4cpp资源
  313. if (logCat) logCat->removeAllAppenders();
  314. if (coutCat) coutCat->removeAllAppenders();
  315. delete rollLogFile; rollLogFile = nullptr;
  316. delete logScreen; logScreen = nullptr;
  317. delete logLayout; logLayout = nullptr;
  318. delete screenLayout; screenLayout = nullptr;
  319. logCat = nullptr;
  320. coutCat = nullptr;
  321. isInitialized_ = false;
  322. }
  323. void LogInstance::setPriority(log4cpp::Priority::PriorityLevel consoleLevel,
  324. log4cpp::Priority::PriorityLevel fileLevel) {
  325. if (isInitialized_) {
  326. if (coutCat) coutCat->setPriority(consoleLevel);
  327. if (logCat) logCat->setPriority(fileLevel);
  328. }
  329. }
  330. // 日志模块初始化函数实现
  331. bool initLogModule(const std::string& logHost,
  332. const std::string& module,
  333. const std::string& configFile,
  334. bool toScreen) {
  335. std::lock_guard<std::mutex> lock(g_moduleMutex);
  336. // 检查是否已经初始化
  337. if (g_moduleLoggers.find(module) != g_moduleLoggers.end()) {
  338. return true; // 已经初始化
  339. }
  340. // 创建新的日志实例
  341. LogInstance* logger = new LogInstance();
  342. bool ret = logger->init(logHost, module, configFile, toScreen);
  343. if (ret) {
  344. LogManager::getInstance().registerInstance(module, logger);
  345. g_moduleLoggers[module] = logger;
  346. return true;
  347. }
  348. else {
  349. delete logger;
  350. return false;
  351. }
  352. }
  353. // 日志模块销毁函数实现
  354. void destroyLogModule(const std::string& module) {
  355. std::lock_guard<std::mutex> lock(g_moduleMutex);
  356. auto it = g_moduleLoggers.find(module);
  357. if (it != g_moduleLoggers.end()) {
  358. it->second->destroyLog();
  359. LogManager::getInstance().unregisterInstance(module);
  360. delete it->second;
  361. g_moduleLoggers.erase(it);
  362. }
  363. }
  364. // 获取所有已初始化的日志模块
  365. std::vector<std::string> getInitializedLogModules() {
  366. std::lock_guard<std::mutex> lock(g_moduleMutex);
  367. std::vector<std::string> modules;
  368. for (const auto& pair : g_moduleLoggers) {
  369. modules.push_back(pair.first);
  370. }
  371. return modules;
  372. }
  373. // 销毁所有日志模块
  374. void destroyAllLogModules() {
  375. std::lock_guard<std::mutex> lock(g_moduleMutex);
  376. for (auto& pair : g_moduleLoggers) {
  377. pair.second->destroyLog();
  378. LogManager::getInstance().unregisterInstance(pair.first);
  379. delete pair.second;
  380. }
  381. g_moduleLoggers.clear();
  382. }