#include "rdp_log.h" #include "parameters.h" #include #include #include #include #include #include #include #include #include namespace { //日志行格式:与旧版 [时间戳][文件:行号] 风格一致,另加 spdlog 级别 const char *const kPattern = "[%Y-%m-%d %H:%M:%S.%e] [%l] [%s:%#] %v"; //日志模块内部状态:默认 logger(标准输出 + 日志文件)与当前日志文件路径 struct RdpLogState { std::shared_ptr logger; //默认 logger(含标准输出 sink,成功时另含文件 sink) std::string log_path; //当前日志文件全路径(降级为仅标准输出时为空) bool initialized; //是否已初始化(含文件创建失败时的降级初始化) RdpLogState() : initialized(false) {} }; RdpLogState &State() { static RdpLogState s; return s; } //创建日志目录(目录已存在时 _mkdir 返回 EEXIST,忽略;逻辑与原 rdp_log/rdp_record 一致) void EnsureDir(const std::string &dir_with_separator) { std::string dir = dir_with_separator; while (dir.size() > 0 && (dir[dir.size() - 1] == '\\' || dir[dir.size() - 1] == '/')) { dir.erase(dir.size() - 1); } if (dir.size() > 0) { _mkdir(dir.c_str()); } } //当前本地时间字符串,格式 "YYYYMMDD_HHMMSS" bool TimeStamp(char *buf, size_t size) { time_t now = time(NULL); struct tm local; if (localtime_s(&local, &now) != 0) { return false; } return strftime(buf, size, "%Y%m%d_%H%M%S", &local) != 0; } //生成日志文件路径:RDP_LOG_DIR + rdp_YYYYMMDD_HHMMSS.log; //同一秒内文件已存在(Destroy→GetB 重连、同目录多进程启动)时追加 _1~_999 后缀,不复用同一文件 bool MakeLogPath(std::string *path) { char stamp[32] = {0}; if (!TimeStamp(stamp, sizeof(stamp))) { return false; } const std::string base = std::string(RDP_LOG_DIR) + "rdp_" + stamp; for (int suffix = 0; suffix < 1000; ++suffix) { std::string candidate = base; if (suffix > 0) { candidate += "_" + std::to_string(suffix); } candidate += ".log"; if (_access(candidate.c_str(), 0) != 0) { *path = candidate; return true; } } return false; } //初始化 logger:统一日志格式,运行期级别放开到 trace(实际输出级别由编译期 //SPDLOG_ACTIVE_LEVEL 过滤),所有级别逐条 flush(日志量小,且调试场景要求异常退出也尽量不丢日志) void ConfigureLogger(const std::shared_ptr &logger) { logger->set_pattern(kPattern); logger->set_level(spdlog::level::trace); logger->flush_on(spdlog::level::trace); spdlog::set_default_logger(logger); } //仅标准输出 sink 的降级 logger(日志文件创建失败时使用) std::shared_ptr MakeStdoutLogger() { std::vector sinks; sinks.push_back(std::make_shared()); std::shared_ptr logger = std::make_shared("rdp", sinks.begin(), sinks.end()); ConfigureLogger(logger); return logger; } } namespace rdp_log { bool Init() { RdpLogState &st = State(); if (st.initialized) { return true; //已经初始化,避免重复创建日志文件 } #if SPDLOG_ACTIVE_LEVEL > SPDLOG_LEVEL_CRITICAL //编译期关闭日志(RDP_LOG_LEVEL=OFF):不初始化 logger、不创建日志文件 return false; #else try { EnsureDir(RDP_LOG_DIR); std::string log_path; if (!MakeLogPath(&log_path)) { throw spdlog::spdlog_ex("no usable log file name in RDP_LOG_DIR"); } std::vector sinks; sinks.push_back(std::make_shared()); //truncate=false:日志文件名已保证不存在(极端并发下万一被抢占则追加而非覆盖已有内容) sinks.push_back(std::make_shared(log_path, false)); std::shared_ptr logger = std::make_shared("rdp", sinks.begin(), sinks.end()); ConfigureLogger(logger); //spdlog 内部错误(如日志文件写失败)打到标准错误,不影响雷达数据处理 spdlog::set_error_handler([](const std::string &msg) { std::fprintf(stderr, "[rdp_log] spdlog error: %s\n", msg.c_str()); }); st.logger = logger; st.log_path = log_path; st.initialized = true; RDP_LOG_INFO("rdp_log enabled, log file: {}", log_path); return true; } catch (const std::exception &e) { //日志文件创建失败(目录不可写等):降级为仅标准输出,不中断雷达数据处理 try { st.logger = MakeStdoutLogger(); st.log_path.clear(); st.initialized = true; std::fprintf(stderr, "[rdp_log] cannot create log file, fallback to stdout only: %s\n", e.what()); } catch (...) { std::fprintf(stderr, "[rdp_log] logger init failed\n"); } return false; } #endif } void Shutdown() { RdpLogState &st = State(); if (!st.initialized || !st.logger) { return; } RDP_LOG_INFO("rdp_log shutdown"); //flush 并释放默认 logger(随之关闭日志文件) spdlog::shutdown(); st.logger.reset(); st.log_path.clear(); st.initialized = false; } std::string log_file_path() { return State().log_path; } }