#ifndef LOG4CPP_H #define LOG4CPP_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // 前置声明 class LogInstance; // 日志实例管理器(全局唯一,管理所有模块的日志实例) class LogManager { public: static LogManager& getInstance() { static LogManager instance; return instance; } // 注册模块日志实例 void registerInstance(const std::string& module, LogInstance* instance) { std::lock_guard lock(mtx_); instances_[module] = instance; } // 获取模块日志实例 LogInstance* getInstance(const std::string& module) { std::lock_guard lock(mtx_); auto it = instances_.find(module); return (it != instances_.end()) ? it->second : nullptr; } // 移除模块日志实例 void unregisterInstance(const std::string& module) { std::lock_guard lock(mtx_); instances_.erase(module); } // 获取所有注册的模块名 std::vector getRegisteredModules() { std::lock_guard lock(mtx_); std::vector modules; for (const auto& pair : instances_) { modules.push_back(pair.first); } return modules; } private: LogManager() = default; ~LogManager() = default; LogManager(const LogManager&) = delete; LogManager& operator=(const LogManager&) = delete; std::unordered_map instances_; std::mutex mtx_; }; // 日志配置结构体 struct LogConfig { std::string root_level = "DEBUG"; std::string log_dir = "logs"; std::string history_dir = "logs/history"; unsigned int max_file_size = 10; // MB int max_backups = 5; std::string file_pattern = "%d{%Y-%m-%d %H:%M:%S.%l} [%p] - %F::%M@%L %m%n"; std::string console_pattern = "%d{%H:%M:%S.%l} [%p] - %F::%M@%L %m%n"; std::string log_file_name; // 自定义日志文件名(为空则使用模块名) }; // 日志实例类(每个动态库一个实例) class LogInstance { public: LogInstance() = default; ~LogInstance() { destroyLog(); } // 初始化日志(module:模块名,确保唯一;configFile:可为空使用默认配置) bool init(const std::string& logHostName, const std::string& module, const std::string& configFile = "", bool toScreen = false); // 销毁日志 void destroyLog(); // 设置日志级别 void setPriority(log4cpp::Priority::PriorityLevel consoleLevel, log4cpp::Priority::PriorityLevel fileLevel); // 日志输出接口(内部使用) template void log(log4cpp::Priority::PriorityLevel level, const char* file, int line, const char* function, const std::string& format, Args&&... args) { if (!isInitialized_) return; std::lock_guard lock(logMtx_); // 格式化消息 std::string formattedMsg = formatMessage(format, std::forward(args)...); // 添加代码位置信息 std::string location; if (file && function) { // 提取文件名(不含路径) const char* filename = strrchr(file, '/'); if (!filename) filename = strrchr(file, '\\'); filename = filename ? filename + 1 : file; std::ostringstream oss; oss << filename << "::" << function << "@" << line; location = oss.str(); } // 输出到文件 if (logCat) { if (!location.empty()) { logCat->log(level, "%s - %s", location.c_str(), formattedMsg.c_str()); } else { logCat->log(level, "%s", formattedMsg.c_str()); } } // 输出到控制台 if (outToScreen && coutCat) { if (!location.empty()) { coutCat->log(level, "%s - %s", location.c_str(), formattedMsg.c_str()); } else { coutCat->log(level, "%s", formattedMsg.c_str()); } } } bool isInitialized() const { return isInitialized_; } const std::string& getModule() const { return moduleName; } const LogConfig& getConfig() const { return config_; } const std::string& getLogFileName() const { return actualLogFileName; } private: // 解析配置文件 bool parseConfigFile(const std::string& configFile, LogConfig& config); // 创建目录 bool createDirectory(const std::string& dir); // 获取当前时间(带毫秒) void getCurrentTimeWithMs(std::string& timeStr); // 转换日志级别 log4cpp::Priority::PriorityLevel parseLogLevel(const std::string& levelStr); // 移动旧日志到历史目录 void moveOldLogsToHistory(); // 格式化消息(替换 {$} 占位符) template std::string formatMessage(const std::string& format, Args&&... args) { std::string result = format; std::vector replacements; // 收集所有参数 (replacements.push_back(toString(std::forward(args))), ...); // 替换所有 {$} 占位符 size_t pos = 0; int argIndex = 0; while ((pos = result.find("{$}", pos)) != std::string::npos) { if (argIndex < replacements.size()) { result.replace(pos, 3, replacements[argIndex]); pos += replacements[argIndex].length(); argIndex++; } else { // 参数不足,保留占位符 pos += 3; } } return result; } // 类型转换为字符串 template std::string toString(T&& value); LogConfig config_; std::string logHost; std::string moduleName; std::string actualLogFileName; // 实际使用的日志文件名 bool outToScreen = false; bool isInitialized_ = false; // log4cpp组件 log4cpp::Category* logCat = nullptr; log4cpp::Category* coutCat = nullptr; log4cpp::RollingFileAppender* rollLogFile = nullptr; log4cpp::OstreamAppender* logScreen = nullptr; log4cpp::PatternLayout* logLayout = nullptr; log4cpp::PatternLayout* screenLayout = nullptr; // 线程安全锁 std::mutex logMtx_; }; // 日志模块初始化函数 bool initLogModule(const std::string& logHost, const std::string& module, const std::string& configFile = "", bool toScreen = false); // 日志模块销毁函数 void destroyLogModule(const std::string& module); // 获取所有已初始化的日志模块 std::vector getInitializedLogModules(); // 销毁所有日志模块 void destroyAllLogModules(); // 类型转换实现 template std::string LogInstance::toString(T&& value) { using DecayedT = std::decay_t; try { if constexpr (std::is_same_v || std::is_same_v || std::is_same_v) { return std::forward(value); } else if constexpr (std::is_arithmetic_v || std::is_enum_v) { std::ostringstream oss; oss << value; return oss.str(); } else { std::ostringstream oss; oss << std::forward(value); return oss.str(); } } catch (...) { return "[LOG_CONVERT_ERROR]"; } } #endif // LOG4CPP_H