Files
radar_data_process/data_process_class_dll/rdp_log.cpp
T

197 lines
5.4 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "rdp_log.h"
#include "parameters.h"
#include <cstdio>
#include <memory>
#include <string>
#include <vector>
#include <time.h>
#include <direct.h>
#include <io.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/stdout_sinks.h>
namespace
{
//日志行格式:与旧版 [时间戳][文件:行号] 风格一致,另加 spdlog 级别
const char *const kPattern = "[%Y-%m-%d %H:%M:%S.%e] [%l] [%s:%#] %v";
//日志模块内部状态:默认 logger(标准输出 + 日志文件)与当前日志文件路径
struct RdpLogState
{
std::shared_ptr<spdlog::logger> 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<spdlog::logger> &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<spdlog::logger> MakeStdoutLogger()
{
std::vector<spdlog::sink_ptr> sinks;
sinks.push_back(std::make_shared<spdlog::sinks::stdout_sink_mt>());
std::shared_ptr<spdlog::logger> logger =
std::make_shared<spdlog::logger>("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<spdlog::sink_ptr> sinks;
sinks.push_back(std::make_shared<spdlog::sinks::stdout_sink_mt>());
//truncate=false:日志文件名已保证不存在(极端并发下万一被抢占则追加而非覆盖已有内容)
sinks.push_back(std::make_shared<spdlog::sinks::basic_file_sink_mt>(log_path, false));
std::shared_ptr<spdlog::logger> logger =
std::make_shared<spdlog::logger>("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;
}
}